130 lines
5.2 KiB
Bash
Executable File
130 lines
5.2 KiB
Bash
Executable File
#!/bin/sh
|
|
# M2 differential TI-compiler ABI check.
|
|
#
|
|
# scripts/tests/m2/diff/check.sh
|
|
#
|
|
# The stored ti-add2.s/ti-locals.s are one-time outputs of the 1987 TI
|
|
# compiler (GSP C Compiler 2.01) under DOSBox. CI never runs DOSBox: this
|
|
# script greps the stored golden files and compares their ABI shape with the
|
|
# assembly our compiler emits for the same K&R sources.
|
|
#
|
|
# The oracle is the TI ABI layout, not instruction selection:
|
|
#
|
|
# add2 first argument at FP(-32), second at FP(-64), result in A8, RETS 2
|
|
# locals a nonzero frame, result in A8, RETS 0
|
|
#
|
|
# TI prints the negative offsets as -32/-64; 0FFE0h/0FFC0h are also accepted
|
|
# because the 1987 assembler may format the same displacement in hex.
|
|
#
|
|
# Overridable: TMS_CLANG TMS_OPT TMS_BITADDR TMS_LLC TI_ADD2 TI_LOCALS
|
|
set -eu
|
|
|
|
ROOT=$(cd "$(dirname "$0")/../../../.." && pwd)
|
|
DIR=$ROOT/scripts/tests/m2/diff
|
|
|
|
LLVM_BIN=${LLVM_BIN:-$ROOT/build/llvm-clean-build/bin}
|
|
TMS_CLANG=${TMS_CLANG:-$LLVM_BIN/clang}
|
|
TMS_OPT=${TMS_OPT:-$LLVM_BIN/opt}
|
|
TMS_BITADDR=${TMS_BITADDR:-$ROOT/tools/tms-bitaddr/tms-bitaddr}
|
|
TMS_LLC=${TMS_LLC:-$LLVM_BIN/llc}
|
|
TI_ADD2=${TI_ADD2:-$DIR/ti-add2.s}
|
|
TI_LOCALS=${TI_LOCALS:-$DIR/ti-locals.s}
|
|
export TMS_CLANG TMS_OPT TMS_BITADDR
|
|
|
|
fail() {
|
|
echo "M2 diff: FAIL: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
for tool in "$TMS_CLANG" "$TMS_OPT" "$TMS_BITADDR" "$TMS_LLC"; do
|
|
[ -x "$tool" ] || fail "missing tool: $tool"
|
|
done
|
|
[ -f "$TI_ADD2" ] || fail "stored TI golden missing: $TI_ADD2"
|
|
[ -f "$TI_LOCALS" ] || fail "stored TI golden missing: $TI_LOCALS"
|
|
|
|
TMP=$(mktemp -d "${TMPDIR:-/tmp}/m2-diff.XXXXXX")
|
|
trap 'rm -rf "$TMP"' EXIT HUP INT TERM
|
|
|
|
# Our add2 is built exactly like the M2 gate: tms-clang -O2, then llc -O0.
|
|
# locals uses -O0 because at -O2 the optimizer folds `x = 7; return x` to a
|
|
# constant and no frame is emitted; the TI compiler keeps the frame, so -O0 is
|
|
# the level at which the ABI frame shape is comparable.
|
|
echo "== compile our add2.s and locals.s"
|
|
"$ROOT/scripts/tms-clang" -O2 -o "$TMP/add2.ll" "$DIR/ti-add2.c"
|
|
"$TMS_LLC" -march=tms34010 -O0 -filetype=asm "$TMP/add2.ll" -o "$TMP/add2.s"
|
|
"$ROOT/scripts/tms-clang" -O0 -o "$TMP/locals.ll" "$DIR/ti-locals.c"
|
|
"$TMS_LLC" -march=tms34010 -O0 -filetype=asm "$TMP/locals.ll" -o "$TMP/locals.s"
|
|
echo " add2.s: $(grep -c . "$TMP/add2.s") lines"
|
|
echo " locals.s: $(grep -c . "$TMP/locals.s") lines"
|
|
|
|
# Ignore our `;` end-of-line comments so a comment cannot satisfy a check.
|
|
sed 's/;.*//' "$TMP/add2.s" > "$TMP/add2.code"
|
|
sed 's/;.*//' "$TMP/locals.s" > "$TMP/locals.code"
|
|
|
|
require() {
|
|
_file=$1
|
|
_pat=$2
|
|
_what=$3
|
|
if grep -Eiq "$_pat" "$_file"; then
|
|
echo " ok $_what"
|
|
return 0
|
|
fi
|
|
echo "M2 diff: FAIL: missing $_what" >&2
|
|
echo " pattern: $_pat" >&2
|
|
sed 's/^/ /' "$_file" >&2
|
|
exit 1
|
|
}
|
|
|
|
require_rets() {
|
|
require "$1" "^[[:space:]]*RETS[[:space:]]+$2([[:space:]]|;|$)" "$3"
|
|
}
|
|
|
|
# TI golden: the argument reads are direct FP-relative loads.
|
|
require "$TI_ADD2" 'MOVE[[:space:]]+\*FP\((-32|0FFE0h)\)' \
|
|
"TI add2 reads the first argument at FP(-32)"
|
|
require "$TI_ADD2" 'MOVE[[:space:]]+\*FP\((-64|0FFC0h)\)' \
|
|
"TI add2 reads the second argument at FP(-64)"
|
|
require "$TI_ADD2" 'MOVE[[:space:]]+[^,]+[[:space:]]*,[[:space:]]*A8([[:space:]]|,|$)' \
|
|
"TI add2 returns in A8"
|
|
require_rets "$TI_ADD2" 2 "TI add2 ends with RETS 2"
|
|
|
|
require "$TI_LOCALS" '(ADDK|ADDI)[[:space:]]+\+?[1-9][0-9]*[[:space:]]*,[[:space:]]*(STK|A14)' \
|
|
"TI locals allocates a nonzero frame"
|
|
require "$TI_LOCALS" 'MOVE[[:space:]]+(STK[[:space:]]*,[[:space:]]*(FP|A13)|A14[[:space:]]*,[[:space:]]*(FP|A13))' \
|
|
"TI locals establishes FP from STK"
|
|
require "$TI_LOCALS" 'MOVE[[:space:]]+[^,]+[[:space:]]*,[[:space:]]*A8([[:space:]]|,|$)' \
|
|
"TI locals returns in A8"
|
|
require_rets "$TI_LOCALS" 0 "TI locals ends with RETS 0"
|
|
|
|
# Our asm: FP is materialised with MOVE A13,A<n>; the argument read is at an
|
|
# ADDI offset from that register. Check the offset and that the register is
|
|
# then dereferenced.
|
|
check_our_arg() {
|
|
_file=$1
|
|
_off=$2
|
|
_desc=$3
|
|
_reg=$(sed -nE "s/.*ADDI[[:space:]]+($_off)[[:space:]]*,[[:space:]]*A([0-9]+).*/\2/p" \
|
|
"$_file" | head -1)
|
|
[ -n "$_reg" ] || fail "our add2 has no ADDI $_off frame offset for $_desc"
|
|
require "$_file" "MOVE[[:space:]]+A13[[:space:]]*,[[:space:]]*A$_reg" \
|
|
"our add2 materialises FP for $_desc"
|
|
require "$_file" "MOVE[[:space:]]+\*A$_reg([[:space:]]|,|$)" \
|
|
"our add2 reads $_desc through the FP register"
|
|
}
|
|
|
|
check_our_arg "$TMP/add2.code" '-32|0FFE0h' "the first argument at FP(-32)"
|
|
check_our_arg "$TMP/add2.code" '-64|0FFC0h' "the second argument at FP(-64)"
|
|
require "$TMP/add2.code" 'MOVE[[:space:]]+[^,]+[[:space:]]*,[[:space:]]*A8([[:space:]]|,|$)' \
|
|
"our add2 returns in A8"
|
|
require_rets "$TMP/add2.code" 2 "our add2 ends with RETS 2"
|
|
|
|
require "$TMP/locals.code" 'ADDI[[:space:]]+\+?[1-9][0-9]*[[:space:]]*,[[:space:]]*A14' \
|
|
"our locals allocates a nonzero frame"
|
|
require "$TMP/locals.code" 'MOVE[[:space:]]+A14[[:space:]]*,[[:space:]]*A13' \
|
|
"our locals establishes FP from STK"
|
|
require "$TMP/locals.code" 'MOVE[[:space:]]+[^,]+[[:space:]]*,[[:space:]]*A8([[:space:]]|,|$)' \
|
|
"our locals returns in A8"
|
|
require_rets "$TMP/locals.code" 0 "our locals ends with RETS 0"
|
|
|
|
echo "M2 diff: PASS"
|