kit

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

commit 3d2eb21daf7ec352bb0d5c59715d12beed45d2e0
parent 307256a262313dbba445b0e35eb0c331cc395ca3
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue, 16 Jun 2026 19:21:18 -0700

arm32 Phase 2: corpus-red cleanup — UO_NOT, frame-O1 reads_frame, popcount, hint intrinsics

- arm_unop: UO_NOT (logical not -> CMP+IT EQ materialize 0/1).
- func_begin_known_frame: the frameless-leaf (slim) tier must not fire when the
  body reads its own frame (__builtin_frame_address/return_address) — add
  !kf->reads_frame so r7 stays anchored (fixes frame_address at -O1).
- INTRIN_POPCOUNT: inline 32-bit SWAR (all masks ThumbExpandImm-encodable).
- INTRIN_EXPECT / ASSUME_ALIGNED (passthrough) + PREFETCH (no-op).

Diffstat:
Msrc/arch/arm32/arch.c | 2+-
Msrc/arch/arm32/native.c | 53+++++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 52 insertions(+), 3 deletions(-)

diff --git a/src/arch/arm32/arch.c b/src/arch/arm32/arch.c @@ -221,11 +221,11 @@ static int arm32_supports_intrinsic(const Compiler* c, KitCgIntrinsic intrin) { case KIT_CG_INTRIN_UMUL_OVERFLOW: case KIT_CG_INTRIN_FRAME_ADDRESS: case KIT_CG_INTRIN_RETURN_ADDRESS: - return 1; case KIT_CG_INTRIN_POPCOUNT: case KIT_CG_INTRIN_PREFETCH: case KIT_CG_INTRIN_EXPECT: case KIT_CG_INTRIN_ASSUME_ALIGNED: + return 1; case KIT_CG_INTRIN_READCYCLECOUNTER: case KIT_CG_INTRIN_SYSCALL: case KIT_CG_INTRIN_SETJMP: diff --git a/src/arch/arm32/native.c b/src/arch/arm32/native.c @@ -635,8 +635,20 @@ static void arm_unop(NativeTarget* t, UnOp op, NativeLoc dst, NativeLoc src) { case UO_BNOT: arm_emit_t32(mc, arm_mvn_reg(rd, rs)); return; + case UO_NOT: { + /* Logical not -> (rs == 0) materialized as 0/1: CMP rs,#0; MOV rd,#0; + * IT EQ; MOV<eq> rd,#1. CMP precedes the MOV (rd may alias rs). */ + u32 enc0, enc1; + thumb_expand_imm_encode(0u, &enc0); + thumb_expand_imm_encode(1u, &enc1); + arm_emit_t32(mc, arm_cmp_imm(rs, enc0)); + arm_emit_t32(mc, arm_mov_imm(rd, enc0)); + arm_emit_t16(mc, (u16)(0xbf08u | (ARM_CC_EQ << 4))); /* IT EQ */ + arm_emit_t32(mc, arm_mov_imm(rd, enc1)); + return; + } default: - arm_panic(a, "unop not lowered (FNEG/NOT are Phase 2)"); + arm_panic(a, "unop not lowered (FNEG is a soft-float follow-on)"); } } @@ -1012,7 +1024,7 @@ static void arm_func_begin_known_frame(NativeTarget* t, const CGFuncDesc* fd, * prologue at all and let arm_func_end emit a bare `BX lr`. Inline asm cannot * occur (asm_block is not installed), so no opaque-lr-clobber guard is needed. */ - a->slim_prologue = kf && kf->is_leaf && cs_mask == 0 && + a->slim_prologue = kf && kf->is_leaf && !kf->reads_frame && cs_mask == 0 && !a->frame.has_alloca && a->frame.cum_off == 0 && a->frame.max_outgoing == 0 && !a->has_sret && !a->is_variadic && @@ -2420,6 +2432,43 @@ static void arm_intrinsic(NativeTarget* t, IntrinKind kind, } } break; + case INTRIN_POPCOUNT: + /* 32-bit SWAR popcount (no hardware popcount on ARMv7-M). All masks are + * ThumbExpandImm-encodable, so no MOVW/MOVT is needed. lr/ip are the + * scratch. 64-bit popcount stays in the cg lane path / a follow-on. */ + if (ndst == 1u && narg == 1u && native_type_size(t, dsts[0].type) <= 4u) { + u32 rs = loc_reg(args[0]), rd = loc_reg(dsts[0]); + u32 e55, e33, e0f, e01; + thumb_expand_imm_encode(0x55555555u, &e55); + thumb_expand_imm_encode(0x33333333u, &e33); + thumb_expand_imm_encode(0x0f0f0f0fu, &e0f); + thumb_expand_imm_encode(0x01010101u, &e01); + arm_emit_t32(mc, arm_shift_imm(1u, ARM_SCRATCH, rs, 1u)); /* ip = x>>1 */ + arm_emit_t32(mc, arm_dp_imm(0u, 0u, ARM_SCRATCH, ARM_SCRATCH, e55)); /* ip &= 0x55.. */ + arm_emit_t32(mc, arm_sub_reg(ARM_TMP, rs, ARM_SCRATCH)); /* lr = x - ip */ + arm_emit_t32(mc, arm_dp_imm(0u, 0u, ARM_SCRATCH, ARM_TMP, e33)); /* ip = lr & 0x33.. */ + arm_emit_t32(mc, arm_shift_imm(1u, ARM_TMP, ARM_TMP, 2u)); /* lr >>= 2 */ + arm_emit_t32(mc, arm_dp_imm(0u, 0u, ARM_TMP, ARM_TMP, e33)); /* lr &= 0x33.. */ + arm_emit_t32(mc, arm_add_reg(ARM_TMP, ARM_TMP, ARM_SCRATCH)); /* lr += ip */ + arm_emit_t32(mc, arm_shift_imm(1u, ARM_SCRATCH, ARM_TMP, 4u)); /* ip = lr>>4 */ + arm_emit_t32(mc, arm_add_reg(ARM_TMP, ARM_TMP, ARM_SCRATCH)); /* lr += ip */ + arm_emit_t32(mc, arm_dp_imm(0u, 0u, ARM_TMP, ARM_TMP, e0f)); /* lr &= 0x0f.. */ + arm_emit_t32(mc, arm_mov_imm(ARM_SCRATCH, e01)); /* ip = 0x01010101 */ + arm_emit_t32(mc, arm_mul(ARM_TMP, ARM_TMP, ARM_SCRATCH)); /* lr *= ip */ + arm_emit_t32(mc, arm_shift_imm(1u, rd, ARM_TMP, 24u)); /* rd = lr>>24 */ + return; + } + break; + case INTRIN_EXPECT: + case INTRIN_ASSUME_ALIGNED: + /* Branch/alignment hints: the result is the first argument, unchanged. */ + if (ndst == 1u && narg >= 1u) { + arm_move(t, dsts[0], args[0]); + return; + } + break; + case INTRIN_PREFETCH: + return; /* no-op: M-profile has no PLD */ case INTRIN_CPU_NOP: arm_emit_t16(mc, arm_nop16()); return;