aa64_apple_varargs.sh (4573B)
1 #!/usr/bin/env bash 2 # AArch64 call-boundary regression: 3 # - Apple unnamed aggregate stack slots keep semantic bytes separate from 4 # their rounded physical carriers (Six: 6 in 8, Twelve: 12 in 16). 5 # - an indirect target initially in x0 survives exact-width stack-argument 6 # marshalling, which owns the backend-private x16/x17 pair. 7 set -euo pipefail 8 9 ROOT="$(cd "$(dirname "$0")/../.." && pwd)" 10 KIT="${KIT:-$ROOT/build/kit}" 11 WORK="$ROOT/build/test/opt/aa64_apple_varargs" 12 SRC="$WORK/fixture.c" 13 mkdir -p "$WORK" 14 15 cat > "$SRC" <<'EOF' 16 typedef struct Six { unsigned char b[6]; } Six; 17 typedef struct Twelve { unsigned char b[12]; } Twelve; 18 19 __attribute__((noinline)) void variadic_sink(int tag, ...) { 20 volatile int seen = tag; 21 (void)seen; 22 } 23 24 __attribute__((noinline)) void pass_apple_varargs(Six six, Twelve twelve) { 25 variadic_sink(7, six, twelve); 26 } 27 28 typedef int (*Indirect)(int, int, int, int, int, int, int, int, Six); 29 30 __attribute__((noinline)) int call_indirect(Indirect fn, Six six) { 31 return fn(1, 2, 3, 4, 5, 6, 7, 8, six); 32 } 33 34 __attribute__((noinline)) static int check_indirect( 35 int a, int b, int c, int d, int e, int f, int g, int h, Six six) { 36 return a == 1 && b == 2 && c == 3 && d == 4 && e == 5 && f == 6 && 37 g == 7 && h == 8 && six.b[0] == 9 && six.b[1] == 10 && 38 six.b[2] == 11 && six.b[3] == 12 && six.b[4] == 13 && 39 six.b[5] == 14; 40 } 41 42 int test_main(void) { 43 Six six = {{9, 10, 11, 12, 13, 14}}; 44 return call_indirect(check_indirect, six) ? 0 : 1; 45 } 46 EOF 47 48 fail() { 49 printf 'aa64 Apple-varargs regression FAILED: %s\n' "$1" >&2 50 if [ -n "${2:-}" ] && [ -f "$2" ]; then 51 sed 's/^/ | /' "$2" >&2 52 fi 53 exit 1 54 } 55 56 slice_fn() { 57 local name="$1" input="$2" output="$3" 58 awk -v name="$name" ' 59 $0 ~ "<_?" name ">:" { in_fn = 1; next } 60 /^[0-9a-f]+ <[^>]+>:/ { in_fn = 0 } 61 in_fn { print } 62 ' "$input" > "$output" 63 } 64 65 for opt in O0 O1; do 66 obj="$WORK/apple.$opt.o" 67 dis="$WORK/apple.$opt.dis" 68 body="$WORK/apple.$opt.pass.dis" 69 "$KIT" build-obj -target aarch64-apple-darwin "-$opt" -c "$SRC" -o "$obj" \ 70 > "$WORK/apple.$opt.compile.out" 2>&1 71 "$KIT" objdump -d "$obj" > "$dis" 2>&1 72 slice_fn pass_apple_varargs "$dis" "$body" 73 [ -s "$body" ] || fail "$opt pass_apple_varargs missing" "$dis" 74 75 # Six is the <=8 control: it is read at six bytes, zero-extended, and the 76 # complete 8-byte carrier is stored. Twelve occupies the following 16-byte 77 # carrier, but only its live 8+4 bytes may be read or written. 78 grep -Eq 'str[[:space:]]+x[0-9]+, \[sp\]' "$body" || 79 fail "$opt missing zero-padded Six carrier store" "$body" 80 grep -Eq 'str[[:space:]]+x[0-9]+, \[sp, #8\]' "$body" || 81 fail "$opt missing first eight bytes of Twelve" "$body" 82 grep -Eq 'str[[:space:]]+w[0-9]+, \[sp, #16\]' "$body" || 83 fail "$opt missing four-byte Twelve tail" "$body" 84 if grep -Eq 'str[[:space:]]+x[0-9]+, \[sp, #16\]' "$body"; then 85 fail "$opt widened Twelve tail into carrier padding" "$body" 86 fi 87 xloads="$(grep -Ec '(ldr|ldur)[[:space:]]+x[0-9]+, \[x29' "$body" || true)" 88 [ "$xloads" -eq 1 ] || 89 fail "$opt expected one 8-byte Twelve source load, got $xloads" "$body" 90 done 91 92 # O1 keeps fn in x0. The target must be saved before the Six stack writes use 93 # x16/x17, then moved into x16 only after those writes are complete. 94 call_body="$WORK/apple.O1.call.dis" 95 slice_fn call_indirect "$WORK/apple.O1.dis" "$call_body" 96 [ -s "$call_body" ] || fail 'O1 call_indirect missing' "$WORK/apple.O1.dis" 97 stash_line="$(grep -n 'mov[[:space:]]\+x30, x0' "$call_body" | head -n1 | cut -d: -f1 || true)" 98 stack_line="$(grep -n 'str[[:space:]]\+w[0-9]\+, \[sp\]' "$call_body" | head -n1 | cut -d: -f1 || true)" 99 reload_line="$(grep -n 'mov[[:space:]]\+x16, x30' "$call_body" | head -n1 | cut -d: -f1 || true)" 100 call_line="$(grep -n 'blr[[:space:]]\+x16' "$call_body" | head -n1 | cut -d: -f1 || true)" 101 if [ -z "$stash_line" ] || [ -z "$stack_line" ] || [ -z "$reload_line" ] || 102 [ -z "$call_line" ] || [ "$stash_line" -ge "$stack_line" ] || 103 [ "$stack_line" -ge "$reload_line" ] || [ "$reload_line" -ge "$call_line" ]; then 104 fail 'indirect target lifetime is not stash -> stack args -> reload -> blr' \ 105 "$call_body" 106 fi 107 108 # Execute the indirect-call oracle when this host can run AArch64 natively. 109 case "$(uname -m 2>/dev/null)" in 110 arm64|aarch64) 111 if "$KIT" run -O1 -e test_main "$SRC" > "$WORK/native.run.out" 2>&1; then 112 rc=0 113 else 114 rc=$? 115 fi 116 [ "$rc" -eq 0 ] || fail "native indirect call exited $rc" "$WORK/native.run.out" 117 ;; 118 esac 119 120 printf 'aa64-apple-varargs: ok\n'