diff --git a/core/rexcode/isa/riscv/encoder.odin b/core/rexcode/isa/riscv/encoder.odin index 62a104fe2..e37d7e8c8 100644 --- a/core/rexcode/isa/riscv/encoder.odin +++ b/core/rexcode/isa/riscv/encoder.odin @@ -455,6 +455,10 @@ resolve_relocation_inline :: #force_inline proc( return true } +is_implicit_op_inline :: #force_inline proc "contextless" (op: Operand_Type) -> bool { + return op == .GPR_SP +} + // ============================================================================= // Scatter / gather helpers for B-type and J-type immediates // ============================================================================= diff --git a/core/rexcode/isa/riscv/encoding_types.odin b/core/rexcode/isa/riscv/encoding_types.odin index a89151ba4..bf3e1bd11 100644 --- a/core/rexcode/isa/riscv/encoding_types.odin +++ b/core/rexcode/isa/riscv/encoding_types.odin @@ -52,11 +52,12 @@ Feature :: enum u8 { } Encoding_Flags :: bit_field u8 { - rv32_only: bool | 1, // RV32 base only - rv64_only: bool | 1, // RV64 base only (e.g. LD/SD/ADDIW/...) - branch: bool | 1, // changes PC - fp_round: bool | 1, // funct3 doubles as FP rounding-mode field - _: u8 | 4, + rv32_only: bool | 1, // RV32 base only + rv64_only: bool | 1, // RV64 base only (e.g. LD/SD/ADDIW/...) + branch: bool | 1, // changes PC + fp_round: bool | 1, // funct3 doubles as FP rounding-mode field + explicit_count: u8 | 3, + has_implicit: bool | 1, } // What the user passes in. @@ -149,7 +150,7 @@ Encoding :: struct #packed { enc: [4]Operand_Encoding, // 4 bits: u32, // 4 -- static bit pattern mask: u32, // 4 -- which bits are static - feature: Feature, // 1 + feature: Feature, // 1 flags: Encoding_Flags, // 1 } #assert(size_of(Encoding) == 20) diff --git a/core/rexcode/isa/riscv/tablegen/gen.odin b/core/rexcode/isa/riscv/tablegen/gen.odin index 8ad85fcef..0a7ee797a 100644 --- a/core/rexcode/isa/riscv/tablegen/gen.odin +++ b/core/rexcode/isa/riscv/tablegen/gen.odin @@ -113,7 +113,22 @@ emit_encode_tables :: proc() -> (total: int) { fmt.sbprintfln(&sb, "\t// .%v", m) for &form in forms { f := &form.encoding - write_row(&sb, f.mnemonic, f.ops, f.enc, f.bits, f.mask, f.feature, f.flags) + flags := f.flags + + encoding_operand_count: u8 = 0 + has_implicit := false + for op_type in f.ops { + if op_type == .NONE { break } + if lib.is_implicit_op_inline(op_type) { + has_implicit = true + } else { + encoding_operand_count += 1 + } + } + flags.explicit_count = encoding_operand_count + flags.has_implicit = has_implicit + + write_row(&sb, f.mnemonic, f.ops, f.enc, f.bits, f.mask, f.feature, flags) } } strings.write_string(&sb, "}\n\n") diff --git a/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin b/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin index bd4cc4e11..05471442c 100644 --- a/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin +++ b/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin @@ -309,6 +309,20 @@ main :: proc() { // NOTE: SideEffectFlag_HINT deliberately excluded — inert, may be DCE'd. return ((side_effects & VOLATILE_SE) != 0); } + + u8 is_call_or_mem() const { + return (cast(u16)side_effects & SideEffectFlag_CONTROL) != 0 || + (cast(u16)implicit_wr & ClobberReg_RSP) != 0; + } + bool has_control() const { + return (cast(u16)side_effects & SideEffectFlag_CONTROL) != 0; + } + bool has_halt() const { + return (cast(u16)side_effects & SideEffectFlag_HALT) != 0; + } + bool is_conditional() const { + return has_control() && (cast(u16)flags_rd != 0); + } }; void clobber_implicit_regs(StringSet *clobber_registers_set, u16 implicit_regs) { @@ -381,13 +395,6 @@ main :: proc() { fmt.sbprintf(&sb, "return cast(u8)((flags>>%du)&((1u<<%d)-1));", bit_offset, bit_size) strings.write_string(&sb, " }\n") } - { - bit_offset := intrinsics.type_field_bit_offset(Encoding_Flags, "op_count") - bit_size := intrinsics.type_field_bit_size(Encoding_Flags, "op_count") - strings.write_string(&sb, "\t\tu8 op_count () const { ") - fmt.sbprintf(&sb, "return cast(u8)((flags>>%du)&((1u<<%d)-1));", bit_offset, bit_size) - strings.write_string(&sb, " }\n") - } { bit_offset := intrinsics.type_field_bit_offset(Encoding_Flags, "lock_ok") strings.write_string(&sb, "\t\tbool lock_ok () const { ") @@ -421,7 +428,8 @@ main :: proc() { strings.write_string(&sb, """ - bool init() { + bool init(i64 word_size) { + gb_unused(word_size); string_map_init(&mnemonic_map, MNEMONIC_COUNT*2); for (u16 m = M_INVALID+1; m < MNEMONIC_COUNT; m++) { string_map_set(&mnemonic_map, mnemonic_strings[m], cast(Mnemonic)m); diff --git a/src/asm_tables.cpp b/src/asm_tables.cpp index eb7e3c109..e2ede6508 100644 --- a/src/asm_tables.cpp +++ b/src/asm_tables.cpp @@ -54,7 +54,9 @@ gb_global String const asm_operand_kind_expected_strings[AsmOperand_COUNT] = { }; #include "asm_tables_amd64.cpp" +#include "asm_tables_riscv.cpp" -void init_asm_tables() { - g_asm_amd64.init(); +void init_asm_tables(i64 word_size) { + g_asm_amd64.init(word_size); + g_asm_riscv.init(word_size); } \ No newline at end of file diff --git a/src/asm_tables_amd64.cpp b/src/asm_tables_amd64.cpp index 06d583ad7..aa3148416 100644 --- a/src/asm_tables_amd64.cpp +++ b/src/asm_tables_amd64.cpp @@ -312,6 +312,20 @@ struct Asm_amd64 { // NOTE: SideEffectFlag_HINT deliberately excluded — inert, may be DCE'd. return ((side_effects & VOLATILE_SE) != 0); } + + u8 is_call_or_mem() const { + return (cast(u16)side_effects & SideEffectFlag_CONTROL) != 0 || + (cast(u16)implicit_wr & ClobberReg_RSP) != 0; + } + bool has_control() const { + return (cast(u16)side_effects & SideEffectFlag_CONTROL) != 0; + } + bool has_halt() const { + return (cast(u16)side_effects & SideEffectFlag_HALT) != 0; + } + bool is_conditional() const { + return has_control() && (cast(u16)flags_rd != 0); + } }; void clobber_implicit_regs(StringSet *clobber_registers_set, u16 implicit_regs) { @@ -424,7 +438,6 @@ struct Asm_amd64 { bool has_implicit () const { return ((flags>>21u)&1) != 0; } u8 explicit_count() const { return cast(u8)((flags>>18u)&((1u<<3)-1)); } - u8 op_count () const { return cast(u8)((flags>>22u)&((1u<<3)-1)); } bool lock_ok () const { return ((flags>>14u)&1) != 0; } bool rep_ok () const { return ((flags>>15u)&1) != 0; } }; @@ -447,7 +460,8 @@ struct Asm_amd64 { StringMap prefix_map; StringMap register_map; - bool init() { + bool init(i64 word_size) { + gb_unused(word_size); string_map_init(&mnemonic_map, MNEMONIC_COUNT*2); for (u16 m = M_INVALID+1; m < MNEMONIC_COUNT; m++) { string_map_set(&mnemonic_map, mnemonic_strings[m], cast(Mnemonic)m); diff --git a/src/check_asm.cpp b/src/check_asm.cpp index d2fec1525..e7f4b6e75 100644 --- a/src/check_asm.cpp +++ b/src/check_asm.cpp @@ -1080,8 +1080,7 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm // aligned at the call boundary) or manipulates RSP directly. Plain memory access // through a parameter pointer does NOT require stack realignment, so // implies_clobber_memory() is intentionally NOT used here. - if ((cast(u16)clobber.side_effects & asm_ctx->SideEffectFlag_CONTROL) != 0 || - (cast(u16)clobber.implicit_wr & asm_ctx->ClobberReg_RSP) != 0) { + if (clobber.is_call_or_mem()) { asm_acc->saw_call_or_mem = true; } @@ -1159,17 +1158,16 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm // is not merely CONTROL-with-fallthrough. We approximate "unconditional" as // CONTROL|HALT with no explicit label/operand fallthrough below. { - u16 se = cast(u16)clobber.side_effects; - bool control = (se & asm_ctx->SideEffectFlag_CONTROL) != 0; - bool halt = (se & asm_ctx->SideEffectFlag_HALT) != 0; + bool control = clobber.has_control(); + bool halt = clobber.has_halt(); // A conditional branch reads a flag and can fall through -> not terminal. - bool conditional = control && (cast(u16)clobber.flags_rd != 0); + bool conditional = clobber.is_conditional(); asm_acc->last_is_terminal = halt || (control && !conditional); } // A branch/call inside the template means subsequent instructions may be reached // out of textual order; stop trusting the linear def model past this point. - if (cast(u16)clobber.side_effects & asm_ctx->SideEffectFlag_CONTROL) { + if (clobber.has_control()) { asm_acc->straight_line = false; } asm_ctx->clobber_implicit_regs(&tmpl_entity->AsmTemplate.clobber_registers_set, produced); @@ -2126,6 +2124,8 @@ gb_internal void check_asm_template(AsmCtx *asm_ctx, CheckerContext *ctx, Entity gb_internal void check_asm_template_from_entity(CheckerContext *c, Entity *e, DeclInfo *d) { if (build_context.metrics.arch == TargetArch_amd64) { check_asm_template(&g_asm_amd64, c, e, d); + } else if (build_context.metrics.arch == TargetArch_riscv64) { + check_asm_template(&g_asm_riscv, c, e, d); } else { error(e->token, "asm templates are not currently supported for this target"); } diff --git a/src/main.cpp b/src/main.cpp index 28b1464cf..84ec22bdf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4321,7 +4321,7 @@ int main(int arg_count, char const **arg_ptr) { bool failed_to_cache_parsing = false; TIME_SECTION("init asm tables"); - init_asm_tables(); + init_asm_tables(build_context.metrics.ptr_size); MAIN_TIME_SECTION("parse files");