Files
tms-c-compiler/scripts/tests/m2/diff/check.sh
T

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"