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