192 lines
6.6 KiB
Bash
Executable File
192 lines
6.6 KiB
Bash
Executable File
#!/bin/sh
|
|
# M3 differential TI-compiler control-flow/Xlib check.
|
|
#
|
|
# scripts/tests/m3/diff/check.sh
|
|
#
|
|
# The stored ti-branch.s/ti-xhello.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 equivalent C.
|
|
#
|
|
# The oracle is the ABI shape, not instruction selection. Each requirement
|
|
# accepts the TI spellings and ours:
|
|
#
|
|
# sumto a conditional branch and an unconditional loop back-edge,
|
|
# result in A8, RETS 2
|
|
# max2 a conditional branch, result in A8, RETS 2
|
|
# xhello nine argument pushes for XCreateSimpleWindow (background first,
|
|
# display last), and the ROM bit displacements 0x480 (Display.screens)
|
|
# / 0x40 (Screen.root)
|
|
#
|
|
# TI emits JRLE/JRUC where we emit JAxx/JAUC; both are accepted. TI prints the
|
|
# displacements as decimal 1152/64, so 480h/40h are accepted too.
|
|
#
|
|
# Our branch.c is built exactly like the M3 gate: tms-clang -O2 with the
|
|
# -replexitval=never optimizer (that rewrite would turn sumto's loop into an
|
|
# i33 multiply, which the target cannot select), then llc -O0.
|
|
#
|
|
# Overridable: TMS_CLANG TMS_OPT TMS_BITADDR TMS_LLC TI_BRANCH TI_XHELLO
|
|
set -eu
|
|
|
|
ROOT=$(cd "$(dirname "$0")/../../../.." && pwd)
|
|
DIR=$ROOT/scripts/tests/m3/diff
|
|
SRC=$ROOT/scripts/m3
|
|
|
|
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_BRANCH=${TI_BRANCH:-$DIR/ti-branch.s}
|
|
TI_XHELLO=${TI_XHELLO:-$DIR/ti-xhello.s}
|
|
export TMS_CLANG TMS_OPT TMS_BITADDR
|
|
|
|
fail() {
|
|
echo "M3 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_BRANCH" ] || fail "stored TI golden missing: $TI_BRANCH"
|
|
[ -f "$TI_XHELLO" ] || fail "stored TI golden missing: $TI_XHELLO"
|
|
|
|
TMP=$(mktemp -d "${TMPDIR:-/tmp}/m3-diff.XXXXXX")
|
|
trap 'rm -rf "$TMP"' EXIT HUP INT TERM
|
|
|
|
# branch.c is generated through a wrapper so only this file keeps its loop.
|
|
OPT_NOREPLEXIT=$TMP/opt-noreplexit
|
|
printf '#!/bin/sh\nexec "%s" -replexitval=never "$@"\n' "$TMS_OPT" > "$OPT_NOREPLEXIT"
|
|
chmod +x "$OPT_NOREPLEXIT"
|
|
|
|
echo "== compile our branch.s and xhello.s"
|
|
TMS_OPT=$OPT_NOREPLEXIT "$ROOT/scripts/tms-clang" -O2 -I "$SRC" \
|
|
-o "$TMP/branch.ll" "$SRC/branch.c"
|
|
"$TMS_LLC" -march=tms34010 -O0 -filetype=asm "$TMP/branch.ll" -o "$TMP/branch.s"
|
|
"$ROOT/scripts/tms-clang" -O2 -I "$SRC" \
|
|
-o "$TMP/xhello.ll" "$SRC/xhello.c"
|
|
"$TMS_LLC" -march=tms34010 -O0 -filetype=asm "$TMP/xhello.ll" -o "$TMP/xhello.s"
|
|
echo " branch.s: $(grep -c . "$TMP/branch.s") lines"
|
|
echo " xhello.s: $(grep -c . "$TMP/xhello.s") lines"
|
|
|
|
# Our llc output has `;` end-of-line comments (`; %bb.0`). Strip them so a
|
|
# comment cannot satisfy a check; TI asm has none.
|
|
for f in "$TMP/branch.s" "$TMP/xhello.s"; do
|
|
sed 's/;.*//' "$f" > "$f.code"
|
|
done
|
|
|
|
# body FILE FUNC: from `_func:` to the next .globl/.end.
|
|
body() {
|
|
awk -v want="_$2:" '
|
|
$1 == want { infn = 1; next }
|
|
infn && ($1 == ".globl" || $1 == ".end") { exit }
|
|
infn { print }
|
|
' "$1"
|
|
}
|
|
|
|
require() {
|
|
_file=$1
|
|
_pat=$2
|
|
_what=$3
|
|
if grep -Eiq "$_pat" "$_file"; then
|
|
echo " ok $_what"
|
|
return 0
|
|
fi
|
|
echo "M3 diff: FAIL: missing $_what" >&2
|
|
echo " pattern: $_pat" >&2
|
|
sed 's/^/ /' "$_file" >&2
|
|
exit 1
|
|
}
|
|
|
|
require_rets2() {
|
|
require "$1" '^[[:space:]]*RETS[[:space:]]+2([[:space:]]|$)' "$2"
|
|
}
|
|
|
|
# A conditional branch: JA/JR plus a condition suffix. JAUC/JRUC/JRU do not
|
|
# match because their suffix is U/UC.
|
|
COND='^[[:space:]]*J[A-Z]*(EQ|NE|LT|LE|GT|GE|V|NV|P|N|Z|NZ|PL|MI)[[:space:]]'
|
|
# An unconditional branch: our JAUC, TI's JRUC (and bare JRU).
|
|
UNCOND='^[[:space:]]*J(AUC|RUC|RU|UMP)[[:space:]]'
|
|
|
|
# has_backedge FILE: true when a branch targets a label defined earlier in the
|
|
# same function body. TI closes the loop with the conditional JRLE L4; we
|
|
# close it with the unconditional JAUC LBB0_1, so either form counts.
|
|
has_backedge() {
|
|
awk '
|
|
/^[A-Za-z_][A-Za-z0-9_]*:/ {
|
|
lbl = $1
|
|
sub(/:$/, "", lbl)
|
|
pos[lbl] = NR
|
|
next
|
|
}
|
|
/^[[:space:]]*J/ {
|
|
if ($2 in pos && pos[$2] < NR)
|
|
found = 1
|
|
}
|
|
END { exit found ? 0 : 1 }
|
|
' "$1"
|
|
}
|
|
|
|
check_branch() { # check_branch FILE LABEL
|
|
_f=$1
|
|
body "$_f" sumto > "$TMP/sumto.body"
|
|
body "$_f" max2 > "$TMP/max2.body"
|
|
require "$TMP/sumto.body" "$COND" "$2: sumto has a conditional branch"
|
|
require "$TMP/sumto.body" "$UNCOND" "$2: sumto has an unconditional branch"
|
|
if ! has_backedge "$TMP/sumto.body"; then
|
|
echo "M3 diff: FAIL: $2: sumto has no loop back-edge" >&2
|
|
sed 's/^/ /' "$TMP/sumto.body" >&2
|
|
exit 1
|
|
fi
|
|
echo " ok $2: sumto branches back to the loop"
|
|
require_rets2 "$TMP/sumto.body" "$2: sumto ends with RETS 2"
|
|
require "$TMP/max2.body" "$COND" "$2: max2 has a conditional branch"
|
|
require_rets2 "$TMP/max2.body" "$2: max2 ends with RETS 2"
|
|
}
|
|
|
|
echo "== ABI shape: TI golden"
|
|
check_branch "$TI_BRANCH" "TI"
|
|
echo "== ABI shape: our compiler"
|
|
check_branch "$TMP/branch.s.code" "ours"
|
|
|
|
# Count the argument pushes since the previous CALLA, then require nine for
|
|
# XCreateSimpleWindow. Both compilers push the last argument first: display,
|
|
# root, x, y, width, height, border_width, border, background.
|
|
count_xcreate_pushes() {
|
|
awk '
|
|
/^_xhello:/ { infn = 1; n = 0; next }
|
|
!infn { next }
|
|
/CALLA[[:space:]]+_XCreateSimpleWindow/ { print n; exit }
|
|
/CALLA/ { n = 0; next }
|
|
/MOVE[[:space:]]+[^,]+,[[:space:]]*\*(STK|A14)\+/ { n++ }
|
|
' "$1"
|
|
}
|
|
|
|
for pair in "$TI_XHELLO:TI" "$TMP/xhello.s.code:ours"; do
|
|
f=${pair%%:*}
|
|
who=${pair##*:}
|
|
pushes=$(count_xcreate_pushes "$f")
|
|
[ -n "$pushes" ] || fail "$who xhello has no CALLA _XCreateSimpleWindow"
|
|
[ "$pushes" = "9" ] \
|
|
|| fail "$who XCreateSimpleWindow has $pushes argument pushes, want 9"
|
|
echo " ok $who: XCreateSimpleWindow takes nine argument pushes"
|
|
done
|
|
|
|
HEX480='(0x480|480h|[^0-9a-f]1152[^0-9a-f])'
|
|
HEX40='(0x40|40h|[^0-9a-f]64[^0-9a-f])'
|
|
|
|
check_offsets() { # check_offsets FILE WHO
|
|
_f=$1
|
|
_who=$2
|
|
body "$_f" xhello > "$TMP/xhello.body"
|
|
require "$TMP/xhello.body" "$HEX480" "$_who: Display.screens at bit 0x480"
|
|
require "$TMP/xhello.body" "$HEX40" "$_who: Screen.root at bit 0x40"
|
|
}
|
|
|
|
echo "== ROM bit displacements"
|
|
check_offsets "$TI_XHELLO" "TI"
|
|
check_offsets "$TMP/xhello.s.code" "ours"
|
|
|
|
echo "M3 diff: PASS"
|