kit

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

prologue_tier.sh (6394B)


      1 #!/usr/bin/env bash
      2 # Structural checks for the -O1 known-frame prologue cost-model tiers.
      3 #
      4 # aa64 is the reference (it already selects a minimal frame shape per function);
      5 # this pins that behaviour and asserts the ported equivalents on x64 and rv64:
      6 #
      7 #   x64 slim        leaf-ish frame (no callee-saves/locals/outgoing) keeps
      8 #                   `push rbp; mov rbp,rsp` but drops the `sub rsp` reservation.
      9 #   x64 red-zone    SysV leaf with a small frame keeps its locals/saves at
     10 #                   rbp-relative offsets in the 128-byte red zone, no `sub rsp`.
     11 #   rv64 leaf       a true leaf with no frame needs (no callee-saves, no slots,
     12 #                   no outgoing, register-only params) emits NO prologue and a
     13 #                   bare `ret` -- it never saves ra / sets up s0.
     14 #
     15 # A non-leaf (calls something) must NOT take the leaf tiers, since the call
     16 # clobbers ra (rv64) / the red zone (x64): those guards are asserted too.
     17 set -euo pipefail
     18 
     19 ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
     20 KIT="${KIT:-$ROOT/build/kit}"
     21 WORK="$ROOT/build/test/opt/prologue_tier"
     22 rm -rf "$WORK"
     23 mkdir -p "$WORK"
     24 
     25 fail() {
     26   printf 'prologue-tier check FAILED: %s\n' "$1" >&2
     27   if [ -n "${2:-}" ] && [ -f "$2" ]; then
     28     sed 's/^/  | /' "$2" >&2
     29   fi
     30   exit 1
     31 }
     32 
     33 slice_func() {
     34   local src="$1" func="$2" out="$3"
     35   awk -v name="$func" '
     36     $0 ~ "^[0-9a-f]+ <" name ">:" { in_fn = 1; print; next }
     37     /^[0-9a-f]+ </ { in_fn = 0 }
     38     in_fn { print }
     39   ' "$src" > "$out"
     40 }
     41 
     42 compile_case() {
     43   local triple="$1" name="$2" src="$3"
     44   "$KIT" cc -target "$triple" -O1 -c "$src" \
     45     -o "$WORK/$name.o" > "$WORK/$name.cc.out" 2> "$WORK/$name.cc.err"
     46   "$KIT" objdump -d "$WORK/$name.o" \
     47     > "$WORK/$name.dis" 2> "$WORK/$name.objdump.err"
     48 }
     49 
     50 # ---- fixtures ----
     51 cat > "$WORK/leaf.c" <<'EOF'
     52 int leaf(int x) { return x + 1; }
     53 EOF
     54 
     55 cat > "$WORK/leaf_call.c" <<'EOF'
     56 extern int g(int);
     57 int leaf_call(int x) { return g(x) + 1; }
     58 EOF
     59 
     60 # 16 bytes of locals, leaf, no calls -> fits the SysV red zone on x64.
     61 cat > "$WORK/small_locals.c" <<'EOF'
     62 int small_locals(int x) {
     63   volatile int a[4];
     64   a[0] = x; a[1] = x + 1; a[2] = x + 2; a[3] = x + 3;
     65   return a[0] + a[1] + a[2] + a[3];
     66 }
     67 EOF
     68 
     69 # Inline asm can clobber the return-address register (rv64 ra) or the red zone
     70 # (x64 SysV), so a function containing ANY asm block must NOT take the
     71 # frame-eliding leaf/red-zone tiers, even though it makes no call.
     72 cat > "$WORK/asm_fn.c" <<'EOF'
     73 int asm_fn(int x) { __asm__ volatile("" ::: "memory"); return x + 1; }
     74 EOF
     75 cat > "$WORK/asm_locals.c" <<'EOF'
     76 int asm_locals(int x) {
     77   volatile int a[4];
     78   __asm__ volatile("" ::: "memory");
     79   a[0] = x; a[1] = x + 1; a[2] = x + 2; a[3] = x + 3;
     80   return a[0] + a[1] + a[2] + a[3];
     81 }
     82 EOF
     83 
     84 # ===================== aa64 reference (characterization) =====================
     85 compile_case aarch64-linux-gnu aa64_leaf "$WORK/leaf.c"
     86 slice_func "$WORK/aa64_leaf.dis" leaf "$WORK/aa64_leaf.fn"
     87 grep -Eq 'stp[[:space:]]+x29, x30, \[sp, #-16\]!' "$WORK/aa64_leaf.fn" ||
     88   fail 'aa64 leaf is not using the slim_prologue frame record' "$WORK/aa64_leaf.fn"
     89 
     90 # ===================== x64 slim (no sub rsp on empty frame) ==================
     91 compile_case x86_64-linux-gnu x64_leaf "$WORK/leaf.c"
     92 slice_func "$WORK/x64_leaf.dis" leaf "$WORK/x64_leaf.fn"
     93 grep -Eq 'push[[:space:]]+%rbp' "$WORK/x64_leaf.fn" ||
     94   fail 'x64 leaf dropped the rbp frame record (slim should keep push rbp)' "$WORK/x64_leaf.fn"
     95 grep -Eq 'sub[ql]?[[:space:]]+\$[0-9]+, %rsp' "$WORK/x64_leaf.fn" &&
     96   fail 'x64 leaf still reserves stack (slim tier should skip sub rsp)' "$WORK/x64_leaf.fn"
     97 
     98 # A register-only call still leaves max_outgoing==0 and an empty frame -> slim.
     99 compile_case x86_64-linux-gnu x64_leaf_call "$WORK/leaf_call.c"
    100 slice_func "$WORK/x64_leaf_call.dis" leaf_call "$WORK/x64_leaf_call.fn"
    101 grep -Eq 'sub[ql]?[[:space:]]+\$[0-9]+, %rsp' "$WORK/x64_leaf_call.fn" &&
    102   fail 'x64 register-only call still reserves stack (slim should skip sub rsp)' "$WORK/x64_leaf_call.fn"
    103 
    104 # ===================== x64 red-zone leaf (locals, no sub rsp) ================
    105 compile_case x86_64-linux-gnu x64_redzone "$WORK/small_locals.c"
    106 slice_func "$WORK/x64_redzone.dis" small_locals "$WORK/x64_redzone.fn"
    107 grep -Eq 'sub[ql]?[[:space:]]+\$[0-9]+, %rsp' "$WORK/x64_redzone.fn" &&
    108   fail 'x64 red-zone leaf still reserves stack (should use the red zone, no sub rsp)' "$WORK/x64_redzone.fn"
    109 grep -Eq '\(%rbp\)' "$WORK/x64_redzone.fn" ||
    110   fail 'x64 red-zone leaf does not address locals rbp-relative' "$WORK/x64_redzone.fn"
    111 
    112 # ===================== rv64 leaf (no frame at all) ==========================
    113 compile_case riscv64-linux-gnu rv64_leaf "$WORK/leaf.c"
    114 slice_func "$WORK/rv64_leaf.dis" leaf "$WORK/rv64_leaf.fn"
    115 grep -Eq '\bsd[[:space:]]+ra,' "$WORK/rv64_leaf.fn" &&
    116   fail 'rv64 leaf saved ra (leaf tier should emit no frame)' "$WORK/rv64_leaf.fn"
    117 grep -Eq 'addi[[:space:]]+sp, sp, -' "$WORK/rv64_leaf.fn" &&
    118   fail 'rv64 leaf adjusted sp (leaf tier should keep sp untouched)' "$WORK/rv64_leaf.fn"
    119 grep -Eq '\bret\b' "$WORK/rv64_leaf.fn" ||
    120   fail 'rv64 leaf is missing its ret' "$WORK/rv64_leaf.fn"
    121 
    122 # Guard: a non-leaf (calls g) must KEEP the frame so ra survives the call.
    123 compile_case riscv64-linux-gnu rv64_leaf_call "$WORK/leaf_call.c"
    124 slice_func "$WORK/rv64_leaf_call.dis" leaf_call "$WORK/rv64_leaf_call.fn"
    125 grep -Eq '\bsd[[:space:]]+ra,' "$WORK/rv64_leaf_call.fn" ||
    126   fail 'rv64 non-leaf dropped the ra save (leaf tier over-fired across a call)' "$WORK/rv64_leaf_call.fn"
    127 
    128 # Guard: inline asm may clobber ra, so a leaf with asm must KEEP the frame.
    129 compile_case riscv64-linux-gnu rv64_asm "$WORK/asm_fn.c"
    130 slice_func "$WORK/rv64_asm.dis" asm_fn "$WORK/rv64_asm.fn"
    131 grep -Eq '\bsd[[:space:]]+ra,' "$WORK/rv64_asm.fn" ||
    132   fail 'rv64 leaf with inline asm dropped the ra save (slim tier must not fire with asm)' "$WORK/rv64_asm.fn"
    133 
    134 # ===================== x64 red-zone must not fire with inline asm ===========
    135 # Inline asm may clobber the red zone (e.g. a `call`), so a red-zone leaf with
    136 # asm must keep its reserved stack.
    137 compile_case x86_64-linux-gnu x64_asm_locals "$WORK/asm_locals.c"
    138 slice_func "$WORK/x64_asm_locals.dis" asm_locals "$WORK/x64_asm_locals.fn"
    139 grep -Eq 'sub[ql]?[[:space:]]+\$[0-9]+, %rsp' "$WORK/x64_asm_locals.fn" ||
    140   fail 'x64 red-zone leaf with inline asm skipped sub rsp (must reserve, asm may clobber the red zone)' "$WORK/x64_asm_locals.fn"
    141 
    142 printf 'prologue-tier: ok\n'