commit fcb3b729a797778a0eb41257685491eaaf760cf4
parent 38d3c95b7c26a5a8fdb6b0ffa33487ce5f2d5b07
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Mon, 15 Jun 2026 15:47:27 -0700
Add native frame and debug regressions
Diffstat:
6 files changed, 138 insertions(+), 109 deletions(-)
diff --git a/src/arch/aa64/native.c b/src/arch/aa64/native.c
@@ -61,9 +61,10 @@ enum {
AA_TMP1 = 17u,
/* Callee-saved frame-base anchor. The single-pass far-slot fast path
* (aa_emit_mem / AA_PATCH_SLOT) addresses fixed slots at positive scaled
- * offsets from a stable bottom anchor: sp itself when the function never moves
- * sp, else this register, set to the frame base in the prologue and saved /
- * restored like a callee-save (only when the function calls alloca). */
+ * offsets from a stable bottom anchor: sp itself when the function never
+ * moves sp, else this register, set to the frame base in the prologue and
+ * saved / restored like a callee-save (only when the function calls alloca).
+ */
AA_FRAME_BASE = 28u,
AA_FP = 29u,
AA_LR = 30u,
@@ -81,11 +82,12 @@ enum {
* the frame below the saved pair is deferred (patched once frame_size is
* final), plus — on stack-probing targets (Windows) — the page probe. The
* single-pass path uses no callee-saved registers (enforced in
- * nd_scratch_acquire), so nothing else is deferred and the reserved region is
- * tiny: worst-case sub = load_imm(2)+3 = 5; +probe load_imm(2)+5 = 7 → 12. */
+ * nd_scratch_acquire), so nothing else is deferred except the optional
+ * alloca frame-base anchor setup: worst-case sub = load_imm(2)+3 = 5; +probe
+ * load_imm(2)+5 = 7; +frame-base save/mov = 2. */
AA_NDT_FIXED_ENTRY_WORDS = 2u,
- AA_NDT_SUB_WORDS = 12u, /* region buffer: worst case (probe + sub) */
- AA_NDT_SUB_WORDS_NOPROBE = 5u, /* reserved region when no stack probe */
+ AA_NDT_SUB_WORDS = 14u, /* worst case (probe + sub + frame-base) */
+ AA_NDT_SUB_WORDS_NOPROBE = 7u, /* reserved region when no stack probe */
};
/* Windows/AArch64 TLS Local-Exec. The TEB pointer lives in the reserved
@@ -299,10 +301,10 @@ typedef struct AANativeTarget {
* plus an AA_PATCH_SLOT, instead of the multi-insn `sub xN,x29,#off ; ldur`
* address build. The base is a stable bottom anchor: sp (the common case) or
* AA_FRAME_BASE when the function moves sp via alloca. Decided at func_begin
- * (off when a Windows GP home area shifts the layout); the frame-base fallback
- * is armed lazily on the first alloca. Only 4/8-byte int/fp slots take this
- * path — byte/half stay on the existing path (their positive scaled reach,
- * 4 KB/8 KB, is too small to guarantee a one-word patch). */
+ * (off when a Windows GP home area shifts the layout); the frame-base
+ * fallback is armed lazily on the first alloca. Only 4/8-byte int/fp slots
+ * take this path — byte/half stay on the existing path (their positive scaled
+ * reach, 4 KB/8 KB, is too small to guarantee a one-word patch). */
u8 slot_sp_base;
u8 uses_frame_base; /* an alloca fired: patch AA_PATCH_SLOT against
* AA_FRAME_BASE and set it up in the prologue. */
@@ -507,7 +509,8 @@ static void aa_emit_add_i64(AANativeTarget* a, u32 rd, u32 rn, i64 off) {
/* Unscaled load with an explicit load opcode (AA64_LDST_OPC_LDR for a plain
* zero-extending load, AA64_LDST_OPC_LDRS_X for a sign-extending ldursb/ldursh
* into the X register). */
-static u32 aa_ldur_op_v(u32 size, u32 v, u32 ld_opc, u32 rt, u32 rn, i32 simm9) {
+static u32 aa_ldur_op_v(u32 size, u32 v, u32 ld_opc, u32 rt, u32 rn,
+ i32 simm9) {
return aa64_ldst_simm9_pack((AA64LdStSimm9){.size = size,
.V = v,
.opc = ld_opc,
@@ -535,12 +538,8 @@ static u32 aa_stur_v(u32 size, u32 v, u32 rt, u32 rn, i32 simm9) {
static u32 aa_ldr_uimm_op_v(u32 size, u32 v, u32 ld_opc, u32 rt, u32 rn,
u32 byte_off) {
u32 sc = byte_off >> size;
- return aa64_ldst_uimm_pack((AA64LdStUimm){.size = size,
- .V = v,
- .opc = ld_opc,
- .imm12 = sc,
- .Rn = rn,
- .Rt = rt});
+ return aa64_ldst_uimm_pack((AA64LdStUimm){
+ .size = size, .V = v, .opc = ld_opc, .imm12 = sc, .Rn = rn, .Rt = rt});
}
static u32 aa_ldr_uimm_v(u32 size, u32 v, u32 rt, u32 rn, u32 byte_off) {
@@ -874,32 +873,32 @@ static void aa_emit_mem(AANativeTarget* a, int load, NativeLoc reg,
* opc=10) so it fills the whole register in one instruction — the cg layer
* then drops the redundant CV_SEXT. Every other load (incl. fp, word, dword)
* keeps the plain zero-extending LDR. */
- ld_opc = (load && (mem.flags & MF_SEXT_LOAD) && !native_loc_is_fp(reg) &&
- sz <= 1u)
- ? AA64_LDST_OPC_LDRS_X
- : AA64_LDST_OPC_LDR;
+ ld_opc =
+ (load && (mem.flags & MF_SEXT_LOAD) && !native_loc_is_fp(reg) && sz <= 1u)
+ ? AA64_LDST_OPC_LDRS_X
+ : AA64_LDST_OPC_LDR;
/* Far fixed-slot fast path (Lever 1/3 / Fix B). A plain frame slot whose
* top-record fp offset is past stur's ±256 range would otherwise cost `sub
* xN,x29,#off (+movk) ; ldur` (2-4 insns). Instead emit a positive scaled
- * `ldr/str [sp,#0]` placeholder and defer the offset: once the frame is final,
- * aa_apply_patches rewrites it to `[base, #frame_size-...]`, base = sp (stable)
- * or AA_FRAME_BASE (alloca).
+ * `ldr/str [sp,#0]` placeholder and defer the offset: once the frame is
+ * final, aa_apply_patches rewrites it to `[base, #frame_size-...]`, base = sp
+ * (stable) or AA_FRAME_BASE (alloca).
*
* A 4/8-byte slot (sz>=2) always fits the scaled imm12 (reach 16 KB/32 KB >=
- * any frame), so it is a one-word patch. A 1/2-byte slot (sz<2, Lever 3) has a
- * smaller scaled reach (4 KB/8 KB) that the largest frames (sqlite VdbeExec
+ * any frame), so it is a one-word patch. A 1/2-byte slot (sz<2, Lever 3) has
+ * a smaller scaled reach (4 KB/8 KB) that the largest frames (sqlite VdbeExec
* ~6.5 KB) exceed, so it reserves TWO words: when the resolved offset fits it
* is `ldrb/strb [base,#scaled] ; nop`, and when it overflows the resolver
* emits the address-build fallback `add x17,base,#hi ; ldrb/strb [x17,#lo]`
* (correct for any frame). byte/half are always integer (vbit=0; FP was
* widened to sz>=2 above).
*
- * Lever 3/4 interaction: a far SIGNED narrow load (ld_opc==LDRS_X) is excluded
- * from this scaled-slot path — the slot patch records only a plain LDR opcode,
- * so it would zero-extend while the cg layer dropped the CV_SEXT. Such loads
- * fall through to the general path below, which honors ld_opc (ldrsb/ldrsh with
- * an address build). Rare (a far signed char/short slot); correctness over the
- * one-insn win. */
+ * Lever 3/4 interaction: a far SIGNED narrow load (ld_opc==LDRS_X) is
+ * excluded from this scaled-slot path — the slot patch records only a plain
+ * LDR opcode, so it would zero-extend while the cg layer dropped the CV_SEXT.
+ * Such loads fall through to the general path below, which honors ld_opc
+ * (ldrsb/ldrsh with an address build). Rare (a far signed char/short slot);
+ * correctness over the one-insn win. */
int sext_far = load && ld_opc == AA64_LDST_OPC_LDRS_X;
if (a->slot_sp_base && addr.base_kind == NATIVE_ADDR_BASE_FRAME &&
addr.index_kind == NATIVE_ADDR_INDEX_NONE && sz <= 3u && !sext_far) {
@@ -918,7 +917,8 @@ static void aa_emit_mem(AANativeTarget* a, int load, NativeLoc reg,
p->u.slot.rt = (u8)rt;
aa_emit32(mc, load ? aa_ldr_uimm_v(sz, vbit, rt, AA_SP, 0)
: aa_str_uimm_v(sz, vbit, rt, AA_SP, 0));
- /* Reserve the fallback's second word for byte/half (filled at resolve). */
+ /* Reserve the fallback's second word for byte/half (filled at resolve).
+ */
if (sz < 2u) aa_emit32(mc, 0xd503201fu /* nop */);
return;
}
@@ -931,27 +931,28 @@ static void aa_emit_mem(AANativeTarget* a, int load, NativeLoc reg,
if (aa_use_got_for_sym(&a->base, addr.base.global.sym)) {
aa_emit32(mc, aa64_adrp(scratch, 0, 0));
mc_emit_reloc_at(mc, mc->section_id, pos, R_AARCH64_ADR_GOT_PAGE,
- addr.base.global.sym, 0, 0, 0);
+ addr.base.global.sym, 0, 0, 0);
pos = mc_pos(mc);
aa_emit32(mc, aa_ldr_uimm(3, scratch, scratch, 0));
mc_emit_reloc_at(mc, mc->section_id, pos, R_AARCH64_LD64_GOT_LO12_NC,
- addr.base.global.sym, 0, 0, 0);
+ addr.base.global.sym, 0, 0, 0);
if (addend) aa_emit_add_i64(a, scratch, scratch, addend);
- aa_emit32(mc, load ? aa_ldur_op_v(sz, native_loc_is_fp(reg), ld_opc, rt,
- scratch, 0)
- : aa_stur_v(sz, native_loc_is_fp(reg), rt, scratch, 0));
+ aa_emit32(
+ mc,
+ load ? aa_ldur_op_v(sz, native_loc_is_fp(reg), ld_opc, rt, scratch, 0)
+ : aa_stur_v(sz, native_loc_is_fp(reg), rt, scratch, 0));
return;
}
aa_emit32(mc, aa64_adrp(scratch, 0, 0));
mc_emit_reloc_at(mc, mc->section_id, pos, R_AARCH64_ADR_PREL_PG_HI21,
- addr.base.global.sym, addend, 0, 0);
+ addr.base.global.sym, addend, 0, 0);
pos = mc_pos(mc);
- aa_emit32(mc, load ? aa_ldr_uimm_op_v(sz, native_loc_is_fp(reg), ld_opc, rt,
- scratch, 0)
- : aa_str_uimm_v(sz, native_loc_is_fp(reg), rt, scratch,
- 0));
+ aa_emit32(mc,
+ load ? aa_ldr_uimm_op_v(sz, native_loc_is_fp(reg), ld_opc, rt,
+ scratch, 0)
+ : aa_str_uimm_v(sz, native_loc_is_fp(reg), rt, scratch, 0));
mc_emit_reloc_at(mc, mc->section_id, pos, aa_ldst_reloc_for_size(sz),
- addr.base.global.sym, addend, 0, 0);
+ addr.base.global.sym, addend, 0, 0);
return;
}
aa_addr_base(a, addr, &base, &off);
@@ -990,9 +991,9 @@ static void aa_emit_mem(AANativeTarget* a, int load, NativeLoc reg,
return;
}
aa_emit_add_imm(a, AA_TMP1, base, off);
- aa_emit32(mc, load ? aa_ldur_op_v(sz, native_loc_is_fp(reg), ld_opc, rt,
- AA_TMP1, 0)
- : aa_stur_v(sz, native_loc_is_fp(reg), rt, AA_TMP1, 0));
+ aa_emit32(
+ mc, load ? aa_ldur_op_v(sz, native_loc_is_fp(reg), ld_opc, rt, AA_TMP1, 0)
+ : aa_stur_v(sz, native_loc_is_fp(reg), rt, AA_TMP1, 0));
}
static NativeAllocClass aa_class_for_type(NativeTarget* t, KitCgTypeId type) {
@@ -1105,8 +1106,8 @@ static u32 aa_tmp_avoiding(u32 reg) {
return reg == AA_TMP0 ? AA_TMP1 : AA_TMP0;
}
-static void aa_load_addr_from_base(AANativeTarget* a, u32 rd, u32 base,
- i64 off, const NativeAddr* addr) {
+static void aa_load_addr_from_base(AANativeTarget* a, u32 rd, u32 base, i64 off,
+ const NativeAddr* addr) {
if (addr->index_kind == NATIVE_ADDR_INDEX_NONE) {
if (rd != base || off) aa_emit_add_i64(a, rd, base, off);
return;
@@ -1324,9 +1325,9 @@ static void aa_func_begin(NativeTarget* t, const CGFuncDesc* fd) {
* the pair and fp land at exactly the addresses every fp-relative offset
* already assumes — the frame is byte-identical to the old single big-`sub`
* prologue, only the instruction sequence changes. */
- aa_emit32(mc, aa64_stp64_pre(AA_FP, AA_LR, AA_SP,
- -(i32)((AA_FRAME_SAVE_SIZE + a->top_home_bytes) /
- 8u)));
+ aa_emit32(mc, aa64_stp64_pre(
+ AA_FP, AA_LR, AA_SP,
+ -(i32)((AA_FRAME_SAVE_SIZE + a->top_home_bytes) / 8u)));
aa_emit32(mc, aa64_add_imm(1, AA_FP, AA_SP, 0, 0)); /* mov x29, sp */
/* Arm the far-slot positive-scaled fast path for the common top-record
* layout. A Windows GP home area (top_home_bytes) places incoming args/home
@@ -1590,10 +1591,10 @@ static void aa_words_restore_frame(AANativeTarget* a, u32* words, u32 cap,
if (aa_cfa_off(a) == AA_FRAME_SAVE_SIZE) {
/* Common top-record case (CFA == 16: no Windows-variadic GP home area). The
* saved pair sits at [fp] and the caller's sp is fp+16, so `mov sp,x29`
- * followed by a post-indexed `ldp x29,x30,[sp],#16` restores the pair and sp
- * in two insns with no scratch — vs the fat path's three (`mov x16,fp; ldp
- * [x16]; add sp,x16,#16`). Correct under alloca: `mov sp,x29` resets sp from
- * the fp anchor before the post-index pops the pair. */
+ * followed by a post-indexed `ldp x29,x30,[sp],#16` restores the pair and
+ * sp in two insns with no scratch — vs the fat path's three (`mov x16,fp;
+ * ldp [x16]; add sp,x16,#16`). Correct under alloca: `mov sp,x29` resets sp
+ * from the fp anchor before the post-index pops the pair. */
if (*n + 2u > cap) aa_panic(a, "instruction patch too small");
words[(*n)++] = aa64_add_imm(1, AA_SP, AA_FP, 0, 0);
words[(*n)++] = aa64_ldp64_post(AA_FP, AA_LR, AA_SP, 2);
@@ -1826,23 +1827,26 @@ static void aa_apply_patches(AANativeTarget* a, const AAFrameLayout* L) {
i32 base_off = (i32)aa_sp_off_saved_pair(L) + fp_off;
u32 base = a->uses_frame_base ? AA_FRAME_BASE : AA_SP;
u32 ubo = (u32)base_off;
- /* Natural alignment is guaranteed by the slot allocator (and the byte case
- * is unconditionally aligned); a misalignment would mean a layout bug. */
+ /* Natural alignment is guaranteed by the slot allocator (and the byte
+ * case is unconditionally aligned); a misalignment would mean a layout
+ * bug. */
if (base_off < 0 || (ubo & ((1u << sl->sz) - 1u)) != 0u)
aa_panic(a, "far slot offset negative or misaligned");
if ((ubo >> sl->sz) <= 0xfffu) {
/* Scaled immediate fits: one-word ldr/str. For byte/half the reserved
* second placeholder word stays the nop emitted at body time. */
aa_patch32(a->base.obj, sec, p->pos,
- sl->load ? aa_ldr_uimm_v(sl->sz, sl->vbit, sl->rt, base, ubo)
- : aa_str_uimm_v(sl->sz, sl->vbit, sl->rt, base, ubo));
+ sl->load
+ ? aa_ldr_uimm_v(sl->sz, sl->vbit, sl->rt, base, ubo)
+ : aa_str_uimm_v(sl->sz, sl->vbit, sl->rt, base, ubo));
} else if (sl->sz < 2u) {
- /* Lever 3 overflow fallback. byte/half scaled reach (4 KB/8 KB) is below
- * the largest frames, so build the address into the reserved second word
- * instead: `add x17,base,#(off & ~0xfff) ; ldrb/strb rt,[x17,#(off &
- * 0xfff)]`. The high part is a multiple of 4096 (fits the shift-12
- * addsub imm for any frame < 16 MB); the low part is <4096 and, for a
- * half, even (base_off is 2-aligned), so both scale-divide cleanly. */
+ /* Lever 3 overflow fallback. byte/half scaled reach (4 KB/8 KB) is
+ * below the largest frames, so build the address into the reserved
+ * second word instead: `add x17,base,#(off & ~0xfff) ; ldrb/strb
+ * rt,[x17,#(off & 0xfff)]`. The high part is a multiple of 4096 (fits
+ * the shift-12 addsub imm for any frame < 16 MB); the low part is <4096
+ * and, for a half, even (base_off is 2-aligned), so both scale-divide
+ * cleanly. */
u32 hi = ubo & ~0xfffu;
u32 lo = ubo & 0xfffu;
if ((hi >> 12) > 0xfffu)
@@ -2121,9 +2125,8 @@ static void aa_cmp_branch(NativeTarget* t, CmpOp op, NativeLoc lhs,
lhs.kind == NATIVE_LOC_REG && rhs.kind == NATIVE_LOC_IMM &&
rhs.v.imm == 0) {
u32 sf = loc_is_64(t, lhs) ? 1u : 0u;
- aa_emit32(t->mc,
- op == CMP_EQ ? aa64_cbz(sf, loc_reg(lhs), 0)
- : aa64_cbnz_imm(sf, loc_reg(lhs), 0));
+ aa_emit32(t->mc, op == CMP_EQ ? aa64_cbz(sf, loc_reg(lhs), 0)
+ : aa64_cbnz_imm(sf, loc_reg(lhs), 0));
mc_emit_label_ref(t->mc, label, R_AARCH64_CONDBR19, 4, 0);
return;
}
@@ -2169,11 +2172,11 @@ static void aa_load_label_addr(NativeTarget* t, NativeLoc dst, MCLabel target) {
u32 pos = mc_pos(mc);
aa_emit32(mc, aa64_adrp(rd, 0, 0));
mc_emit_reloc_at(mc, mc->section_id, pos, R_AARCH64_ADR_PREL_PG_HI21, sym, 0,
- 0, 0);
+ 0, 0);
pos = mc_pos(mc);
aa_emit32(mc, aa64_add_imm(1, rd, rd, 0, 0));
mc_emit_reloc_at(mc, mc->section_id, pos, R_AARCH64_ADD_ABS_LO12_NC, sym, 0,
- 0, 0);
+ 0, 0);
}
static void aa_move(NativeTarget* t, NativeLoc dst, NativeLoc src) {
@@ -2252,8 +2255,8 @@ static void aa_load_addr(NativeTarget* t, NativeLoc dst, NativeAddr addr) {
mem.type = load.base_type;
mem.size = 8;
mem.align = 8;
- aa_emit_mem(a, 1, native_loc_reg(dst.type, NATIVE_REG_INT, base_reg), load,
- mem);
+ aa_emit_mem(a, 1, native_loc_reg(dst.type, NATIVE_REG_INT, base_reg),
+ load, mem);
aa_load_addr_from_base(a, rd, base_reg, addr.offset, &addr);
return;
}
@@ -2266,27 +2269,25 @@ static void aa_load_addr(NativeTarget* t, NativeLoc dst, NativeAddr addr) {
u32 pos = mc_pos(t->mc);
if (aa_use_got_for_sym(t, addr.base.global.sym)) {
aa_emit32(t->mc, aa64_adrp(base_reg, 0, 0));
- mc_emit_reloc_at(t->mc, t->mc->section_id, pos,
- R_AARCH64_ADR_GOT_PAGE, addr.base.global.sym, 0, 0,
- 0);
+ mc_emit_reloc_at(t->mc, t->mc->section_id, pos, R_AARCH64_ADR_GOT_PAGE,
+ addr.base.global.sym, 0, 0, 0);
pos = mc_pos(t->mc);
aa_emit32(t->mc, aa_ldr_uimm(3, base_reg, base_reg, 0));
mc_emit_reloc_at(t->mc, t->mc->section_id, pos,
- R_AARCH64_LD64_GOT_LO12_NC, addr.base.global.sym,
- 0, 0, 0);
+ R_AARCH64_LD64_GOT_LO12_NC, addr.base.global.sym, 0, 0,
+ 0);
if (addend) aa_emit_add_i64(a, base_reg, base_reg, addend);
aa_load_addr_from_base(a, rd, base_reg, 0, &addr);
return;
}
aa_emit32(t->mc, aa64_adrp(base_reg, 0, 0));
mc_emit_reloc_at(t->mc, t->mc->section_id, pos,
- R_AARCH64_ADR_PREL_PG_HI21, addr.base.global.sym,
- addend, 0, 0);
+ R_AARCH64_ADR_PREL_PG_HI21, addr.base.global.sym, addend,
+ 0, 0);
pos = mc_pos(t->mc);
aa_emit32(t->mc, aa64_add_imm(1, base_reg, base_reg, 0, 0));
- mc_emit_reloc_at(t->mc, t->mc->section_id, pos,
- R_AARCH64_ADD_ABS_LO12_NC, addr.base.global.sym,
- addend, 0, 0);
+ mc_emit_reloc_at(t->mc, t->mc->section_id, pos, R_AARCH64_ADD_ABS_LO12_NC,
+ addr.base.global.sym, addend, 0, 0);
aa_load_addr_from_base(a, rd, base_reg, 0, &addr);
return;
}
@@ -2346,12 +2347,12 @@ static void aa_tls_addr_of_win(NativeTarget* t, NativeLoc dst, ObjSymId sym,
pos = mc_pos(mc);
aa_emit32(mc, aa64_add_imm(1, rd, rd, 0, 1));
mc_emit_reloc_at(mc, sec, pos, R_COFF_AARCH64_SECREL_HIGH12A, sym, addend, 1,
- 0);
+ 0);
/* (7) rd += :secrel_lo12:sym (ADD with sh=0). */
pos = mc_pos(mc);
aa_emit32(mc, aa64_add_imm(1, rd, rd, 0, 0));
mc_emit_reloc_at(mc, sec, pos, R_COFF_AARCH64_SECREL_LOW12A, sym, addend, 1,
- 0);
+ 0);
}
static void aa_tls_addr_of(NativeTarget* t, NativeLoc dst, ObjSymId sym,
@@ -2364,11 +2365,11 @@ static void aa_tls_addr_of(NativeTarget* t, NativeLoc dst, ObjSymId sym,
aa_emit32(mc, aa64_adrp(0, 0, 0));
pos = mc_pos(mc) - 4u;
mc_emit_reloc_at(mc, mc->section_id, pos, R_AARCH64_TLVP_LOAD_PAGE21, sym,
- 0, 0, 0);
+ 0, 0, 0);
aa_emit32(mc, aa_ldr_uimm(3, 0, 0, 0));
pos = mc_pos(mc) - 4u;
mc_emit_reloc_at(mc, mc->section_id, pos, R_AARCH64_TLVP_LOAD_PAGEOFF12,
- sym, 0, 0, 0);
+ sym, 0, 0, 0);
aa_emit32(mc, aa_ldr_uimm(3, AA_TMP0, 0, 0));
aa_emit32(mc, aa64_blr(AA_TMP0));
if (addend) aa_emit_add_i64(a, 0, 0, addend);
@@ -2385,12 +2386,12 @@ static void aa_tls_addr_of(NativeTarget* t, NativeLoc dst, ObjSymId sym,
aa_emit32(mc, aa_mrs_tpidr_el0(rd));
pos = mc_pos(mc);
aa_emit32(mc, aa64_add_imm(1, rd, rd, 0, 1));
- mc_emit_reloc_at(mc, mc->section_id, pos, R_AARCH64_TLSLE_ADD_TPREL_HI12,
- sym, addend, 0, 0);
+ mc_emit_reloc_at(mc, mc->section_id, pos, R_AARCH64_TLSLE_ADD_TPREL_HI12, sym,
+ addend, 0, 0);
pos = mc_pos(mc);
aa_emit32(mc, aa64_add_imm(1, rd, rd, 0, 0));
mc_emit_reloc_at(mc, mc->section_id, pos, R_AARCH64_TLSLE_ADD_TPREL_LO12_NC,
- sym, addend, 0, 0);
+ sym, addend, 0, 0);
}
static NativeLoc aa_tmp_loc(KitCgTypeId type, Reg reg) {
@@ -2690,8 +2691,8 @@ static void aa_unop(NativeTarget* t, UnOp op, NativeLoc dst, NativeLoc src) {
static void aa_emit_cmp_to_flags(NativeTarget* t, NativeLoc lhs,
NativeLoc rhs) {
if (native_loc_is_fp(lhs)) {
- aa_emit32(t->mc, aa_fcmp(loc_size32(t, lhs) == 8u, loc_reg(lhs),
- loc_reg(rhs)));
+ aa_emit32(t->mc,
+ aa_fcmp(loc_size32(t, lhs) == 8u, loc_reg(lhs), loc_reg(rhs)));
return;
}
{
@@ -2855,10 +2856,11 @@ static void aa_alloca(NativeTarget* t, NativeLoc dst, NativeLoc size,
/* First alloca arms the frame-base anchor. The far-slot fast path can no
* longer key off sp (this `sub sp` floats it), so deferred AA_PATCH_SLOTs —
* those already emitted and any still to come — resolve against
- * AA_FRAME_BASE instead. Reserve a static-frame home for the caller's value;
- * the prologue saves it and sets AA_FRAME_BASE = frame base (before the body
- * runs, so every slot access sees a valid anchor), the epilogue restores
- * it. Only needed when slot_sp_base armed the fast path in the first place. */
+ * AA_FRAME_BASE instead. Reserve a static-frame home for the caller's
+ * value; the prologue saves it and sets AA_FRAME_BASE = frame base (before
+ * the body runs, so every slot access sees a valid anchor), the epilogue
+ * restores it. Only needed when slot_sp_base armed the fast path in the
+ * first place. */
if (a->slot_sp_base && !a->uses_frame_base) {
NativeFrameSlotDesc sd;
memset(&sd, 0, sizeof sd);
@@ -3200,7 +3202,8 @@ static void aa_plan_call(NativeTarget* t, const NativeCallDesc* desc,
? abi->ret.nparts
: ((!abi && desc->nresults) ? 1u : 0u);
memset(plan, 0, sizeof *plan);
- rets = nrets_cap ? arena_zarray(t->c->tu, NativeCallPlanRet, nrets_cap) : NULL;
+ rets =
+ nrets_cap ? arena_zarray(t->c->tu, NativeCallPlanRet, nrets_cap) : NULL;
plan->callee = desc->callee;
plan->rets = rets;
plan->flags = desc->flags;
@@ -3255,8 +3258,8 @@ static void aa_plan_call(NativeTarget* t, const NativeCallDesc* desc,
const ABIArgPart* part = &ai->parts[0];
NativeAllocClass cls =
part->cls == ABI_CLASS_FP ? NATIVE_REG_FP : NATIVE_REG_INT;
- NativeLoc tmpreg = native_loc_reg(desc->args[i].type, cls,
- cls == NATIVE_REG_FP ? 16u : AA_TMP0);
+ NativeLoc tmpreg = native_loc_reg(
+ desc->args[i].type, cls, cls == NATIVE_REG_FP ? 16u : AA_TMP0);
aa_load_part(t, tmpreg, desc->args[i], part->src_offset, part->size);
aa_store_outgoing_part(t, tail_call, stack, tmpreg, n);
stack += n;
@@ -3393,7 +3396,7 @@ static void aa_emit_tail_site(NativeTarget* t, NativeLoc callee) {
u32 pos = mc_pos(t->mc);
aa_emit32(t->mc, aa64_b(0));
mc_emit_reloc_at(t->mc, t->mc->section_id, pos, R_AARCH64_JUMP26,
- callee.v.global.sym, callee.v.global.addend, 0, 0);
+ callee.v.global.sym, callee.v.global.addend, 0, 0);
} else {
aa_panic(a, "unsupported tail target");
}
@@ -3408,8 +3411,8 @@ static void aa_emit_tail_site(NativeTarget* t, NativeLoc callee) {
for (u32 i = 0; i < AA_TAIL_WORDS; ++i) aa_emit32(t->mc, 0xd503201fu);
if (callee.kind == NATIVE_LOC_GLOBAL) {
mc_emit_reloc_at(t->mc, t->mc->section_id,
- p->pos + (AA_TAIL_WORDS - 1u) * 4u, R_AARCH64_JUMP26,
- callee.v.global.sym, callee.v.global.addend, 0, 0);
+ p->pos + (AA_TAIL_WORDS - 1u) * 4u, R_AARCH64_JUMP26,
+ callee.v.global.sym, callee.v.global.addend, 0, 0);
}
}
@@ -3425,8 +3428,8 @@ static void aa_emit_call(NativeTarget* t, const NativeCallPlan* plan) {
if (plan->callee.kind == NATIVE_LOC_GLOBAL) {
aa_emit32(t->mc, aa64_bl(0));
mc_emit_reloc_at(t->mc, t->mc->section_id, mc_pos(t->mc) - 4u,
- R_AARCH64_CALL26, plan->callee.v.global.sym,
- plan->callee.v.global.addend, 0, 0);
+ R_AARCH64_CALL26, plan->callee.v.global.sym,
+ plan->callee.v.global.addend, 0, 0);
return;
}
if (plan->callee.kind == NATIVE_LOC_REG) {
@@ -4126,10 +4129,9 @@ static const Reg aa_int_scratch[] = {9u, 10u, 11u};
* arg-shuffle fp cycle scratch) and v20/v21 are the NDT per-op fp scratch, so
* all three stay out of the pool. Callee-saved v8..v15 (AAPCS64) are appended
* like the int set; the caller-saved-only NDT never selects them. */
-static const Reg aa_fp_allocable[] = {0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u,
- 18u, 19u, 22u, 23u, 24u, 25u, 26u, 27u,
- 28u, 29u, 30u, 31u, 8u, 9u, 10u, 11u,
- 12u, 13u, 14u, 15u};
+static const Reg aa_fp_allocable[] = {
+ 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 18u, 19u, 22u, 23u, 24u, 25u,
+ 26u, 27u, 28u, 29u, 30u, 31u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, 15u};
static const Reg aa_fp_scratch[] = {20u, 21u};
#define AA_PHYS_INT_ALLOC(r) \
diff --git a/test/asm/roundtrip/mem_large_alloca_frame.c b/test/asm/roundtrip/mem_large_alloca_frame.c
@@ -0,0 +1,13 @@
+/* Regression: aarch64 -O0 single-pass prologue reservation must cover a large
+ * fixed frame plus the alloca frame-base anchor setup. The 5000-byte local
+ * makes the deferred stack subtraction require the multi-insn form; alloca then
+ * adds the x28 save/setup in the same reserved prologue region. */
+int test_main(void) {
+ volatile char fixed[5000];
+ volatile int n = 32;
+ char* p = (char*)__builtin_alloca((unsigned)n);
+ fixed[0] = 17;
+ fixed[4999] = 19;
+ p[0] = 6;
+ return fixed[0] + fixed[4999] + p[0];
+}
diff --git a/test/asm/roundtrip/mem_large_alloca_frame.expected b/test/asm/roundtrip/mem_large_alloca_frame.expected
@@ -0,0 +1 @@
+42
diff --git a/test/asm/roundtrip/mem_large_alloca_frame.targets b/test/asm/roundtrip/mem_large_alloca_frame.targets
@@ -0,0 +1 @@
+aa64
diff --git a/test/parse/cases/builtin_32_alloca_large_frame.c b/test/parse/cases/builtin_32_alloca_large_frame.c
@@ -0,0 +1,11 @@
+/* Large fixed frame plus dynamic alloca. This catches native prologue/frame
+ * placeholder sizing bugs across aa64, x64, rv64, and rv32. */
+int test_main(void) {
+ volatile char fixed[5000];
+ volatile int n = 32;
+ char* p = (char*)__builtin_alloca((unsigned)n);
+ fixed[0] = 17;
+ fixed[4999] = 19;
+ p[0] = 6;
+ return fixed[0] + fixed[4999] + p[0];
+}
diff --git a/test/parse/cases/builtin_32_alloca_large_frame.expected b/test/parse/cases/builtin_32_alloca_large_frame.expected
@@ -0,0 +1 @@
+42