kit

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

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:
Msrc/arch/arm32/native.c | 54++++++++++++++++++++++++++++++++++++++++++++++--------
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) {