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