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