commit b05d4004256585236bb5b1aee44b177aea3cdb32
parent 017aba99c7025892aa12a320937438d387161da8
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 16 Jun 2026 17:17:03 -0700
arm32 Phase 2: aggregate copy_bytes + set_bytes
Diffstat:
1 file changed, 46 insertions(+), 8 deletions(-)
diff --git a/src/arch/arm32/native.c b/src/arch/arm32/native.c
@@ -1087,19 +1087,57 @@ static void arm_tls_addr_of(NativeTarget* t, NativeLoc dst, ObjSymId sym,
(void)addend;
ARM_UNIMPL("tls_addr_of");
}
+/* copy_bytes / set_bytes: aggregate (struct/array, and the wide8 i64/double)
+ * memory ops. Both keep the materialized src/dst NativeAddr bases as-is and bake
+ * the running byte offset into the load/store immediate (arm_emit_mem dispatches
+ * the width and the T3/T4 offset encoding), mirroring the aa64 sibling backend.
+ *
+ * The only transfer scratch is ARM_TMP (lr): it is RESERVED (never holds an NDT
+ * operand or a materialized address base — base regs are r7/r0..r3/r4..r11/ip)
+ * and dead in the body, so a single copy needs no extra register. Because the
+ * offset is folded into the access, deep frame slots or large aggregates can
+ * push the effective offset past the encodable range; arm_emit_mem then panics
+ * cleanly (the -O0 frame floor of [r7,#-255] is a pre-existing Phase-1 limit,
+ * not widened here). */
static void arm_copy_bytes(NativeTarget* t, NativeAddr dst, NativeAddr src,
AggregateAccess acc) {
- (void)dst;
- (void)src;
- (void)acc;
- ARM_UNIMPL("copy_bytes (aggregate)");
+ Arm32NativeTarget* a = arm_of(t);
+ KitCgTypeId i32t = builtin_id(KIT_CG_BUILTIN_I32);
+ NativeLoc tmp = native_loc_reg(i32t, NATIVE_REG_INT, ARM_TMP);
+ u32 off = 0;
+ /* Widest convenient granule is a word (4); the trailing 1/2/3 bytes fall to
+ * halfword/byte ops. The width ladder also keeps each chunk naturally aligned
+ * for an aligned aggregate (4|4|...|2|1) and never over-runs acc.size. */
+ while (off < acc.size) {
+ u32 rem = acc.size - off;
+ u32 sz = rem >= 4u ? 4u : rem >= 2u ? 2u : 1u;
+ MemAccess mem = acc.mem;
+ NativeAddr s = src, d = dst;
+ mem.size = sz;
+ mem.align = sz;
+ s.offset += (i32)off;
+ d.offset += (i32)off;
+ arm_emit_mem(a, 1, tmp, s, mem); /* LDR/LDRH/LDRB tmp, [src+off] */
+ arm_emit_mem(a, 0, tmp, d, mem); /* STR/STRH/STRB tmp, [dst+off] */
+ off += sz;
+ }
}
+/* set_bytes: store the materialized fill byte `v` across acc.size bytes. The
+ * common case is struct/array zero-init (v == 0); any fill byte is correct. One
+ * STRB per byte — correctness-first; word-splat widening is deferred (it would
+ * need a second scratch for the 0x01010101 multiplier or a shifted-ORR encoder,
+ * and the dst base may itself live in ip). */
static void arm_set_bytes(NativeTarget* t, NativeAddr dst, NativeLoc v,
AggregateAccess acc) {
- (void)dst;
- (void)v;
- (void)acc;
- ARM_UNIMPL("set_bytes (memset)");
+ Arm32NativeTarget* a = arm_of(t);
+ MemAccess mem = acc.mem;
+ mem.size = 1u;
+ mem.align = 1u;
+ for (u32 off = 0; off < acc.size; ++off) {
+ NativeAddr d = dst;
+ d.offset += (i32)off;
+ arm_emit_mem(a, 0, v, d, mem); /* STRB v, [dst+off] */
+ }
}
static void arm_bitfield_load(NativeTarget* t, NativeLoc dst, NativeAddr addr,
BitFieldAccess bf) {