kit

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

arch.c (22165B)


      1 #include "arch/arch.h"
      2 
      3 #include <string.h>
      4 
      5 #include "arch/riscv/asm.h"
      6 #include "arch/riscv/disasm.h"
      7 #include "arch/riscv/regs.h"
      8 #include "arch/riscv/rv64.h"
      9 #include "arch/riscv/variant.h"
     10 #include "cg/native_direct_target.h"
     11 #include "core/bytes.h"
     12 #include "core/strbuf.h"
     13 #include "link/link_arch.h"
     14 #include "obj/obj.h"
     15 
     16 extern const LinkArchDesc link_arch_rv64;
     17 extern const ArchDbgOps rv64_dbg_ops;
     18 extern const ArchEmuOps rv64_emu_ops;
     19 extern const ArchDwarfOps rv64_dwarf_ops;
     20 extern const ArchAsmOps rv64_asm_ops;
     21 extern const LinkArchDesc link_arch_rv32;
     22 extern const ArchDbgOps rv32_dbg_ops;
     23 
     24 static int rv64_register_at_public(uint32_t idx, KitArchReg* out) {
     25   const char* nm = NULL;
     26   int rc;
     27   if (!out) return 1;
     28   rc = rv64_register_iter_get(idx, &out->dwarf_idx, &nm);
     29   if (rc == 0) out->name = kit_slice_cstr(nm);
     30   return rc;
     31 }
     32 
     33 static SrcLoc rv64_no_loc(void) {
     34   SrcLoc l = {0, 0, 0};
     35   return l;
     36 }
     37 
     38 static int rv64_apply_label_fixup(Compiler* c, const ArchLabelFixup* fx) {
     39   const Section* s;
     40   u8 cur[4];
     41   u32 word;
     42   u32 b;
     43 
     44   (void)c;
     45   if (!fx) return 1;
     46   s = obj_section_get(fx->obj, fx->sec_id);
     47   if (!s) return 0;
     48 
     49   /* INTRA_AUIPC_ADDI is a width=8 pair; other kinds patch a single 4-byte
     50    * instruction. Read the first word only for the 4-byte cases. */
     51   if (fx->kind != R_RV_INTRA_AUIPC_ADDI) {
     52     if (fx->width != 4) return 1;
     53     buf_read(&s->bytes, fx->offset, cur, 4);
     54     word = rd_u32_le(cur);
     55   } else {
     56     buf_read(&s->bytes, fx->offset, cur, 4);
     57     word = rd_u32_le(cur);
     58   }
     59   b = (u32)fx->disp;
     60 
     61   switch (fx->kind) {
     62     case R_RV_BRANCH:
     63       /* B-type reaches ±4 KiB. Conditional branches are emitted over a jal
     64        * (see rv_cmp_branch) so this only carries small fixed displacements;
     65        * a violation is a backend bug, not silently-truncated code. */
     66       if ((i64)fx->disp < -(i64)(1 << 12) || (i64)fx->disp >= (i64)(1 << 12))
     67         compiler_panic(c, rv64_no_loc(), "rv64: BRANCH out of range (±4KiB)");
     68       word &= 0x01fff07fu;
     69       word |= ((b >> 12) & 1u) << 31;
     70       word |= ((b >> 5) & 0x3fu) << 25;
     71       word |= ((b >> 1) & 0xfu) << 8;
     72       word |= ((b >> 11) & 1u) << 7;
     73       break;
     74     case R_RV_JAL:
     75       /* J-type reaches ±1 MiB — ample for intra-function jumps (including the
     76        * long leg of a conditional branch). Fail loudly rather than wrap. */
     77       if ((i64)fx->disp < -(i64)(1 << 20) || (i64)fx->disp >= (i64)(1 << 20))
     78         compiler_panic(c, rv64_no_loc(), "rv64: JAL out of range (±1MiB)");
     79       word &= 0x00000fffu;
     80       word |= ((b >> 20) & 1u) << 31;
     81       word |= ((b >> 1) & 0x3ffu) << 21;
     82       word |= ((b >> 11) & 1u) << 20;
     83       word |= ((b >> 12) & 0xffu) << 12;
     84       break;
     85     case R_RV_INTRA_AUIPC_ADDI: {
     86       /* width=8: patch both the AUIPC at fx->offset and the ADDI at
     87        * fx->offset+4. disp is the byte offset from the AUIPC PC to the
     88        * target label. */
     89       u8 cur2[4];
     90       u32 word2;
     91       i32 disp = (i32)fx->disp;
     92       /* hi20 is the top 20 bits of (disp + 0x800) so the sign-extended
     93        * 12-bit lo12 cancels out. */
     94       u32 hi20 = (u32)((disp + 0x800) >> 12) & 0xfffffu;
     95       u32 lo12 = (u32)disp & 0xfffu;
     96       if (fx->width != 8) return 1;
     97       /* AUIPC: keep rd (bits 11:7) and opcode (bits 6:0); patch imm[31:12]. */
     98       word = (word & 0x00000fffu) | (hi20 << 12);
     99       wr_u32_le(cur, word);
    100       obj_patch(fx->obj, fx->sec_id, fx->offset, cur, 4);
    101       buf_read(&s->bytes, fx->offset + 4, cur2, 4);
    102       word2 = rd_u32_le(cur2);
    103       /* ADDI: keep rs1/funct3/rd/opcode (bits 19:0); patch imm[11:0]. */
    104       word2 = (word2 & 0x000fffffu) | (lo12 << 20);
    105       wr_u32_le(cur2, word2);
    106       obj_patch(fx->obj, fx->sec_id, fx->offset + 4, cur2, 4);
    107       return 0;
    108     }
    109     default:
    110       return 1;
    111   }
    112 
    113   wr_u32_le(cur, word);
    114   obj_patch(fx->obj, fx->sec_id, fx->offset, cur, 4);
    115   return 0;
    116 }
    117 
    118 /* Mirrors `clang --target=riscv64-linux-gnu -E -dM` for the in-scope
    119  * RV64GC profile: I/M/F/D/A/C + Zicsr-minimal. Macros that depend on
    120  * extensions outside scope (V, B, Zve*, Zfh, …) are deliberately
    121  * absent. ABI variant is lp64d. */
    122 static const KitPredefinedMacro rv64_predefined_macros[] = {
    123     {KIT_SLICE_LIT("__riscv"), KIT_SLICE_LIT("1")},
    124     {KIT_SLICE_LIT("__riscv_xlen"), KIT_SLICE_LIT("64")},
    125     {KIT_SLICE_LIT("__riscv_float_abi_double"), KIT_SLICE_LIT("1")},
    126     {KIT_SLICE_LIT("__riscv_atomic"), KIT_SLICE_LIT("1")},
    127     {KIT_SLICE_LIT("__riscv_mul"), KIT_SLICE_LIT("1")},
    128     {KIT_SLICE_LIT("__riscv_div"), KIT_SLICE_LIT("1")},
    129     {KIT_SLICE_LIT("__riscv_muldiv"), KIT_SLICE_LIT("1")},
    130     {KIT_SLICE_LIT("__riscv_compressed"), KIT_SLICE_LIT("1")},
    131     {KIT_SLICE_LIT("__riscv_flen"), KIT_SLICE_LIT("64")},
    132     {KIT_SLICE_LIT("__riscv_fdiv"), KIT_SLICE_LIT("1")},
    133     {KIT_SLICE_LIT("__riscv_fsqrt"), KIT_SLICE_LIT("1")},
    134     {KIT_SLICE_LIT("__riscv_zicsr"), KIT_SLICE_LIT("1")},
    135     {KIT_SLICE_LIT("__riscv_zifencei"), KIT_SLICE_LIT("1")},
    136     {KIT_SLICE_LIT("__riscv_arch_test"), KIT_SLICE_LIT("1")},
    137     {KIT_SLICE_LIT("__LP64__"), KIT_SLICE_LIT("1")},
    138     {KIT_SLICE_LIT("_LP64"), KIT_SLICE_LIT("1")},
    139     {KIT_SLICE_LIT("__ORDER_LITTLE_ENDIAN__"), KIT_SLICE_LIT("1234")},
    140     {KIT_SLICE_LIT("__ORDER_BIG_ENDIAN__"), KIT_SLICE_LIT("4321")},
    141     {KIT_SLICE_LIT("__BYTE_ORDER__"), KIT_SLICE_LIT("__ORDER_LITTLE_ENDIAN__")},
    142     {KIT_SLICE_LIT("__LITTLE_ENDIAN__"), KIT_SLICE_LIT("1")},
    143 };
    144 
    145 /* Mirrors `clang --target=riscv32-linux-gnu -march=rv32imafc_zicsr_zifencei
    146  * -mabi=ilp32f -E -dM` for the DEFAULT ilp32f hard-single profile:
    147  * I/M/F/A/C + Zicsr-minimal, single-precision float ABI (double is soft).
    148  * __riscv_flen=32 (no D). __ILP32__/_ILP32 replace __LP64__/_LP64.
    149  *
    150  * Known v1 limitation: predefined macros are a static (ptr,count) table
    151  * consumed without a Target (src/api/compile.c), so this fixed table reflects
    152  * the default ilp32f profile only. Soft-float ilp32 codegen correctness is
    153  * driven by c->target.float_abi, not these macros. */
    154 static const KitPredefinedMacro rv32_predefined_macros[] = {
    155     {KIT_SLICE_LIT("__riscv"), KIT_SLICE_LIT("1")},
    156     {KIT_SLICE_LIT("__riscv_xlen"), KIT_SLICE_LIT("32")},
    157     {KIT_SLICE_LIT("__riscv_float_abi_single"), KIT_SLICE_LIT("1")},
    158     {KIT_SLICE_LIT("__riscv_flen"), KIT_SLICE_LIT("32")},
    159     {KIT_SLICE_LIT("__riscv_fdiv"), KIT_SLICE_LIT("1")},
    160     {KIT_SLICE_LIT("__riscv_fsqrt"), KIT_SLICE_LIT("1")},
    161     {KIT_SLICE_LIT("__riscv_atomic"), KIT_SLICE_LIT("1")},
    162     {KIT_SLICE_LIT("__riscv_mul"), KIT_SLICE_LIT("1")},
    163     {KIT_SLICE_LIT("__riscv_div"), KIT_SLICE_LIT("1")},
    164     {KIT_SLICE_LIT("__riscv_muldiv"), KIT_SLICE_LIT("1")},
    165     {KIT_SLICE_LIT("__riscv_compressed"), KIT_SLICE_LIT("1")},
    166     {KIT_SLICE_LIT("__riscv_zicsr"), KIT_SLICE_LIT("1")},
    167     {KIT_SLICE_LIT("__riscv_zifencei"), KIT_SLICE_LIT("1")},
    168     {KIT_SLICE_LIT("__riscv_arch_test"), KIT_SLICE_LIT("1")},
    169     {KIT_SLICE_LIT("__ILP32__"), KIT_SLICE_LIT("1")},
    170     {KIT_SLICE_LIT("_ILP32"), KIT_SLICE_LIT("1")},
    171     {KIT_SLICE_LIT("__ORDER_LITTLE_ENDIAN__"), KIT_SLICE_LIT("1234")},
    172     {KIT_SLICE_LIT("__ORDER_BIG_ENDIAN__"), KIT_SLICE_LIT("4321")},
    173     {KIT_SLICE_LIT("__BYTE_ORDER__"), KIT_SLICE_LIT("__ORDER_LITTLE_ENDIAN__")},
    174     {KIT_SLICE_LIT("__LITTLE_ENDIAN__"), KIT_SLICE_LIT("1")},
    175 };
    176 
    177 enum {
    178   RV64_FEAT_I = 0,
    179   RV64_FEAT_M,
    180   RV64_FEAT_A,
    181   RV64_FEAT_F,
    182   RV64_FEAT_D,
    183   RV64_FEAT_C,
    184   RV64_FEAT_ZICSR,
    185   RV64_FEAT_ZIFENCEI,
    186 };
    187 
    188 static const ArchTargetFeature rv64_target_features[] = {
    189     {"i"}, {"m"}, {"a"}, {"f"}, {"d"}, {"c"}, {"zicsr"}, {"zifencei"},
    190 };
    191 
    192 static void rv64_feature_set(u64* words, u32 nwords, u32 idx) {
    193   if (!words || idx / 64u >= nwords) return;
    194   words[idx / 64u] |= 1ull << (idx % 64u);
    195 }
    196 
    197 static void rv64_feature_clear(u64* words, u32 nwords, u32 idx) {
    198   if (!words || idx / 64u >= nwords) return;
    199   words[idx / 64u] &= ~(1ull << (idx % 64u));
    200 }
    201 
    202 static void rv64_feature_disable_all(u64* words, u32 nwords) {
    203   rv64_feature_clear(words, nwords, RV64_FEAT_I);
    204   rv64_feature_clear(words, nwords, RV64_FEAT_M);
    205   rv64_feature_clear(words, nwords, RV64_FEAT_A);
    206   rv64_feature_clear(words, nwords, RV64_FEAT_F);
    207   rv64_feature_clear(words, nwords, RV64_FEAT_D);
    208   rv64_feature_clear(words, nwords, RV64_FEAT_C);
    209   rv64_feature_clear(words, nwords, RV64_FEAT_ZICSR);
    210   rv64_feature_clear(words, nwords, RV64_FEAT_ZIFENCEI);
    211 }
    212 
    213 static void rv64_feature_enable_g(u64* words, u32 nwords) {
    214   rv64_feature_set(words, nwords, RV64_FEAT_I);
    215   rv64_feature_set(words, nwords, RV64_FEAT_M);
    216   rv64_feature_set(words, nwords, RV64_FEAT_A);
    217   rv64_feature_set(words, nwords, RV64_FEAT_F);
    218   rv64_feature_set(words, nwords, RV64_FEAT_D);
    219   rv64_feature_set(words, nwords, RV64_FEAT_ZICSR);
    220   rv64_feature_set(words, nwords, RV64_FEAT_ZIFENCEI);
    221 }
    222 
    223 static int rv64_has_prefix(const char* p, const char* end, const char* lit) {
    224   size_t n = strlen(lit);
    225   return (size_t)(end - p) >= n && memcmp(p, lit, n) == 0;
    226 }
    227 
    228 static void rv64_skip_version(const char** pp, const char* end) {
    229   const char* p = *pp;
    230   while (p < end && ((*p >= '0' && *p <= '9') || *p == 'p')) ++p;
    231   *pp = p;
    232 }
    233 
    234 static KitStatus rv64_target_feature_apply_isa(const Target* target,
    235                                                KitSlice isa, u64* words,
    236                                                u32 nwords) {
    237   const char* p;
    238   const char* end;
    239   const RiscvVariant* v = riscv_variant_for_kind(target->arch);
    240   if (isa.len < 5 || memcmp(isa.s, v->isa_prefix, 4) != 0)
    241     return KIT_UNSUPPORTED;
    242   p = isa.s + 4;
    243   end = isa.s + isa.len;
    244   rv64_feature_disable_all(words, nwords);
    245   while (p < end) {
    246     if (*p == '_') {
    247       ++p;
    248       continue;
    249     }
    250     switch (*p) {
    251       case 'i':
    252         rv64_feature_set(words, nwords, RV64_FEAT_I);
    253         ++p;
    254         rv64_skip_version(&p, end);
    255         continue;
    256       case 'm':
    257         rv64_feature_set(words, nwords, RV64_FEAT_M);
    258         ++p;
    259         rv64_skip_version(&p, end);
    260         continue;
    261       case 'a':
    262         rv64_feature_set(words, nwords, RV64_FEAT_A);
    263         ++p;
    264         rv64_skip_version(&p, end);
    265         continue;
    266       case 'f':
    267         rv64_feature_set(words, nwords, RV64_FEAT_F);
    268         ++p;
    269         rv64_skip_version(&p, end);
    270         continue;
    271       case 'd':
    272         rv64_feature_set(words, nwords, RV64_FEAT_D);
    273         ++p;
    274         rv64_skip_version(&p, end);
    275         continue;
    276       case 'c':
    277         rv64_feature_set(words, nwords, RV64_FEAT_C);
    278         ++p;
    279         rv64_skip_version(&p, end);
    280         continue;
    281       case 'g':
    282         rv64_feature_enable_g(words, nwords);
    283         ++p;
    284         rv64_skip_version(&p, end);
    285         continue;
    286       case 'z':
    287         if (rv64_has_prefix(p, end, "zicsr")) {
    288           rv64_feature_set(words, nwords, RV64_FEAT_ZICSR);
    289           p += 5;
    290           rv64_skip_version(&p, end);
    291           continue;
    292         }
    293         if (rv64_has_prefix(p, end, "zifencei")) {
    294           rv64_feature_set(words, nwords, RV64_FEAT_ZIFENCEI);
    295           p += 8;
    296           rv64_skip_version(&p, end);
    297           continue;
    298         }
    299         break;
    300     }
    301     return KIT_UNSUPPORTED;
    302   }
    303   return KIT_OK;
    304 }
    305 
    306 static void rv64_target_feature_defaults(const Target* target, u64* words,
    307                                          u32 nwords) {
    308   rv64_feature_set(words, nwords, RV64_FEAT_I);
    309   rv64_feature_set(words, nwords, RV64_FEAT_M);
    310   rv64_feature_set(words, nwords, RV64_FEAT_A);
    311   rv64_feature_set(words, nwords, RV64_FEAT_F);
    312   /* rv32 default profile is rv32imafc_zicsr_zifencei (ilp32f hard-single) —
    313    * no D. rv64 keeps the full G+C (lp64d) profile including D. */
    314   if (target->arch != KIT_ARCH_RV32)
    315     rv64_feature_set(words, nwords, RV64_FEAT_D);
    316   rv64_feature_set(words, nwords, RV64_FEAT_C);
    317   rv64_feature_set(words, nwords, RV64_FEAT_ZICSR);
    318   rv64_feature_set(words, nwords, RV64_FEAT_ZIFENCEI);
    319 }
    320 
    321 static CgTarget* rv64_backend_make(Compiler* c, ObjBuilder* o,
    322                                    const KitCodeOptions* opts) {
    323   MCEmitter* mc = NULL;
    324   Debug* debug = NULL;
    325   CgTarget* t;
    326   NativeTarget* native;
    327   NativeDirectTargetConfig cfg;
    328   if (cg_mc_debug_new(c, o, opts, &mc, &debug) != KIT_OK) return NULL;
    329   native = rv64_native_target_new(c, o, mc);
    330   if (!native) return NULL;
    331   memset(&cfg, 0, sizeof cfg);
    332   cfg.native = native;
    333   cfg.ops = rv64_native_direct_ops();
    334   t = native_direct_target_new(c, o, &cfg);
    335   if (t) t->debug = debug;
    336   return t;
    337 }
    338 
    339 static CgTarget* rv64_semantic_target_new(Compiler* c, ObjBuilder* o,
    340                                           MCEmitter* mc) {
    341   NativeTarget* native;
    342   NativeDirectTargetConfig cfg;
    343   if (!mc) mc = mc_new(c, o);
    344   native = rv64_native_target_new(c, o, mc);
    345   if (!native) return NULL;
    346   memset(&cfg, 0, sizeof cfg);
    347   cfg.native = native;
    348   cfg.ops = rv64_native_direct_ops();
    349   return native_direct_target_new(c, o, &cfg);
    350 }
    351 
    352 /* RISC-V emits only the target-C convention; it has no SysV/Win64/AAPCS/WASM
    353  * variant. Shared by rv64 and rv32 (one backend, one answer). */
    354 static int rv64_supports_call_conv(const Compiler* c, KitCgCallConv cc) {
    355   (void)c;
    356   switch (cc) {
    357     case KIT_CG_CC_TARGET_C:
    358       return 1;
    359     case KIT_CG_CC_SYSV:
    360     case KIT_CG_CC_WIN64:
    361     case KIT_CG_CC_AAPCS:
    362     case KIT_CG_CC_WASM:
    363     case KIT_CG_CC_INTERRUPT:
    364       return 0;
    365   }
    366   return 0;
    367 }
    368 
    369 /* Capability twin of rv_intrinsic (src/arch/riscv/native.c); keep the two in
    370  * sync. rv32 and rv64 share one backend, so they share this answer (the old
    371  * type.c matrix normalized rv32->rv64 for exactly this reason). No default
    372  * case, so a new KitCgIntrinsic trips -Wswitch here. */
    373 static int rv64_supports_intrinsic(const Compiler* c, KitCgIntrinsic intrin) {
    374   switch (intrin) {
    375     case KIT_CG_INTRIN_TRAP:
    376     case KIT_CG_INTRIN_CLZ:
    377     case KIT_CG_INTRIN_CTZ:
    378     case KIT_CG_INTRIN_POPCOUNT:
    379     case KIT_CG_INTRIN_BSWAP:
    380     case KIT_CG_INTRIN_SADD_OVERFLOW:
    381     case KIT_CG_INTRIN_UADD_OVERFLOW:
    382     case KIT_CG_INTRIN_SSUB_OVERFLOW:
    383     case KIT_CG_INTRIN_USUB_OVERFLOW:
    384     case KIT_CG_INTRIN_SMUL_OVERFLOW:
    385     case KIT_CG_INTRIN_UMUL_OVERFLOW:
    386     case KIT_CG_INTRIN_PREFETCH:
    387     case KIT_CG_INTRIN_EXPECT:
    388     case KIT_CG_INTRIN_ASSUME_ALIGNED:
    389     case KIT_CG_INTRIN_CPU_NOP:
    390     case KIT_CG_INTRIN_CPU_YIELD:
    391     case KIT_CG_INTRIN_ISB:
    392     case KIT_CG_INTRIN_DMB:
    393     case KIT_CG_INTRIN_DSB:
    394     case KIT_CG_INTRIN_WFI:
    395     case KIT_CG_INTRIN_FRAME_ADDRESS:
    396     case KIT_CG_INTRIN_RETURN_ADDRESS:
    397       return 1;
    398     case KIT_CG_INTRIN_SYSCALL:
    399       return c->target.os == KIT_OS_LINUX ||
    400              c->target.os == KIT_OS_FREESTANDING;
    401     case KIT_CG_INTRIN_SETJMP:
    402     case KIT_CG_INTRIN_LONGJMP:
    403     case KIT_CG_INTRIN_FMA:
    404     case KIT_CG_INTRIN_IRQ_SAVE:
    405     case KIT_CG_INTRIN_IRQ_RESTORE:
    406     case KIT_CG_INTRIN_IRQ_DISABLE:
    407     case KIT_CG_INTRIN_IRQ_ENABLE:
    408     case KIT_CG_INTRIN_WFE:
    409     case KIT_CG_INTRIN_SEV:
    410     case KIT_CG_INTRIN_DCACHE_CLEAN:
    411     case KIT_CG_INTRIN_DCACHE_INVALIDATE:
    412     case KIT_CG_INTRIN_DCACHE_CLEAN_INVALIDATE:
    413     case KIT_CG_INTRIN_ICACHE_INVALIDATE:
    414     case KIT_CG_INTRIN_CORO_SWITCH:
    415       return 0;
    416   }
    417   return 0;
    418 }
    419 
    420 static int rv64_feature_get(const u64* words, u32 nwords, u32 idx) {
    421   if (!words || idx / 64u >= nwords) return 0;
    422   return (words[idx / 64u] & (1ull << (idx % 64u))) != 0;
    423 }
    424 
    425 /* RISC-V float-ABI resolution + validation, factored out of src/api/core.c so
    426  * non-arch code resolves the ABI by capability (arch_resolve_float_abi) instead
    427  * of an `arch == RV32 || RV64` branch. Shared by rv32 and rv64 (one backend).
    428  * Mirrors the historical core.c logic exactly: an explicit -mabi (`abi`) picks
    429  * the ABI and must match the pointer width; otherwise the ABI is derived from
    430  * the resolved -march F/D bits. A hard single/double ABI requires the matching
    431  * extension. Writes spec->float_abi on success; on error returns KIT_INVALID
    432  * and fills `err`. */
    433 static KitStatus rv64_resolve_float_abi(const ArchImpl* impl,
    434                                         KitTargetSpec* spec,
    435                                         const u64* feature_words,
    436                                         u32 nfeature_words, KitSlice abi,
    437                                         char* err, size_t errcap) {
    438   u32 fidx, didx;
    439   int has_f;
    440   int has_d;
    441   KitFloatAbi fa;
    442   StrBuf sb;
    443   strbuf_init(&sb, err, errcap);
    444   has_f = arch_target_feature_index(impl, kit_slice_cstr("f"), &fidx) &&
    445           rv64_feature_get(feature_words, nfeature_words, fidx);
    446   has_d = arch_target_feature_index(impl, kit_slice_cstr("d"), &didx) &&
    447           rv64_feature_get(feature_words, nfeature_words, didx);
    448 
    449   if (abi.s && abi.len) {
    450     int is_ilp32 = 0;
    451     int is_lp64 = 0;
    452     if (kit_slice_eq_cstr(abi, "ilp32") || kit_slice_eq_cstr(abi, "ilp32f") ||
    453         kit_slice_eq_cstr(abi, "ilp32d")) {
    454       is_ilp32 = 1;
    455     } else if (kit_slice_eq_cstr(abi, "lp64") ||
    456                kit_slice_eq_cstr(abi, "lp64f") ||
    457                kit_slice_eq_cstr(abi, "lp64d")) {
    458       is_lp64 = 1;
    459     } else {
    460       strbuf_puts(&sb, "unsupported ABI for ");
    461       strbuf_puts(&sb, impl->name);
    462       strbuf_puts(&sb, ": ");
    463       strbuf_put_slice(&sb, abi);
    464       return KIT_INVALID;
    465     }
    466     /* Width prefix must match pointer size. */
    467     if ((is_ilp32 && spec->ptr_size != 4u) ||
    468         (is_lp64 && spec->ptr_size != 8u)) {
    469       strbuf_puts(&sb, "ABI ");
    470       strbuf_put_slice(&sb, abi);
    471       strbuf_puts(&sb, " does not match pointer width for ");
    472       strbuf_puts(&sb, impl->name);
    473       return KIT_INVALID;
    474     }
    475     if (kit_slice_eq_cstr(abi, "ilp32d") || kit_slice_eq_cstr(abi, "lp64d")) {
    476       fa = KIT_FLOAT_ABI_DOUBLE;
    477     } else if (kit_slice_eq_cstr(abi, "ilp32f") ||
    478                kit_slice_eq_cstr(abi, "lp64f")) {
    479       fa = KIT_FLOAT_ABI_SINGLE;
    480     } else {
    481       fa = KIT_FLOAT_ABI_SOFT;
    482     }
    483   } else {
    484     /* Derive from the resolved -march feature bits. */
    485     if (has_d)
    486       fa = KIT_FLOAT_ABI_DOUBLE;
    487     else if (has_f)
    488       fa = KIT_FLOAT_ABI_SINGLE;
    489     else
    490       fa = KIT_FLOAT_ABI_SOFT;
    491   }
    492 
    493   if (fa == KIT_FLOAT_ABI_SINGLE && !has_f) {
    494     strbuf_puts(&sb,
    495                 "hardware single-float ABI requires the 'f' extension for ");
    496     strbuf_puts(&sb, impl->name);
    497     return KIT_INVALID;
    498   }
    499   if (fa == KIT_FLOAT_ABI_DOUBLE && !has_d) {
    500     strbuf_puts(&sb,
    501                 "hardware double-float ABI requires the 'd' extension for ");
    502     strbuf_puts(&sb, impl->name);
    503     return KIT_INVALID;
    504   }
    505   spec->float_abi = (uint8_t)fa;
    506   return KIT_OK;
    507 }
    508 
    509 const ArchImpl arch_impl_rv64 = {
    510     .backend = {.name = "rv64", .make = rv64_backend_make},
    511     .kind = KIT_ARCH_RV64,
    512     .name = "rv64",
    513     .cgtarget_new = rv64_semantic_target_new,
    514     .asm_new = rv64_arch_asm_new,
    515     .disasm_new = rv64_disasm_new,
    516     .apply_label_fixup = rv64_apply_label_fixup,
    517     .decode = &rv64_decode_ops,
    518     .emu = &rv64_emu_ops,
    519     .link = &link_arch_rv64,
    520     .dwarf = &rv64_dwarf_ops,
    521     .dbg = &rv64_dbg_ops,
    522     .asm_ops = &rv64_asm_ops,
    523     .predefined_macros = rv64_predefined_macros,
    524     .npredefined_macros =
    525         (u32)(sizeof rv64_predefined_macros / sizeof rv64_predefined_macros[0]),
    526     .target_features = rv64_target_features,
    527     .ntarget_features =
    528         (u32)(sizeof rv64_target_features / sizeof rv64_target_features[0]),
    529     .target_feature_defaults = rv64_target_feature_defaults,
    530     .target_feature_apply_isa = rv64_target_feature_apply_isa,
    531     .register_name = rv64_register_name,
    532     .register_index = rv64_register_index,
    533     .register_count = rv64_register_iter_size,
    534     .register_at = rv64_register_at_public,
    535     /* RISC-V psABI: return address in x1 (ra). 4-byte aligned insns
    536      * (cover 2-byte C-ext too via code_align=2). Data align -8 for
    537      * doubleword stack stride. CFA = sp at entry. */
    538     .cfi_return_addr_reg = 1u,
    539     .cfi_code_align_factor = 2,
    540     .cfi_data_align_factor = -8,
    541     .cfi_cfa_init_reg = 2u,
    542     .cfi_cfa_init_offset = 0,
    543     .backend_features = KIT_CG_BACKEND_STRICT_ALIGNMENT,
    544     .atomic_lock_free_max = 8u,
    545     .supports_call_conv = rv64_supports_call_conv,
    546     .supports_intrinsic = rv64_supports_intrinsic,
    547     .resolve_float_abi = rv64_resolve_float_abi,
    548 };
    549 
    550 /* RV32 shares nearly all of the RISC-V backend with rv64 — the per-XLEN
    551  * differences are threaded through RiscvVariant inside the shared functions.
    552  * Differs only in: backend/arch names + kind, the link descriptor + dbg ops
    553  * (rv_lw / 2-byte min insn), the ilp32f predefined-macro table, and the CFI
    554  * data alignment factor (-4 word stride vs rv64's -8 doubleword). */
    555 const ArchImpl arch_impl_rv32 = {
    556     .backend = {.name = "rv32", .make = rv64_backend_make},
    557     .kind = KIT_ARCH_RV32,
    558     .name = "rv32",
    559     .cgtarget_new = rv64_semantic_target_new,
    560     .asm_new = rv64_arch_asm_new,
    561     .disasm_new = rv64_disasm_new,
    562     .apply_label_fixup = rv64_apply_label_fixup,
    563     .decode = &rv64_decode_ops,
    564     .emu = &rv64_emu_ops,
    565     .link = &link_arch_rv32,
    566     .dwarf = &rv64_dwarf_ops,
    567     .dbg = &rv32_dbg_ops,
    568     .asm_ops = &rv64_asm_ops,
    569     .predefined_macros = rv32_predefined_macros,
    570     .npredefined_macros =
    571         (u32)(sizeof rv32_predefined_macros / sizeof rv32_predefined_macros[0]),
    572     .target_features = rv64_target_features,
    573     .ntarget_features =
    574         (u32)(sizeof rv64_target_features / sizeof rv64_target_features[0]),
    575     .target_feature_defaults = rv64_target_feature_defaults,
    576     .target_feature_apply_isa = rv64_target_feature_apply_isa,
    577     .register_name = rv64_register_name,
    578     .register_index = rv64_register_index,
    579     .register_count = rv64_register_iter_size,
    580     .register_at = rv64_register_at_public,
    581     /* RISC-V psABI: return address in x1 (ra). 4-byte aligned insns
    582      * (cover 2-byte C-ext too via code_align=2). Data align -4 for
    583      * word stack stride (rv32). CFA = sp at entry. */
    584     .cfi_return_addr_reg = 1u,
    585     .cfi_code_align_factor = 2,
    586     .cfi_data_align_factor = -4,
    587     .cfi_cfa_init_reg = 2u,
    588     .cfi_cfa_init_offset = 0,
    589     .backend_features = KIT_CG_BACKEND_STRICT_ALIGNMENT,
    590     /* rv32 has no native 64-bit atomics (no lr.d/sc.d/amo*.d). */
    591     .atomic_lock_free_max = 4u,
    592     .supports_call_conv = rv64_supports_call_conv,
    593     .supports_intrinsic = rv64_supports_intrinsic,
    594     .resolve_float_abi = rv64_resolve_float_abi,
    595 };