kit

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

commit beb0ad1e2b1f44d0c85a4de95c4481e7feb7e552
parent 599588c5956838f1644b0c511e60cd2d270091cd
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 17 Jun 2026 00:48:30 -0700

arm32 inline asm: bind immediate + memory operands (case 20)

20_cg_api_inline_asm_full exercises i/m/=&r/inout constraints the operand
binders did not cover:
- -O1 (arm_asm_block_native) panicked on any non-register input; bind an 'i'
  immediate as OPK_IMM and an 'm' memory operand by materializing its address
  (NATIVE_LOC_ADDR / frame home) into the operand scratch and binding bound_mem.
- -O0 (arm_direct_load_address_to_reg) routed an 'm' operand's address through
  arm_direct_addr, which yields a FRAME_VALUE base load_addr has no form for;
  use materialize_addr so an INDIRECT pointer-in-slot loads first.

Closes the last toy red: DEPTH=full arm32 corpus is 423/0/2 (the 2 skips are the
aarch64-only privileged-baremetal case). The empty templates this case uses are
served by the existing minimal runner; routing real mnemonics through the full
descriptor-driven asm.c remains a follow-on.

Diffstat:
Msrc/arch/arm32/native.c | 46++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 42 insertions(+), 4 deletions(-)

diff --git a/src/arch/arm32/native.c b/src/arch/arm32/native.c @@ -3105,7 +3105,10 @@ static void arm_direct_load_operand_to_reg(NativeDirectTarget* d, Operand op, } static void arm_direct_load_address_to_reg(NativeDirectTarget* d, Operand op, NativeLoc dst) { - d->native->load_addr(d->native, dst, arm_direct_addr(d, op)); + /* For an "m" memory operand: a LOCAL's address is its frame home; an INDIRECT + * (*(base+ofs)) names an address held in a frame slot, so materialize_addr + * loads the pointer first (load_addr has no FRAME_VALUE base form). */ + d->native->load_addr(d->native, dst, arm_direct_materialize_addr(d, op)); } static void arm_direct_store_reg_to_operand(NativeDirectTarget* d, Operand op, NativeLoc src) { @@ -3385,11 +3388,46 @@ static void arm_asm_block_native(NativeTarget* t, const char* tmpl, continue; } type = ins[i].type ? ins[i].type : in_locs[i].type; - if (in_locs[i].kind != NATIVE_LOC_REG) + if (in_locs[i].kind == NATIVE_LOC_REG) { + arm_asm_bound_reg(&bound_ins[i], type, (NativeAllocClass)in_locs[i].cls, + (Reg)in_locs[i].v.reg); + } else if (body[0] == 'i' && in_locs[i].kind == NATIVE_LOC_IMM) { + /* Immediate ("i"/"n") constraint: bind the literal value, no register. */ + memset(&bound_ins[i], 0, sizeof bound_ins[i]); + bound_ins[i].kind = OPK_IMM; + bound_ins[i].type = type; + bound_ins[i].v.imm = in_locs[i].v.imm; + } else if (body[0] == 'm') { + /* Memory ("m") constraint: bind a base register holding the operand's + * address. The optimizer hands a computed address (NATIVE_LOC_ADDR) or a + * frame/stack home; either way materialize the address into the operand + * scratch (ip) and bind it as the memory base. */ + NativeAddr ma; + memset(&ma, 0, sizeof ma); + if (in_locs[i].kind == NATIVE_LOC_ADDR) { + ma = in_locs[i].v.addr; + } else if (in_locs[i].kind == NATIVE_LOC_FRAME || + in_locs[i].kind == NATIVE_LOC_STACK) { + ma.base_kind = NATIVE_ADDR_BASE_FRAME; + ma.base.frame = in_locs[i].kind == NATIVE_LOC_FRAME + ? in_locs[i].v.frame + : in_locs[i].v.stack.slot; + ma.offset = in_locs[i].kind == NATIVE_LOC_STACK + ? in_locs[i].v.stack.offset + : 0; + ma.base_type = type; + } else { + arm_asm_panic_at(c, arm_of(t)->loc, "unsupported memory asm input"); + } + arm_load_addr(t, + native_loc_reg(builtin_id(KIT_CG_BUILTIN_I32), + NATIVE_REG_INT, ARM_SCRATCH), + ma); + arm_asm_bound_mem(&bound_ins[i], type, (Reg)ARM_SCRATCH); + } else { arm_asm_panic_at(c, arm_of(t)->loc, "optimizer asm input not in a register"); - arm_asm_bound_reg(&bound_ins[i], type, (NativeAllocClass)in_locs[i].cls, - (Reg)in_locs[i].v.reg); + } } arm_asm_run_template(t, tmpl, bound_outs, nout, bound_ins, nin); }