kit

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

arch.c (11987B)


      1 #include "arch/arch.h"
      2 
      3 #include <string.h>
      4 
      5 #include "arch/rv64/asm.h"
      6 #include "arch/rv64/disasm.h"
      7 #include "arch/rv64/regs.h"
      8 #include "arch/rv64/rv64.h"
      9 #include "cg/native_direct_target.h"
     10 #include "core/bytes.h"
     11 #include "link/link_arch.h"
     12 #include "obj/obj.h"
     13 
     14 extern const LinkArchDesc link_arch_rv64;
     15 extern const ArchDbgOps rv64_dbg_ops;
     16 extern const ArchEmuOps rv64_emu_ops;
     17 extern const ArchDwarfOps rv64_dwarf_ops;
     18 extern const ArchAsmOps rv64_asm_ops;
     19 
     20 static int rv64_register_at_public(uint32_t idx, KitArchReg* out) {
     21   const char* nm = NULL;
     22   int rc;
     23   if (!out) return 1;
     24   rc = rv64_register_iter_get(idx, &out->dwarf_idx, &nm);
     25   if (rc == 0) out->name = kit_slice_cstr(nm);
     26   return rc;
     27 }
     28 
     29 static SrcLoc rv64_no_loc(void) {
     30   SrcLoc l = {0, 0, 0};
     31   return l;
     32 }
     33 
     34 static int rv64_apply_label_fixup(Compiler* c, const ArchLabelFixup* fx) {
     35   const Section* s;
     36   u8 cur[4];
     37   u32 word;
     38   u32 b;
     39 
     40   (void)c;
     41   if (!fx) return 1;
     42   s = obj_section_get(fx->obj, fx->sec_id);
     43   if (!s) return 0;
     44 
     45   /* INTRA_AUIPC_ADDI is a width=8 pair; other kinds patch a single 4-byte
     46    * instruction. Read the first word only for the 4-byte cases. */
     47   if (fx->kind != R_RV_INTRA_AUIPC_ADDI) {
     48     if (fx->width != 4) return 1;
     49     buf_read(&s->bytes, fx->offset, cur, 4);
     50     word = rd_u32_le(cur);
     51   } else {
     52     buf_read(&s->bytes, fx->offset, cur, 4);
     53     word = rd_u32_le(cur);
     54   }
     55   b = (u32)fx->disp;
     56 
     57   switch (fx->kind) {
     58     case R_RV_BRANCH:
     59       /* B-type reaches ±4 KiB. Conditional branches are emitted over a jal
     60        * (see rv_cmp_branch) so this only carries small fixed displacements;
     61        * a violation is a backend bug, not silently-truncated code. */
     62       if ((i64)fx->disp < -(i64)(1 << 12) || (i64)fx->disp >= (i64)(1 << 12))
     63         compiler_panic(c, rv64_no_loc(), "rv64: BRANCH out of range (±4KiB)");
     64       word &= 0x01fff07fu;
     65       word |= ((b >> 12) & 1u) << 31;
     66       word |= ((b >> 5) & 0x3fu) << 25;
     67       word |= ((b >> 1) & 0xfu) << 8;
     68       word |= ((b >> 11) & 1u) << 7;
     69       break;
     70     case R_RV_JAL:
     71       /* J-type reaches ±1 MiB — ample for intra-function jumps (including the
     72        * long leg of a conditional branch). Fail loudly rather than wrap. */
     73       if ((i64)fx->disp < -(i64)(1 << 20) || (i64)fx->disp >= (i64)(1 << 20))
     74         compiler_panic(c, rv64_no_loc(), "rv64: JAL out of range (±1MiB)");
     75       word &= 0x00000fffu;
     76       word |= ((b >> 20) & 1u) << 31;
     77       word |= ((b >> 1) & 0x3ffu) << 21;
     78       word |= ((b >> 11) & 1u) << 20;
     79       word |= ((b >> 12) & 0xffu) << 12;
     80       break;
     81     case R_RV_INTRA_AUIPC_ADDI: {
     82       /* width=8: patch both the AUIPC at fx->offset and the ADDI at
     83        * fx->offset+4. disp is the byte offset from the AUIPC PC to the
     84        * target label. */
     85       u8 cur2[4];
     86       u32 word2;
     87       i32 disp = (i32)fx->disp;
     88       /* hi20 is the top 20 bits of (disp + 0x800) so the sign-extended
     89        * 12-bit lo12 cancels out. */
     90       u32 hi20 = (u32)((disp + 0x800) >> 12) & 0xfffffu;
     91       u32 lo12 = (u32)disp & 0xfffu;
     92       if (fx->width != 8) return 1;
     93       /* AUIPC: keep rd (bits 11:7) and opcode (bits 6:0); patch imm[31:12]. */
     94       word = (word & 0x00000fffu) | (hi20 << 12);
     95       wr_u32_le(cur, word);
     96       obj_patch(fx->obj, fx->sec_id, fx->offset, cur, 4);
     97       buf_read(&s->bytes, fx->offset + 4, cur2, 4);
     98       word2 = rd_u32_le(cur2);
     99       /* ADDI: keep rs1/funct3/rd/opcode (bits 19:0); patch imm[11:0]. */
    100       word2 = (word2 & 0x000fffffu) | (lo12 << 20);
    101       wr_u32_le(cur2, word2);
    102       obj_patch(fx->obj, fx->sec_id, fx->offset + 4, cur2, 4);
    103       return 0;
    104     }
    105     default:
    106       return 1;
    107   }
    108 
    109   wr_u32_le(cur, word);
    110   obj_patch(fx->obj, fx->sec_id, fx->offset, cur, 4);
    111   return 0;
    112 }
    113 
    114 /* Mirrors `clang --target=riscv64-linux-gnu -E -dM` for the in-scope
    115  * RV64GC profile: I/M/F/D/A/C + Zicsr-minimal. Macros that depend on
    116  * extensions outside scope (V, B, Zve*, Zfh, …) are deliberately
    117  * absent. ABI variant is lp64d. */
    118 static const KitPredefinedMacro rv64_predefined_macros[] = {
    119     {KIT_SLICE_LIT("__riscv"), KIT_SLICE_LIT("1")},
    120     {KIT_SLICE_LIT("__riscv_xlen"), KIT_SLICE_LIT("64")},
    121     {KIT_SLICE_LIT("__riscv_float_abi_double"), KIT_SLICE_LIT("1")},
    122     {KIT_SLICE_LIT("__riscv_atomic"), KIT_SLICE_LIT("1")},
    123     {KIT_SLICE_LIT("__riscv_mul"), KIT_SLICE_LIT("1")},
    124     {KIT_SLICE_LIT("__riscv_div"), KIT_SLICE_LIT("1")},
    125     {KIT_SLICE_LIT("__riscv_muldiv"), KIT_SLICE_LIT("1")},
    126     {KIT_SLICE_LIT("__riscv_compressed"), KIT_SLICE_LIT("1")},
    127     {KIT_SLICE_LIT("__riscv_flen"), KIT_SLICE_LIT("64")},
    128     {KIT_SLICE_LIT("__riscv_fdiv"), KIT_SLICE_LIT("1")},
    129     {KIT_SLICE_LIT("__riscv_fsqrt"), KIT_SLICE_LIT("1")},
    130     {KIT_SLICE_LIT("__riscv_zicsr"), KIT_SLICE_LIT("1")},
    131     {KIT_SLICE_LIT("__riscv_zifencei"), KIT_SLICE_LIT("1")},
    132     {KIT_SLICE_LIT("__riscv_arch_test"), KIT_SLICE_LIT("1")},
    133     {KIT_SLICE_LIT("__LP64__"), KIT_SLICE_LIT("1")},
    134     {KIT_SLICE_LIT("_LP64"), KIT_SLICE_LIT("1")},
    135     {KIT_SLICE_LIT("__ORDER_LITTLE_ENDIAN__"), KIT_SLICE_LIT("1234")},
    136     {KIT_SLICE_LIT("__ORDER_BIG_ENDIAN__"), KIT_SLICE_LIT("4321")},
    137     {KIT_SLICE_LIT("__BYTE_ORDER__"), KIT_SLICE_LIT("__ORDER_LITTLE_ENDIAN__")},
    138     {KIT_SLICE_LIT("__LITTLE_ENDIAN__"), KIT_SLICE_LIT("1")},
    139 };
    140 
    141 enum {
    142   RV64_FEAT_I = 0,
    143   RV64_FEAT_M,
    144   RV64_FEAT_A,
    145   RV64_FEAT_F,
    146   RV64_FEAT_D,
    147   RV64_FEAT_C,
    148   RV64_FEAT_ZICSR,
    149   RV64_FEAT_ZIFENCEI,
    150 };
    151 
    152 static const ArchTargetFeature rv64_target_features[] = {
    153     {"i"}, {"m"}, {"a"}, {"f"}, {"d"}, {"c"}, {"zicsr"}, {"zifencei"},
    154 };
    155 
    156 static void rv64_feature_set(u64* words, u32 nwords, u32 idx) {
    157   if (!words || idx / 64u >= nwords) return;
    158   words[idx / 64u] |= 1ull << (idx % 64u);
    159 }
    160 
    161 static void rv64_feature_clear(u64* words, u32 nwords, u32 idx) {
    162   if (!words || idx / 64u >= nwords) return;
    163   words[idx / 64u] &= ~(1ull << (idx % 64u));
    164 }
    165 
    166 static void rv64_feature_disable_all(u64* words, u32 nwords) {
    167   rv64_feature_clear(words, nwords, RV64_FEAT_I);
    168   rv64_feature_clear(words, nwords, RV64_FEAT_M);
    169   rv64_feature_clear(words, nwords, RV64_FEAT_A);
    170   rv64_feature_clear(words, nwords, RV64_FEAT_F);
    171   rv64_feature_clear(words, nwords, RV64_FEAT_D);
    172   rv64_feature_clear(words, nwords, RV64_FEAT_C);
    173   rv64_feature_clear(words, nwords, RV64_FEAT_ZICSR);
    174   rv64_feature_clear(words, nwords, RV64_FEAT_ZIFENCEI);
    175 }
    176 
    177 static void rv64_feature_enable_g(u64* words, u32 nwords) {
    178   rv64_feature_set(words, nwords, RV64_FEAT_I);
    179   rv64_feature_set(words, nwords, RV64_FEAT_M);
    180   rv64_feature_set(words, nwords, RV64_FEAT_A);
    181   rv64_feature_set(words, nwords, RV64_FEAT_F);
    182   rv64_feature_set(words, nwords, RV64_FEAT_D);
    183   rv64_feature_set(words, nwords, RV64_FEAT_ZICSR);
    184   rv64_feature_set(words, nwords, RV64_FEAT_ZIFENCEI);
    185 }
    186 
    187 static int rv64_has_prefix(const char* p, const char* end, const char* lit) {
    188   size_t n = strlen(lit);
    189   return (size_t)(end - p) >= n && memcmp(p, lit, n) == 0;
    190 }
    191 
    192 static void rv64_skip_version(const char** pp, const char* end) {
    193   const char* p = *pp;
    194   while (p < end && ((*p >= '0' && *p <= '9') || *p == 'p')) ++p;
    195   *pp = p;
    196 }
    197 
    198 static KitStatus rv64_target_feature_apply_isa(const Target* target,
    199                                                KitSlice isa, u64* words,
    200                                                u32 nwords) {
    201   const char* p;
    202   const char* end;
    203   (void)target;
    204   if (isa.len < 5 || memcmp(isa.s, "rv64", 4) != 0) return KIT_UNSUPPORTED;
    205   p = isa.s + 4;
    206   end = isa.s + isa.len;
    207   rv64_feature_disable_all(words, nwords);
    208   while (p < end) {
    209     if (*p == '_') {
    210       ++p;
    211       continue;
    212     }
    213     switch (*p) {
    214       case 'i':
    215         rv64_feature_set(words, nwords, RV64_FEAT_I);
    216         ++p;
    217         rv64_skip_version(&p, end);
    218         continue;
    219       case 'm':
    220         rv64_feature_set(words, nwords, RV64_FEAT_M);
    221         ++p;
    222         rv64_skip_version(&p, end);
    223         continue;
    224       case 'a':
    225         rv64_feature_set(words, nwords, RV64_FEAT_A);
    226         ++p;
    227         rv64_skip_version(&p, end);
    228         continue;
    229       case 'f':
    230         rv64_feature_set(words, nwords, RV64_FEAT_F);
    231         ++p;
    232         rv64_skip_version(&p, end);
    233         continue;
    234       case 'd':
    235         rv64_feature_set(words, nwords, RV64_FEAT_D);
    236         ++p;
    237         rv64_skip_version(&p, end);
    238         continue;
    239       case 'c':
    240         rv64_feature_set(words, nwords, RV64_FEAT_C);
    241         ++p;
    242         rv64_skip_version(&p, end);
    243         continue;
    244       case 'g':
    245         rv64_feature_enable_g(words, nwords);
    246         ++p;
    247         rv64_skip_version(&p, end);
    248         continue;
    249       case 'z':
    250         if (rv64_has_prefix(p, end, "zicsr")) {
    251           rv64_feature_set(words, nwords, RV64_FEAT_ZICSR);
    252           p += 5;
    253           rv64_skip_version(&p, end);
    254           continue;
    255         }
    256         if (rv64_has_prefix(p, end, "zifencei")) {
    257           rv64_feature_set(words, nwords, RV64_FEAT_ZIFENCEI);
    258           p += 8;
    259           rv64_skip_version(&p, end);
    260           continue;
    261         }
    262         break;
    263     }
    264     return KIT_UNSUPPORTED;
    265   }
    266   return KIT_OK;
    267 }
    268 
    269 static void rv64_target_feature_defaults(const Target* target, u64* words,
    270                                          u32 nwords) {
    271   (void)target;
    272   rv64_feature_set(words, nwords, RV64_FEAT_I);
    273   rv64_feature_set(words, nwords, RV64_FEAT_M);
    274   rv64_feature_set(words, nwords, RV64_FEAT_A);
    275   rv64_feature_set(words, nwords, RV64_FEAT_F);
    276   rv64_feature_set(words, nwords, RV64_FEAT_D);
    277   rv64_feature_set(words, nwords, RV64_FEAT_C);
    278   rv64_feature_set(words, nwords, RV64_FEAT_ZICSR);
    279   rv64_feature_set(words, nwords, RV64_FEAT_ZIFENCEI);
    280 }
    281 
    282 static CgTarget* rv64_backend_make(Compiler* c, ObjBuilder* o,
    283                                    const KitCodeOptions* opts) {
    284   MCEmitter* mc = NULL;
    285   Debug* debug = NULL;
    286   CgTarget* t;
    287   NativeTarget* native;
    288   NativeDirectTargetConfig cfg;
    289   if (cg_mc_debug_new(c, o, opts, &mc, &debug) != KIT_OK) return NULL;
    290   native = rv64_native_target_new(c, o, mc);
    291   if (!native) return NULL;
    292   memset(&cfg, 0, sizeof cfg);
    293   cfg.native = native;
    294   cfg.ops = rv64_native_direct_ops();
    295   t = native_direct_target_new(c, o, &cfg);
    296   if (t) t->debug = debug;
    297   return t;
    298 }
    299 
    300 static CgTarget* rv64_semantic_target_new(Compiler* c, ObjBuilder* o,
    301                                           MCEmitter* mc) {
    302   NativeTarget* native;
    303   NativeDirectTargetConfig cfg;
    304   if (!mc) mc = mc_new(c, o);
    305   native = rv64_native_target_new(c, o, mc);
    306   if (!native) return NULL;
    307   memset(&cfg, 0, sizeof cfg);
    308   cfg.native = native;
    309   cfg.ops = rv64_native_direct_ops();
    310   return native_direct_target_new(c, o, &cfg);
    311 }
    312 
    313 const ArchImpl arch_impl_rv64 = {
    314     .backend = {.name = "rv64", .make = rv64_backend_make},
    315     .kind = KIT_ARCH_RV64,
    316     .name = "rv64",
    317     .cgtarget_new = rv64_semantic_target_new,
    318     .asm_new = rv64_arch_asm_new,
    319     .disasm_new = rv64_disasm_new,
    320     .apply_label_fixup = rv64_apply_label_fixup,
    321     .decode = &rv64_decode_ops,
    322     .emu = &rv64_emu_ops,
    323     .link = &link_arch_rv64,
    324     .dwarf = &rv64_dwarf_ops,
    325     .dbg = &rv64_dbg_ops,
    326     .asm_ops = &rv64_asm_ops,
    327     .predefined_macros = rv64_predefined_macros,
    328     .npredefined_macros =
    329         (u32)(sizeof rv64_predefined_macros / sizeof rv64_predefined_macros[0]),
    330     .target_features = rv64_target_features,
    331     .ntarget_features =
    332         (u32)(sizeof rv64_target_features / sizeof rv64_target_features[0]),
    333     .target_feature_defaults = rv64_target_feature_defaults,
    334     .target_feature_apply_isa = rv64_target_feature_apply_isa,
    335     .register_name = rv64_register_name,
    336     .register_index = rv64_register_index,
    337     .register_count = rv64_register_iter_size,
    338     .register_at = rv64_register_at_public,
    339     /* RISC-V psABI: return address in x1 (ra). 4-byte aligned insns
    340      * (cover 2-byte C-ext too via code_align=2). Data align -8 for
    341      * doubleword stack stride. CFA = sp at entry. */
    342     .cfi_return_addr_reg = 1u,
    343     .cfi_code_align_factor = 2,
    344     .cfi_data_align_factor = -8,
    345     .cfi_cfa_init_reg = 2u,
    346     .cfi_cfa_init_offset = 0,
    347 };