kit

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

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:
Msrc/arch/aa64/native.c | 220++++++++++++++++++++++++++++++++++++++++---------------------------------------
Atest/asm/roundtrip/mem_large_alloca_frame.c | 13+++++++++++++
Atest/asm/roundtrip/mem_large_alloca_frame.expected | 1+
Atest/asm/roundtrip/mem_large_alloca_frame.targets | 1+
Atest/parse/cases/builtin_32_alloca_large_frame.c | 11+++++++++++
Atest/parse/cases/builtin_32_alloca_large_frame.expected | 1+
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