aa64_tail_call.sh (3390B)
1 #!/usr/bin/env bash 2 # Structural aarch64 tail-call checks. 3 # 4 # These cases prove that realized tails are sibling calls: 5 # - direct stack-arg tail calls reuse the caller's incoming stack-arg window 6 # - direct tails lower to AARCH64_JUMP26 (`b`), not AARCH64_CALL26 (`bl`) 7 # - indirect tails lower to `br`, not `blr` 8 # - the caller frame is restored before the sibling branch 9 set -euo pipefail 10 11 ROOT="$(cd "$(dirname "$0")/../.." && pwd)" 12 KIT="${KIT:-$ROOT/build/kit}" 13 WORK="$ROOT/build/test/opt/aa64_tail_call" 14 mkdir -p "$WORK" 15 16 fail() { 17 printf 'aa64-tail-call check FAILED: %s\n' "$1" >&2 18 if [ -n "${2:-}" ] && [ -f "$2" ]; then 19 sed 's/^/ | /' "$2" >&2 20 fi 21 exit 1 22 } 23 24 slice_func() { 25 local src="$1" func="$2" out="$3" 26 awk -v name="$func" ' 27 $0 ~ "^[0-9a-f]+ <" name ">:" { in_fn = 1; print; next } 28 /^[0-9a-f]+ </ { in_fn = 0 } 29 in_fn { print } 30 ' "$src" > "$out" 31 } 32 33 compile_case() { 34 local name="$1" src="$2" 35 "$KIT" cc -target aarch64-linux-gnu -O1 -c "$src" \ 36 -o "$WORK/$name.o" > "$WORK/$name.cc.out" 2> "$WORK/$name.cc.err" 37 "$KIT" objdump -d "$WORK/$name.o" \ 38 > "$WORK/$name.dis" 2> "$WORK/$name.objdump.err" 39 "$KIT" objdump -r "$WORK/$name.o" \ 40 > "$WORK/$name.relocs" 2> "$WORK/$name.relocs.err" 41 } 42 43 compile_case direct_stack "$ROOT/test/toy/cases/25_tail_many_stack_args.toy" 44 slice_func "$WORK/direct_stack.dis" caller "$WORK/direct_stack.caller.dis" 45 46 if grep -Eq '\bbl\b.*target' "$WORK/direct_stack.caller.dis"; then 47 fail 'direct stack-arg tail used bl target' "$WORK/direct_stack.caller.dis" 48 fi 49 if ! grep -Eq '\bb\b.*target.*AARCH64_JUMP26' "$WORK/direct_stack.caller.dis"; then 50 fail 'direct stack-arg tail missing b target / AARCH64_JUMP26' \ 51 "$WORK/direct_stack.caller.dis" 52 fi 53 if ! grep -Eq '\bstr\b.*\[x29, #16\]' "$WORK/direct_stack.caller.dis" || 54 ! grep -Eq '\bstr\b.*\[x29, #24\]' "$WORK/direct_stack.caller.dis"; then 55 fail 'direct stack-arg tail did not write caller incoming stack window' \ 56 "$WORK/direct_stack.caller.dis" 57 fi 58 if ! awk ' 59 /add[[:space:]]+sp, sp,/ { restored = 1 } 60 /[[:space:]]b[[:space:]]+.*target.*AARCH64_JUMP26/ { 61 found = 1; ok = restored; exit 62 } 63 END { exit(found && ok ? 0 : 1) } 64 ' "$WORK/direct_stack.caller.dis"; then 65 fail 'direct tail branched before restoring sp' "$WORK/direct_stack.caller.dis" 66 fi 67 if ! grep -Eq 'AARCH64_JUMP26[[:space:]]+target' "$WORK/direct_stack.relocs"; then 68 fail 'direct tail relocation is not AARCH64_JUMP26' "$WORK/direct_stack.relocs" 69 fi 70 if grep -Eq 'AARCH64_CALL26[[:space:]]+target' "$WORK/direct_stack.relocs"; then 71 fail 'direct tail emitted AARCH64_CALL26 relocation to target' \ 72 "$WORK/direct_stack.relocs" 73 fi 74 75 compile_case indirect "$ROOT/test/toy/cases/32_musttail_indirect.toy" 76 slice_func "$WORK/indirect.dis" apply "$WORK/indirect.apply.dis" 77 78 if grep -Eq '\bblr\b' "$WORK/indirect.apply.dis"; then 79 fail 'indirect musttail used blr' "$WORK/indirect.apply.dis" 80 fi 81 if ! grep -Eq '\bbr[[:space:]]+x[0-9]+' "$WORK/indirect.apply.dis"; then 82 fail 'indirect musttail missing br' "$WORK/indirect.apply.dis" 83 fi 84 if ! awk ' 85 /ldp[[:space:]]+x29, x30,/ { restored = 1 } 86 /[[:space:]]br[[:space:]]+x[0-9]+/ { found = 1; ok = restored; exit } 87 END { exit(found && ok ? 0 : 1) } 88 ' "$WORK/indirect.apply.dis"; then 89 fail 'indirect tail branched before restoring frame record' \ 90 "$WORK/indirect.apply.dis" 91 fi 92 93 printf 'aa64-tail-call: ok\n'