kit

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

arch.c (22912B)


      1 /* arch_impl_arm32: the ARM32 (ARMv7-M Thumb-2, arm-none-eabi) ArchImpl.
      2  *
      3  * The backend is built on the shared NativeDirectTarget/MCEmitter substrate;
      4  * arch.c wires the factories, register metadata, CFI defaults, predefined
      5  * macros, target features, and the intra-function Thumb-2 branch label-fixup
      6  * patcher. The -march/-mcpu/-mfpu/-mfloat-abi axes resolve here: -march/-mcpu
      7  * set the dsp feature (ARMv7-M baseline vs ARMv7E-M), -mfpu sets vfp/fpv5, and
      8  * feature_predefines emits the matching __ARM_* macros. Soft-float v1 (the
      9  * driver still rejects -mfloat-abi=hard/softfp). See doc/plan/ARM32.md. */
     10 #include "arch/arch.h"
     11 
     12 #include <string.h>
     13 
     14 #include "arch/arm32/arm32.h"
     15 #include "arch/arm32/regs.h"
     16 #include "arch/arm32/variant.h"
     17 #include "cg/native_direct_target.h"
     18 #include "core/bytes.h"
     19 #include "core/core.h"
     20 #include "core/strbuf.h"
     21 #include "link/link_arch.h"
     22 #include "obj/obj.h"
     23 
     24 extern const LinkArchDesc link_arch_arm32;
     25 extern const ArchDbgOps arm32_dbg_ops;
     26 extern const ArchDwarfOps arm32_dwarf_ops;
     27 extern const ArchDecodeOps arm32_decode_ops;
     28 extern const ArchAsmOps arm32_asm_ops;
     29 extern ArchAsm* arm32_arch_asm_new(Compiler*);
     30 extern ArchDisasm* arm32_disasm_new(Compiler*);
     31 
     32 static int arm32_register_at_public(uint32_t idx, KitArchReg* out) {
     33   return arch_register_at_public(idx, out, arm32_register_iter_get);
     34 }
     35 
     36 static SrcLoc arm32_no_loc(void) {
     37   SrcLoc l = {0, 0, 0};
     38   return l;
     39 }
     40 
     41 /* Read/write a 32-bit Thumb-2 instruction stored as two little-endian
     42  * half-words (high half-word first): the returned value has hw1 in [31:16],
     43  * hw2 in [15:0]. */
     44 static u32 arm_rd_t32(const u8* p) {
     45   return ((u32)rd_u16_le(p) << 16) | (u32)rd_u16_le(p + 2);
     46 }
     47 static void arm_wr_t32(u8* p, u32 instr) {
     48   wr_u16_le(p, (u16)(instr >> 16));
     49   wr_u16_le(p + 2, (u16)(instr & 0xffffu));
     50 }
     51 
     52 /* Patch an intra-function Thumb-2 branch recorded by the MCEmitter label-fixup
     53  * path: B.W (T4, R_ARM_THM_JUMP24, +-16MiB) and B<cond>.W (T3,
     54  * R_ARM_THM_JUMP19, +-1MiB). fx->disp is the site-relative byte displacement
     55  * (target - branch_start); the Thumb pipeline reads PC as branch_start + 4, so
     56  * the encoded offset is fx->disp - 4. The opcode/cond bits the emitter wrote
     57  * are preserved; only the split immediate fields are filled in. */
     58 static int arm32_apply_label_fixup(Compiler* c, const ArchLabelFixup* fx) {
     59   const Section* s;
     60   u8 cur[4];
     61   u32 instr;
     62   i64 d;
     63 
     64   if (!fx) return 1;
     65   if (fx->width != 4) return 1;
     66   s = obj_section_get(fx->obj, fx->sec_id);
     67   if (!s) return 0;
     68   buf_read(&s->bytes, fx->offset, cur, 4);
     69   instr = arm_rd_t32(cur);
     70   d = (i64)fx->disp - 4; /* Thumb PC bias */
     71 
     72   switch (fx->kind) {
     73     case R_ARM_THM_JUMP24: {
     74       u32 sbit, i1, i2, j1, j2, imm10, imm11;
     75       if (d < -(i64)(1 << 24) || d >= (i64)(1 << 24))
     76         compiler_panic(c, arm32_no_loc(), "arm32: B.W out of range (+-16MiB)");
     77       sbit = (u32)((d >> 24) & 1u);
     78       i1 = (u32)((d >> 23) & 1u);
     79       i2 = (u32)((d >> 22) & 1u);
     80       imm10 = (u32)((d >> 12) & 0x3ffu);
     81       imm11 = (u32)((d >> 1) & 0x7ffu);
     82       j1 = (~(i1 ^ sbit)) & 1u;
     83       j2 = (~(i2 ^ sbit)) & 1u;
     84       instr &= 0xf800d000u; /* keep opcode (11110), hw2[15:14]=10, hw2[12]=1 */
     85       instr |= (sbit << 26) | (imm10 << 16);
     86       instr |= (j1 << 13) | (j2 << 11) | imm11;
     87       break;
     88     }
     89     case R_ARM_THM_JUMP19: {
     90       /* B<cond>.W T3: imm32 = SignExtend(S:J2:J1:imm6:imm11:0), no XOR-with-S. */
     91       u32 sbit, j1, j2, imm6, imm11;
     92       if (d < -(i64)(1 << 20) || d >= (i64)(1 << 20))
     93         compiler_panic(c, arm32_no_loc(), "arm32: B.cond.W out of range (+-1MiB)");
     94       sbit = (u32)((d >> 20) & 1u);
     95       j2 = (u32)((d >> 19) & 1u);
     96       j1 = (u32)((d >> 18) & 1u);
     97       imm6 = (u32)((d >> 12) & 0x3fu);
     98       imm11 = (u32)((d >> 1) & 0x7ffu);
     99       /* clear S(26), imm6(21:16), J1(13), J2(11), imm11(10:0); keep opcode,
    100        * cond(25:22), hw2[15:14]=10, hw2[12]=0. */
    101       instr &= ~((1u << 26) | (0x3fu << 16) | (1u << 13) | (1u << 11) | 0x7ffu);
    102       instr |= (sbit << 26) | (imm6 << 16) | (j1 << 13) | (j2 << 11) | imm11;
    103       break;
    104     }
    105     default:
    106       return 1;
    107   }
    108 
    109   arm_wr_t32(cur, instr);
    110   obj_patch(fx->obj, fx->sec_id, fx->offset, cur, 4);
    111   return 0;
    112 }
    113 
    114 /* The always-on base of the arm32 predefined-macro set: everything that does
    115  * NOT vary with the -march/-mcpu DSP extension or the -mfpu/-mfloat-abi float
    116  * configuration. Validated against
    117  * `clang --target=arm-none-eabi -march=armv7-m -mfloat-abi=soft -E -dM` (and the
    118  * armv7e-m / hard-float variants — see arm32_feature_predefines). The ILP32
    119  * size/type macros are emitted by the preprocessor from target.ptr_size=4.
    120  *
    121  * The feature-keyed macros — the arch revision (__ARM_ARCH_7M__ vs
    122  * __ARM_ARCH_7EM__), the DSP set, and the float set (__ARM_FP / __SOFTFP__ /
    123  * __ARM_PCS_VFP / ...) — are NOT in this table; they are selected by
    124  * arm32_feature_predefines from the resolved feature words + spec->float_abi. */
    125 static const KitPredefinedMacro arm32_predefined_macros[] = {
    126     {KIT_SLICE_LIT("__arm__"), KIT_SLICE_LIT("1")},
    127     {KIT_SLICE_LIT("__ARMEL__"), KIT_SLICE_LIT("1")},
    128     {KIT_SLICE_LIT("__THUMBEL__"), KIT_SLICE_LIT("1")},
    129     {KIT_SLICE_LIT("__thumb__"), KIT_SLICE_LIT("1")},
    130     {KIT_SLICE_LIT("__thumb2__"), KIT_SLICE_LIT("1")},
    131     {KIT_SLICE_LIT("__THUMB_INTERWORK__"), KIT_SLICE_LIT("1")},
    132     /* arm-none-eabi is an ELF target with no symbol-name prefix; gcc/clang
    133      * define both. Without them, hand-written runtime asm that branches on the
    134      * object format (compiler-rt's assembly.h) falls through to the COFF path
    135      * (`.def/.scl/.endef`, a leading `_`). __USER_LABEL_PREFIX__ is empty. */
    136     {KIT_SLICE_LIT("__ELF__"), KIT_SLICE_LIT("1")},
    137     {KIT_SLICE_LIT("__USER_LABEL_PREFIX__"), KIT_SLICE_LIT("")},
    138     {KIT_SLICE_LIT("__ARM_32BIT_STATE"), KIT_SLICE_LIT("1")},
    139     {KIT_SLICE_LIT("__ARM_ACLE"), KIT_SLICE_LIT("200")},
    140     {KIT_SLICE_LIT("__ARM_ARCH"), KIT_SLICE_LIT("7")},
    141     {KIT_SLICE_LIT("__ARM_ARCH_PROFILE"), KIT_SLICE_LIT("'M'")},
    142     {KIT_SLICE_LIT("__ARM_ARCH_ISA_THUMB"), KIT_SLICE_LIT("2")},
    143     {KIT_SLICE_LIT("__ARM_ARCH_ISA_ARM"), KIT_SLICE_LIT("0")},
    144     {KIT_SLICE_LIT("__ARM_EABI__"), KIT_SLICE_LIT("1")},
    145     {KIT_SLICE_LIT("__ARM_PCS"), KIT_SLICE_LIT("1")},
    146     {KIT_SLICE_LIT("__ARM_FEATURE_CLZ"), KIT_SLICE_LIT("1")},
    147     {KIT_SLICE_LIT("__ARM_FEATURE_QBIT"), KIT_SLICE_LIT("1")},
    148     {KIT_SLICE_LIT("__ARM_FEATURE_SAT"), KIT_SLICE_LIT("1")},
    149     {KIT_SLICE_LIT("__ARM_FEATURE_UNALIGNED"), KIT_SLICE_LIT("1")},
    150     {KIT_SLICE_LIT("__ARM_FEATURE_LDREX"), KIT_SLICE_LIT("0x7")},
    151     {KIT_SLICE_LIT("__ARM_FEATURE_COPROC"), KIT_SLICE_LIT("0xf")},
    152     {KIT_SLICE_LIT("__ARM_FEATURE_IDIV"), KIT_SLICE_LIT("1")},
    153     {KIT_SLICE_LIT("__ARM_ARCH_EXT_IDIV__"), KIT_SLICE_LIT("1")},
    154     {KIT_SLICE_LIT("__ARM_FP16_FORMAT_IEEE"), KIT_SLICE_LIT("1")},
    155     {KIT_SLICE_LIT("__ARM_FP16_ARGS"), KIT_SLICE_LIT("1")},
    156     {KIT_SLICE_LIT("__ARM_SIZEOF_WCHAR_T"), KIT_SLICE_LIT("4")},
    157     {KIT_SLICE_LIT("__ARM_SIZEOF_MINIMAL_ENUM"), KIT_SLICE_LIT("4")},
    158     /* __VFP_FP__ denotes the VFP *number format* (IEEE-754, little-endian word
    159      * order), not FPU presence; clang/gcc define it even on soft-float. */
    160     {KIT_SLICE_LIT("__VFP_FP__"), KIT_SLICE_LIT("1")},
    161     {KIT_SLICE_LIT("__ORDER_LITTLE_ENDIAN__"), KIT_SLICE_LIT("1234")},
    162     {KIT_SLICE_LIT("__ORDER_BIG_ENDIAN__"), KIT_SLICE_LIT("4321")},
    163     {KIT_SLICE_LIT("__BYTE_ORDER__"), KIT_SLICE_LIT("__ORDER_LITTLE_ENDIAN__")},
    164     {KIT_SLICE_LIT("__LITTLE_ENDIAN__"), KIT_SLICE_LIT("1")},
    165 };
    166 
    167 /* Feature-word indices — MUST match arm32_target_features[] order below. */
    168 enum {
    169   ARM32_FEAT_DSP = 0,  /* ARMv7E-M saturating + packed-SIMD (the DSP extension) */
    170   ARM32_FEAT_VFP = 1,  /* VFP present (FPv4-SP, Cortex-M4F) */
    171   ARM32_FEAT_FPV5 = 2, /* FPv5 (Cortex-M7) — implies VFP */
    172 };
    173 
    174 static const ArchTargetFeature arm32_target_features[] = {
    175     {"dsp"},
    176     {"vfp"},
    177     {"fpv5"},
    178 };
    179 
    180 static void arm32_feature_set(u64* words, u32 nwords, u32 idx) {
    181   if (!words || idx / 64u >= nwords) return;
    182   words[idx / 64u] |= 1ull << (idx % 64u);
    183 }
    184 
    185 static void arm32_feature_clear(u64* words, u32 nwords, u32 idx) {
    186   if (!words || idx / 64u >= nwords) return;
    187   words[idx / 64u] &= ~(1ull << (idx % 64u));
    188 }
    189 
    190 static int arm32_feature_get(const u64* words, u32 nwords, u32 idx) {
    191   if (!words || idx / 64u >= nwords) return 0;
    192   return (words[idx / 64u] & (1ull << (idx % 64u))) != 0;
    193 }
    194 
    195 static void arm32_target_feature_defaults(const Target* t, u64* words,
    196                                           u32 nwords) {
    197   (void)t;
    198   (void)words;
    199   (void)nwords; /* baseline ARMv7-M (Cortex-M3) soft: no features default-on. */
    200 }
    201 
    202 /* -march= profile -> the DSP feature bit. armv7-m / thumbv7m is the ARMv7-M
    203  * baseline (no DSP); armv7e-m / thumbv7em is ARMv7E-M (DSP). The FP features
    204  * come from -mfpu= (an explicit feature), not -march=. */
    205 static KitStatus arm32_target_feature_apply_isa(const Target* t, KitSlice isa,
    206                                                 u64* words, u32 nwords) {
    207   (void)t;
    208   if (kit_slice_eq_cstr(isa, "armv7-m") || kit_slice_eq_cstr(isa, "thumbv7m")) {
    209     arm32_feature_clear(words, nwords, ARM32_FEAT_DSP);
    210     return KIT_OK;
    211   }
    212   if (kit_slice_eq_cstr(isa, "armv7e-m") ||
    213       kit_slice_eq_cstr(isa, "thumbv7em")) {
    214     arm32_feature_set(words, nwords, ARM32_FEAT_DSP);
    215     return KIT_OK;
    216   }
    217   return KIT_UNSUPPORTED;
    218 }
    219 
    220 /* -mcpu= core -> ISA profile (the DSP bit). cortex-m3 is ARMv7-M (no DSP);
    221  * cortex-m4/m4f/m7/m7f are ARMv7E-M (DSP). The trailing 'f' selects an FPU
    222  * (hard-float, a follow-on) — it does not change the integer/DSP profile, and
    223  * the FP features still arrive via -mfpu=, so cortex-m4 and cortex-m4f resolve
    224  * the same DSP bit here. */
    225 static KitStatus arm32_target_feature_apply_cpu(const Target* t, KitSlice cpu,
    226                                                 u64* words, u32 nwords) {
    227   (void)t;
    228   if (kit_slice_eq_cstr(cpu, "cortex-m3")) {
    229     arm32_feature_clear(words, nwords, ARM32_FEAT_DSP);
    230     return KIT_OK;
    231   }
    232   if (kit_slice_eq_cstr(cpu, "cortex-m4") ||
    233       kit_slice_eq_cstr(cpu, "cortex-m4f") ||
    234       kit_slice_eq_cstr(cpu, "cortex-m7") ||
    235       kit_slice_eq_cstr(cpu, "cortex-m7f")) {
    236     arm32_feature_set(words, nwords, ARM32_FEAT_DSP);
    237     return KIT_OK;
    238   }
    239   return KIT_UNSUPPORTED;
    240 }
    241 
    242 /* Resolve & validate spec->float_abi for arm-none-eabi.
    243  *   (empty) / "soft"     -> SOFT  (no FPU; float/double via AEABI helpers).
    244  *   "softfp" / "hard"    -> SINGLE (FPv4-SP / FPv5 single-precision hard-float),
    245  *                           requiring a VFP/FPV5 feature (else KIT_INVALID).
    246  * SOFT gates the arch-neutral soft-float lowering in src/cg/arith.c
    247  * (api_target_has_no_hw_double). The driver still rejects -mfloat-abi=hard/softfp
    248  * (hard-float codegen is a separate work item), so the non-SOFT branches are
    249  * written and unit-testable at the kit_target_new API level but unreachable via
    250  * the CLI today. `double` stays soft on FPv4-SP (the FPV5 axis would extend
    251  * this), mirroring rv32 ilp32f. */
    252 static KitStatus arm32_resolve_float_abi(const ArchImpl* impl,
    253                                          KitTargetSpec* spec,
    254                                          const u64* feature_words,
    255                                          u32 nfeature_words, KitSlice abi,
    256                                          char* err, size_t errcap) {
    257   StrBuf sb;
    258   int has_vfp;
    259   strbuf_init(&sb, err, errcap);
    260   has_vfp = arm32_feature_get(feature_words, nfeature_words, ARM32_FEAT_VFP) ||
    261             arm32_feature_get(feature_words, nfeature_words, ARM32_FEAT_FPV5);
    262 
    263   if (!abi.s || abi.len == 0 || kit_slice_eq_cstr(abi, "soft")) {
    264     if (spec) spec->float_abi = (uint8_t)KIT_FLOAT_ABI_SOFT;
    265     return KIT_OK;
    266   }
    267   if (kit_slice_eq_cstr(abi, "softfp") || kit_slice_eq_cstr(abi, "hard")) {
    268     if (!has_vfp) {
    269       strbuf_puts(&sb, "hardware float ABI (");
    270       strbuf_put_slice(&sb, abi);
    271       strbuf_puts(&sb, ") requires an FPU (-mfpu=fpv4-sp-d16 / fpv5-d16) for ");
    272       strbuf_puts(&sb, impl->name);
    273       return KIT_INVALID;
    274     }
    275     if (spec) spec->float_abi = (uint8_t)KIT_FLOAT_ABI_SINGLE;
    276     return KIT_OK;
    277   }
    278   strbuf_puts(&sb, "unsupported float ABI for ");
    279   strbuf_puts(&sb, impl->name);
    280   strbuf_puts(&sb, ": ");
    281   strbuf_put_slice(&sb, abi);
    282   return KIT_INVALID;
    283 }
    284 
    285 /* ---- Feature-keyed predefined macros -------------------------------------
    286  * Each discrete {arch-rev} x {float-config} combination is a fully precomputed
    287  * `static const` table; the hook selects exactly one and returns it (no mutable
    288  * state — same rodata-table contract as arm32_predefined_macros). Validated
    289  * against host clang (see arm32_predefined_macros).
    290  *
    291  * Arch revision is mutually exclusive on the DSP bit: no-DSP -> __ARM_ARCH_7M__;
    292  * DSP -> __ARM_ARCH_7EM__ + __ARM_FEATURE_DSP + __ARM_FEATURE_SIMD32.
    293  *
    294  * Float config keys on float_abi + (vfp | fpv5). SOFT defines only __SOFTFP__
    295  * and leaves __ARM_FP UNDEFINED (so `#ifdef __ARM_FP` selects the integer path —
    296  * e.g. the coro asm skips the d8..d15 VFP saves). A resolved hardware ABI
    297  * defines __ARM_FP + __ARM_FEATURE_FMA + __ARM_PCS_VFP + the legacy
    298  * __ARM_VFPV2/3/4__ aliases; FPV5 (Cortex-M7) raises __ARM_FP to 0xe and adds
    299  * __ARM_FPV5__. */
    300 
    301 /* macros: __ARM_ARCH_7M__ */
    302 static const KitPredefinedMacro arm32_macros_7m_soft[] = {
    303     {KIT_SLICE_LIT("__ARM_ARCH_7M__"), KIT_SLICE_LIT("1")},
    304     {KIT_SLICE_LIT("__SOFTFP__"), KIT_SLICE_LIT("1")},
    305 };
    306 static const KitPredefinedMacro arm32_macros_7m_v4hard[] = {
    307     {KIT_SLICE_LIT("__ARM_ARCH_7M__"), KIT_SLICE_LIT("1")},
    308     {KIT_SLICE_LIT("__ARM_FP"), KIT_SLICE_LIT("0x6")},
    309     {KIT_SLICE_LIT("__ARM_FEATURE_FMA"), KIT_SLICE_LIT("1")},
    310     {KIT_SLICE_LIT("__ARM_VFPV2__"), KIT_SLICE_LIT("1")},
    311     {KIT_SLICE_LIT("__ARM_VFPV3__"), KIT_SLICE_LIT("1")},
    312     {KIT_SLICE_LIT("__ARM_VFPV4__"), KIT_SLICE_LIT("1")},
    313     {KIT_SLICE_LIT("__ARM_PCS_VFP"), KIT_SLICE_LIT("1")},
    314 };
    315 static const KitPredefinedMacro arm32_macros_7m_v5hard[] = {
    316     {KIT_SLICE_LIT("__ARM_ARCH_7M__"), KIT_SLICE_LIT("1")},
    317     {KIT_SLICE_LIT("__ARM_FP"), KIT_SLICE_LIT("0xe")},
    318     {KIT_SLICE_LIT("__ARM_FEATURE_FMA"), KIT_SLICE_LIT("1")},
    319     {KIT_SLICE_LIT("__ARM_FPV5__"), KIT_SLICE_LIT("1")},
    320     {KIT_SLICE_LIT("__ARM_VFPV2__"), KIT_SLICE_LIT("1")},
    321     {KIT_SLICE_LIT("__ARM_VFPV3__"), KIT_SLICE_LIT("1")},
    322     {KIT_SLICE_LIT("__ARM_VFPV4__"), KIT_SLICE_LIT("1")},
    323     {KIT_SLICE_LIT("__ARM_PCS_VFP"), KIT_SLICE_LIT("1")},
    324 };
    325 
    326 /* macros: __ARM_ARCH_7EM__ + DSP */
    327 static const KitPredefinedMacro arm32_macros_7em_soft[] = {
    328     {KIT_SLICE_LIT("__ARM_ARCH_7EM__"), KIT_SLICE_LIT("1")},
    329     {KIT_SLICE_LIT("__ARM_FEATURE_DSP"), KIT_SLICE_LIT("1")},
    330     {KIT_SLICE_LIT("__ARM_FEATURE_SIMD32"), KIT_SLICE_LIT("1")},
    331     {KIT_SLICE_LIT("__SOFTFP__"), KIT_SLICE_LIT("1")},
    332 };
    333 static const KitPredefinedMacro arm32_macros_7em_v4hard[] = {
    334     {KIT_SLICE_LIT("__ARM_ARCH_7EM__"), KIT_SLICE_LIT("1")},
    335     {KIT_SLICE_LIT("__ARM_FEATURE_DSP"), KIT_SLICE_LIT("1")},
    336     {KIT_SLICE_LIT("__ARM_FEATURE_SIMD32"), KIT_SLICE_LIT("1")},
    337     {KIT_SLICE_LIT("__ARM_FP"), KIT_SLICE_LIT("0x6")},
    338     {KIT_SLICE_LIT("__ARM_FEATURE_FMA"), KIT_SLICE_LIT("1")},
    339     {KIT_SLICE_LIT("__ARM_VFPV2__"), KIT_SLICE_LIT("1")},
    340     {KIT_SLICE_LIT("__ARM_VFPV3__"), KIT_SLICE_LIT("1")},
    341     {KIT_SLICE_LIT("__ARM_VFPV4__"), KIT_SLICE_LIT("1")},
    342     {KIT_SLICE_LIT("__ARM_PCS_VFP"), KIT_SLICE_LIT("1")},
    343 };
    344 static const KitPredefinedMacro arm32_macros_7em_v5hard[] = {
    345     {KIT_SLICE_LIT("__ARM_ARCH_7EM__"), KIT_SLICE_LIT("1")},
    346     {KIT_SLICE_LIT("__ARM_FEATURE_DSP"), KIT_SLICE_LIT("1")},
    347     {KIT_SLICE_LIT("__ARM_FEATURE_SIMD32"), KIT_SLICE_LIT("1")},
    348     {KIT_SLICE_LIT("__ARM_FP"), KIT_SLICE_LIT("0xe")},
    349     {KIT_SLICE_LIT("__ARM_FEATURE_FMA"), KIT_SLICE_LIT("1")},
    350     {KIT_SLICE_LIT("__ARM_FPV5__"), KIT_SLICE_LIT("1")},
    351     {KIT_SLICE_LIT("__ARM_VFPV2__"), KIT_SLICE_LIT("1")},
    352     {KIT_SLICE_LIT("__ARM_VFPV3__"), KIT_SLICE_LIT("1")},
    353     {KIT_SLICE_LIT("__ARM_VFPV4__"), KIT_SLICE_LIT("1")},
    354     {KIT_SLICE_LIT("__ARM_PCS_VFP"), KIT_SLICE_LIT("1")},
    355 };
    356 
    357 static u32 arm32_feature_predefines(const ArchImpl* impl,
    358                                     const KitTargetSpec* spec, const u64* words,
    359                                     u32 nwords, const KitPredefinedMacro** out) {
    360   int dsp = arm32_feature_get(words, nwords, ARM32_FEAT_DSP);
    361   int vfp = arm32_feature_get(words, nwords, ARM32_FEAT_VFP);
    362   int fpv5 = arm32_feature_get(words, nwords, ARM32_FEAT_FPV5);
    363   KitFloatAbi fa = spec ? (KitFloatAbi)spec->float_abi : KIT_FLOAT_ABI_SOFT;
    364   /* Hardware float = a resolved single/double ABI with a VFP feature present.
    365    * kit's KitFloatAbi has no distinct softfp encoding, so a resolved hardware
    366    * ABI follows the hard-float macro convention (__ARM_PCS_VFP). FPV5 raises the
    367    * profile. With no VFP feature or an unresolved/soft ABI, the float set is
    368    * soft (__SOFTFP__, __ARM_FP undefined). */
    369   int hardfp =
    370       (fa == KIT_FLOAT_ABI_SINGLE || fa == KIT_FLOAT_ABI_DOUBLE) && (vfp || fpv5);
    371   (void)impl;
    372 
    373   if (!dsp) {
    374     if (!hardfp) {
    375       *out = arm32_macros_7m_soft;
    376       return (u32)(sizeof arm32_macros_7m_soft / sizeof arm32_macros_7m_soft[0]);
    377     }
    378     if (fpv5) {
    379       *out = arm32_macros_7m_v5hard;
    380       return (u32)(sizeof arm32_macros_7m_v5hard /
    381                    sizeof arm32_macros_7m_v5hard[0]);
    382     }
    383     *out = arm32_macros_7m_v4hard;
    384     return (u32)(sizeof arm32_macros_7m_v4hard / sizeof arm32_macros_7m_v4hard[0]);
    385   }
    386   if (!hardfp) {
    387     *out = arm32_macros_7em_soft;
    388     return (u32)(sizeof arm32_macros_7em_soft / sizeof arm32_macros_7em_soft[0]);
    389   }
    390   if (fpv5) {
    391     *out = arm32_macros_7em_v5hard;
    392     return (u32)(sizeof arm32_macros_7em_v5hard /
    393                  sizeof arm32_macros_7em_v5hard[0]);
    394   }
    395   *out = arm32_macros_7em_v4hard;
    396   return (u32)(sizeof arm32_macros_7em_v4hard /
    397                sizeof arm32_macros_7em_v4hard[0]);
    398 }
    399 
    400 static int arm32_supports_call_conv(const Compiler* c, KitCgCallConv cc) {
    401   (void)c;
    402   return cc == KIT_CG_CC_TARGET_C;
    403 }
    404 
    405 /* Kept in lockstep with arm_intrinsic in native.c: returning 1 for an
    406  * intrinsic this backend does not lower panics at emit. No default: case so
    407  * -Wswitch enforces the twin-sync invariant. Lowered: TRAP, CLZ, CTZ (RBIT+CLZ),
    408  * BSWAP (REV/REV16; the 64-bit form is a __bswapdi2 libcall in cg), the no-op /
    409  * event hints (NOP/YIELD/WFI/WFE/SEV), and the barriers (DMB/DSB/ISB). POPCOUNT,
    410  * the overflow forms, FMA, syscall/setjmp, IRQ/cache ops are other tracks. */
    411 static int arm32_supports_intrinsic(const Compiler* c, KitCgIntrinsic intrin) {
    412   (void)c;
    413   switch (intrin) {
    414     case KIT_CG_INTRIN_TRAP:
    415     case KIT_CG_INTRIN_CLZ:
    416     case KIT_CG_INTRIN_CTZ:
    417     case KIT_CG_INTRIN_BSWAP:
    418     case KIT_CG_INTRIN_CPU_NOP:
    419     case KIT_CG_INTRIN_CPU_YIELD:
    420     case KIT_CG_INTRIN_ISB:
    421     case KIT_CG_INTRIN_DMB:
    422     case KIT_CG_INTRIN_DSB:
    423     case KIT_CG_INTRIN_WFI:
    424     case KIT_CG_INTRIN_WFE:
    425     case KIT_CG_INTRIN_SEV:
    426     case KIT_CG_INTRIN_SADD_OVERFLOW:
    427     case KIT_CG_INTRIN_UADD_OVERFLOW:
    428     case KIT_CG_INTRIN_SSUB_OVERFLOW:
    429     case KIT_CG_INTRIN_USUB_OVERFLOW:
    430     case KIT_CG_INTRIN_SMUL_OVERFLOW:
    431     case KIT_CG_INTRIN_UMUL_OVERFLOW:
    432     case KIT_CG_INTRIN_FRAME_ADDRESS:
    433     case KIT_CG_INTRIN_RETURN_ADDRESS:
    434     case KIT_CG_INTRIN_POPCOUNT:
    435     case KIT_CG_INTRIN_PREFETCH:
    436     case KIT_CG_INTRIN_EXPECT:
    437     case KIT_CG_INTRIN_ASSUME_ALIGNED:
    438     case KIT_CG_INTRIN_SMUL_HIGH:
    439     case KIT_CG_INTRIN_UMUL_HIGH:
    440       return 1;
    441     case KIT_CG_INTRIN_READCYCLECOUNTER:
    442     case KIT_CG_INTRIN_SYSCALL:
    443     case KIT_CG_INTRIN_SETJMP:
    444     case KIT_CG_INTRIN_LONGJMP:
    445     case KIT_CG_INTRIN_FMA:
    446     case KIT_CG_INTRIN_IRQ_SAVE:
    447     case KIT_CG_INTRIN_IRQ_RESTORE:
    448     case KIT_CG_INTRIN_IRQ_DISABLE:
    449     case KIT_CG_INTRIN_IRQ_ENABLE:
    450     case KIT_CG_INTRIN_DCACHE_CLEAN:
    451     case KIT_CG_INTRIN_DCACHE_INVALIDATE:
    452     case KIT_CG_INTRIN_DCACHE_CLEAN_INVALIDATE:
    453     case KIT_CG_INTRIN_ICACHE_INVALIDATE:
    454     case KIT_CG_INTRIN_CORO_SWITCH:
    455       return 0;
    456   }
    457   return 0;
    458 }
    459 
    460 static CgTarget* arm32_backend_make(Compiler* c, ObjBuilder* o,
    461                                     const KitCodeOptions* opts) {
    462   return native_direct_backend_make(c, o, opts, arm32_native_target_new,
    463                                     arm32_native_direct_ops());
    464 }
    465 
    466 static CgTarget* arm32_semantic_target_new(Compiler* c, ObjBuilder* o,
    467                                            MCEmitter* mc) {
    468   return native_direct_semantic_target_new(c, o, mc, arm32_native_target_new,
    469                                            arm32_native_direct_ops());
    470 }
    471 
    472 const ArchImpl arch_impl_arm32 = {
    473     .backend = {.name = "arm32", .make = arm32_backend_make},
    474     .kind = KIT_ARCH_ARM_32,
    475     .name = "arm32",
    476     .cgtarget_new = arm32_semantic_target_new,
    477     .asm_new = arm32_arch_asm_new,
    478     .disasm_new = arm32_disasm_new,
    479     .apply_label_fixup = arm32_apply_label_fixup,
    480     .decode = &arm32_decode_ops,
    481     /* .emu omitted (NULL) — no emu lifter for arm32 yet (like aa64/x64). */
    482     .link = &link_arch_arm32,
    483     .dwarf = &arm32_dwarf_ops,
    484     .dbg = &arm32_dbg_ops,
    485     .asm_ops = &arm32_asm_ops,
    486     .predefined_macros = arm32_predefined_macros,
    487     .npredefined_macros =
    488         (u32)(sizeof arm32_predefined_macros / sizeof arm32_predefined_macros[0]),
    489     .target_features = arm32_target_features,
    490     .ntarget_features =
    491         (u32)(sizeof arm32_target_features / sizeof arm32_target_features[0]),
    492     .target_feature_defaults = arm32_target_feature_defaults,
    493     .target_feature_apply_isa = arm32_target_feature_apply_isa,
    494     .target_feature_apply_cpu = arm32_target_feature_apply_cpu,
    495     .register_name = arm32_register_name,
    496     .register_index = arm32_register_index,
    497     .register_count = arm32_register_iter_size,
    498     .register_at = arm32_register_at_public,
    499     /* AAPCS32 / Thumb-2: LR = r14, SP = r13 (legacy AArch32 DWARF numbering).
    500      * 2-byte minimum insn width -> code_align 2; word stack stride ->
    501      * data_align -4. CFA = sp at entry. */
    502     .cfi_return_addr_reg = 14u,
    503     .cfi_code_align_factor = 2,
    504     .cfi_data_align_factor = -4,
    505     .cfi_cfa_init_reg = 13u,
    506     .cfi_cfa_init_offset = 0,
    507     .backend_features = KIT_CG_BACKEND_STRICT_ALIGNMENT,
    508     .atomic_lock_free_max = 4u, /* no LDREXD/STREXD on M-profile -> 8B spinlock */
    509     .supports_call_conv = arm32_supports_call_conv,
    510     .supports_intrinsic = arm32_supports_intrinsic,
    511     /* float_abi resolves to SOFT unless an FPU feature + a hardware ABI is set
    512      * (gates cg soft-float). The driver still rejects -mfloat-abi=hard/softfp. */
    513     .resolve_float_abi = arm32_resolve_float_abi,
    514     /* arm32 folds its float macros into feature_predefines (it has both the
    515      * feature words and spec->float_abi there), so float_predefines stays NULL.
    516      * feature_predefines selects __ARM_ARCH_7M__/7EM__ + DSP + the float set. */
    517     .feature_predefines = arm32_feature_predefines,
    518 };