kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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'