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:
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);
}