65816-llvm-mos/scripts/smokeTest.sh
Scott Duensing 873eab4922 Checkpoint.
2026-04-25 17:07:28 -05:00

275 lines
9.3 KiB
Bash
Executable file

#!/usr/bin/env bash
# W65816 backend smoke test. Run after any change to confirm the
# scaffold still builds and llc still registers the target. Non-zero
# exit on any failure.
#
# Usage: scripts/smokeTest.sh [--build]
# --build Run ninja to (re)build LLVMW65816* + llc before testing.
# Without this flag the script assumes tools/llvm-mos-build
# is already up to date.
set -euo pipefail
source "$(dirname "$0")/common.sh"
BUILD_DIR="$TOOLS_DIR/llvm-mos-build"
LLC="$BUILD_DIR/bin/llc"
LLVM_MC="$BUILD_DIR/bin/llvm-mc"
doBuild=0
for arg in "$@"; do
case "$arg" in
--build) doBuild=1 ;;
*) die "unknown flag: $arg" ;;
esac
done
[ -x "$LLC" ] || die "llc not found at $LLC; run setup.sh and applyBackend.sh, or pass --build"
if [ "$doBuild" -eq 1 ]; then
log "ninja LLVMW65816* llc llvm-mc llvm-objdump"
ninja -C "$BUILD_DIR" LLVMW65816Info LLVMW65816Desc LLVMW65816CodeGen \
LLVMW65816AsmParser LLVMW65816Disassembler llc llvm-mc llvm-objdump
fi
# 1. Target must be registered.
log "check: llc --version lists w65816"
if ! "$LLC" --version 2>/dev/null | grep -q "^[[:space:]]*w65816[[:space:]]"; then
die "llc does not list the w65816 target"
fi
# 2. Empty IR must compile to nothing.
log "check: llc -march=w65816 -filetype=null /dev/null exits 0"
"$LLC" -march=w65816 -filetype=null /dev/null
# 3. Trivial IR that shouldn't touch our (unimplemented) codegen paths.
tmp="$(mktemp --suffix=.ll)"
trap 'rm -f "$tmp"' EXIT
cat > "$tmp" <<'EOF'
; ModuleID = 'smoke'
target triple = "w65816-unknown-unknown"
; Empty module: exercises target initialization only.
EOF
log "check: llc accepts an empty module with w65816 triple"
"$LLC" -filetype=null "$tmp"
# 4. MC layer round-trip. Assemble a representative mix of addressing
# modes and mode-switching instructions and grep for the expected
# encoding bytes. Hex-byte strings are stable across llvm-mc
# formatting changes, unlike full-line string matching.
if [ -x "$LLVM_MC" ]; then
log "check: llvm-mc -arch=w65816 emits expected encodings"
# Only exercise instructions that round-trip cleanly:
# - LDA/LDX/LDY immediates without explicit force use the _Imm16
# form (codegen-dominant path). A pure `lda #x` assembles to
# LDA_Imm16 since the _Imm8 variant is isCodeGenOnly.
mcInput=' nop
rep #0x30
sep #0x20
lda #0x1234
sta 0x10
sta 0x1000
sta 0x010000
mvn 0x01, 0x02
jsl 0x012345'
mcOut="$(printf '%s\n' "$mcInput" | "$LLVM_MC" -arch=w65816 -show-encoding 2>&1)"
assertHas() {
if ! printf '%s\n' "$mcOut" | grep -qF "$1"; then
warn "missing expected encoding: $1"
printf '%s\n' "$mcOut" >&2
die "llvm-mc did not produce expected encoding"
fi
}
assertHas "[0xea]"
assertHas "[0xc2,0x30]"
assertHas "[0xe2,0x20]"
assertHas "[0xa9,0x34,0x12]"
assertHas "[0x85,0x10]"
assertHas "[0x8d,0x00,0x10]"
assertHas "[0x8f,0x00,0x00,0x01]"
assertHas "[0x54,0x01,0x02]"
assertHas "[0x22,0x45,0x23,0x01]"
else
warn "llvm-mc not built; skipping MC round-trip check"
fi
# 5. Disassembler round-trip. A raw byte stream fed to llvm-mc
# --disassemble should produce the mnemonic we expect.
if [ -x "$LLVM_MC" ]; then
log "check: llvm-mc --disassemble decodes bytes back to mnemonics"
disasmOut="$(printf '0xea 0xa9 0x34 0x12 0x85 0x10 0x8d 0x00 0x10 0x6b\n' \
| "$LLVM_MC" --disassemble --triple=w65816 2>&1)"
for mnem in "nop" "lda #0x1234" "sta 0x10" "sta 0x1000" "rtl"; do
if ! printf '%s\n' "$disasmOut" | grep -qF "$mnem"; then
warn "disassembler missing: $mnem"
printf '%s\n' "$disasmOut" >&2
die "disassembler round-trip failed"
fi
done
fi
# 6. End-to-end codegen: IR -> asm -> ELF -> disassembly.
# This is the first real codegen test: verifies that our LowerReturn,
# DAG pattern for the i16 constant pseudo, and prologue-emitting
# frame lowering produce runnable 65816 machine code.
OBJDUMP="$BUILD_DIR/bin/llvm-objdump"
if [ -x "$LLC" ] && [ -x "$LLVM_MC" ] && [ -x "$OBJDUMP" ]; then
log "check: end-to-end IR -> asm -> ELF -> disasm for a trivial function"
irFile="$(mktemp --suffix=.ll)"
sFile="$(mktemp --suffix=.s)"
oFile="$(mktemp --suffix=.o)"
trap 'rm -f "$irFile" "$sFile" "$oFile"' EXIT
cat > "$irFile" <<'EOF'
target triple = "w65816-unknown-unknown"
define i16 @answer() { ret i16 42 }
EOF
"$LLC" -march=w65816 "$irFile" -o "$sFile"
"$LLVM_MC" -arch=w65816 -filetype=obj "$sFile" -o "$oFile"
disasm="$("$OBJDUMP" --triple=w65816 -d "$oFile" 2>&1)"
for expect in "rep #0x30" "lda #0x2a" "rtl"; do
if ! printf '%s\n' "$disasm" | grep -qF "$expect"; then
warn "end-to-end pipeline missing: $expect"
printf '%s\n' "$disasm" >&2
die "end-to-end pipeline failed"
fi
done
fi
# 7. Real codegen check: a non-trivial function exercising globals,
# arithmetic, branches, bitwise. This tests our DAG selection
# patterns and AsmPrinter pseudo expansions.
if [ -x "$LLC" ]; then
log "check: llc compiles a multi-pattern function"
irFile="$(mktemp --suffix=.ll)"
sFile="$(mktemp --suffix=.s)"
trap 'rm -f "$irFile" "$sFile"' EXIT
cat > "$irFile" <<'EOF'
target triple = "w65816-unknown-unknown"
@a = global i16 0
@b = global i16 0
define i16 @demo() {
%x = load i16, ptr @a
%y = load i16, ptr @b
%s = add i16 %x, %y
%m = and i16 %s, 4095
%c = icmp ult i16 %m, 100
br i1 %c, label %lo, label %hi
lo:
ret i16 0
hi:
ret i16 %m
}
EOF
"$LLC" -march=w65816 "$irFile" -o "$sFile"
for expect in "rep #0x30" "lda a" "clc" "adc b" "and #0xfff" "cmp #0x64" "bcs" "rtl"; do
if ! grep -qF "$expect" "$sFile"; then
warn "multi-pattern test missing: $expect"
cat "$sFile" >&2
die "multi-pattern test failed"
fi
done
fi
# 8. Function call check: caller passes i16 in A, callee adds, returns.
if [ -x "$LLC" ]; then
log "check: llc compiles a function call (single i16 arg in A)"
irCallFile="$(mktemp --suffix=.ll)"
sCallFile="$(mktemp --suffix=.s)"
trap 'rm -f "$irFile" "$sFile" "$irCallFile" "$sCallFile"' EXIT
cat > "$irCallFile" <<'EOF'
target triple = "w65816-unknown-unknown"
define i16 @inc(i16 %x) {
%r = add i16 %x, 1
ret i16 %r
}
define i16 @caller() {
%r = call i16 @inc(i16 41)
ret i16 %r
}
EOF
"$LLC" -march=w65816 "$irCallFile" -o "$sCallFile"
# Caller passes 41 in A and JSL's inc. Inc is now an `inc a`
# peephole (was clc; adc #1 before the INA_PSEUDO pattern).
for expect in "lda #0x29" "jsl inc" "inc a"; do
if ! grep -qF "$expect" "$sCallFile"; then
warn "call test missing: $expect"
cat "$sCallFile" >&2
die "call test failed"
fi
done
fi
# 9. Multi-arg sum: 3-arg function reads args 1 and 2 via stack-relative
# addressing.
if [ -x "$LLC" ]; then
log "check: llc compiles a 3-arg function (stack-relative reads)"
irMaFile="$(mktemp --suffix=.ll)"
sMaFile="$(mktemp --suffix=.s)"
trap 'rm -f "$irFile" "$sFile" "$irCallFile" "$sCallFile" "$irMaFile" "$sMaFile"' EXIT
cat > "$irMaFile" <<'EOF'
target triple = "w65816-unknown-unknown"
define i16 @sum3(i16 %a, i16 %b, i16 %c) {
%ab = add i16 %a, %b
%r = add i16 %ab, %c
ret i16 %r
}
EOF
"$LLC" -march=w65816 "$irMaFile" -o "$sMaFile"
for expect in "adc 0x4, s" "adc 0x6, s" "rtl"; do
if ! grep -qF "$expect" "$sMaFile"; then
warn "multi-arg test missing: $expect"
cat "$sMaFile" >&2
die "multi-arg test failed"
fi
done
fi
# 10. i8 codegen: pure-i8 function uses SEP #$20 prologue and `inc a`.
if [ -x "$LLC" ]; then
log "check: llc compiles a pure-i8 function (SEP #\$20 prologue)"
irI8File="$(mktemp --suffix=.ll)"
sI8File="$(mktemp --suffix=.s)"
trap 'rm -f "$irFile" "$sFile" "$irCallFile" "$sCallFile" "$irMaFile" "$sMaFile" "$irI8File" "$sI8File"' EXIT
cat > "$irI8File" <<'EOF'
target triple = "w65816-unknown-unknown"
define i8 @i8_inc(i8 %x) {
%r = add i8 %x, 1
ret i8 %r
}
EOF
"$LLC" -march=w65816 "$irI8File" -o "$sI8File"
for expect in "sep #0x20" "inc a" "rtl"; do
if ! grep -qF "$expect" "$sI8File"; then
warn "i8 test missing: $expect"
cat "$sI8File" >&2
die "i8 test failed"
fi
done
fi
# 11. Real C through clang. Uses the clang front-end if it has been
# built; skipped otherwise (clang takes 15-30 minutes to build the
# first time; afterwards rebuilds are fast).
CLANG="$BUILD_DIR/bin/clang"
if [ -x "$CLANG" ] && [ -x "$OBJDUMP" ]; then
log "check: clang -target w65816 -O2 compiles a tiny C function"
cFile="$(mktemp --suffix=.c)"
oFile2="$(mktemp --suffix=.o)"
trap 'rm -f "$irFile" "$sFile" "$irCallFile" "$sCallFile" "$irMaFile" "$sMaFile" "$irI8File" "$sI8File" "$cFile" "$oFile2"' EXIT
cat > "$cFile" <<'EOF'
int answer(void) { return 42; }
EOF
"$CLANG" --target=w65816 -O2 -c "$cFile" -o "$oFile2"
disasm="$("$OBJDUMP" --triple=w65816 -d "$oFile2" 2>&1)"
for expect in "rep #0x30" "lda #0x2a" "rtl"; do
if ! printf '%s\n' "$disasm" | grep -qF "$expect"; then
warn "clang test missing: $expect"
printf '%s\n' "$disasm" >&2
die "clang end-to-end test failed"
fi
done
fi
log "all smoke checks passed"