From 5ca7638bb21710d1d3e8f97fca66652df13c2d7d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 Aug 2026 11:11:53 +0100 Subject: [PATCH] Check asm instruction operand count --- .../x86/tablegen/cpp-compiler/cpp-gen.odin | 17 ++--- src/asm_tables_amd64.cpp | 17 ++--- src/check_asm.cpp | 73 ++++++++++++++++--- 3 files changed, 77 insertions(+), 30 deletions(-) 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 89c3cf596..8418b99b4 100644 --- a/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin +++ b/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin @@ -174,14 +174,14 @@ main :: proc() { } { bit_offset := intrinsics.type_field_bit_offset(Encoding_Flags, "explicit_count") - bit_size := intrinsics.type_field_bit_offset(Encoding_Flags, "explicit_count") + bit_size := intrinsics.type_field_bit_size(Encoding_Flags, "explicit_count") strings.write_string(&sb, "\t\tu8 explicit_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, "op_count") - bit_size := 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") @@ -203,8 +203,6 @@ main :: proc() { strings.write_string(&sb, "\tStringMap mnemonic_map;\n") strings.write_string(&sb, "\tStringMap prefix_map;\n") strings.write_string(&sb, "\tStringMap register_map;\n") - strings.write_string(&sb, "\tSlice ENCODE_RUNS;\n") - strings.write_string(&sb, "\tSlice ENCODE_FORMS;\n") { strings.write_string(&sb, "\tbool init() {\n") @@ -246,15 +244,16 @@ main :: proc() { strings.write_string(&sb, "\t\treturn found ? *found : REG_INVALID;\n") } { - strings.write_string(&sb, "\tSlice encoding_forms(Mnemonic m) const {\n") + strings.write_string(&sb, "\tSlice encoding_forms(/*Mnemonic*/ u16 m) const {\n") defer strings.write_string(&sb, "\t}\n") - strings.write_string(&sb, "\t\tEncodeRun r = ENCODE_RUNS[m];\n") - strings.write_string(&sb, "\t\treturn slice_lower_and_count(ENCODE_FORMS, r.start, r.count);\n") + strings.write_string(&sb, "\t\tEncodeRun r = raw_encode_runs[m];\n") + strings.write_string(&sb, "\t\tEncoding *ENCODE_FORMS = cast(Encoding *)raw_encode_forms;\n") + strings.write_string(&sb, "\t\treturn Slice{ENCODE_FORMS+r.start, r.count};\n") } { - strings.write_string(&sb, "\tu16 reg_class(Register r) const {\n") - strings.write_string(&sb, "\t\treturn 0xFF00 & cast(u16)r;\n") + strings.write_string(&sb, "\tu16 reg_class(/*Register*/ u16 r) const {\n") + strings.write_string(&sb, "\t\treturn 0xFF00 & r;\n") strings.write_string(&sb, "\t}\n") } { diff --git a/src/asm_tables_amd64.cpp b/src/asm_tables_amd64.cpp index 2d8ba1557..2c8848ba0 100644 --- a/src/asm_tables_amd64.cpp +++ b/src/asm_tables_amd64.cpp @@ -229,8 +229,8 @@ struct Asm_amd64 { EncodingFlags flags; bool has_implicit () const { return ((flags>>21u)&1) != 0; } - u8 explicit_count() const { return cast(u8)((flags>>18u)&((1u<<18)-1)); } - u8 op_count () const { return cast(u8)((flags>>22u)&((1u<<22)-1)); } + u8 explicit_count() const { return cast(u8)((flags>>18u)&((1u<<3)-1)); } + u8 op_count () const { return cast(u8)((flags>>22u)&((1u<<3)-1)); } }; #pragma pack(pop) GB_STATIC_ASSERT(gb_size_of(Encoding) == 16); @@ -248,8 +248,6 @@ struct Asm_amd64 { StringMap mnemonic_map; StringMap prefix_map; StringMap register_map; - Slice ENCODE_RUNS; - Slice ENCODE_FORMS; bool init() { string_map_init(&mnemonic_map, MNEMONIC_COUNT*2); for (u16 m = M_INVALID+1; m < MNEMONIC_COUNT; m++) { @@ -277,12 +275,13 @@ struct Asm_amd64 { Register *found = string_map_get(®ister_map, name); return found ? *found : REG_INVALID; } - Slice encoding_forms(Mnemonic m) const { - EncodeRun r = ENCODE_RUNS[m]; - return slice_lower_and_count(ENCODE_FORMS, r.start, r.count); + Slice encoding_forms(/*Mnemonic*/ u16 m) const { + EncodeRun r = raw_encode_runs[m]; + Encoding *ENCODE_FORMS = cast(Encoding *)raw_encode_forms; + return Slice{ENCODE_FORMS+r.start, r.count}; } - u16 reg_class(Register r) const { - return 0xFF00 & cast(u16)r; + u16 reg_class(/*Register*/ u16 r) const { + return 0xFF00 & r; } // size in bits for register u16 reg_size(Register r) const { diff --git a/src/check_asm.cpp b/src/check_asm.cpp index 828d6af93..3b73d5114 100644 --- a/src/check_asm.cpp +++ b/src/check_asm.cpp @@ -290,7 +290,7 @@ gb_internal void check_asm_specs(CheckerContext *ctx, Scope *scope, Slice } } -gb_internal bool check_register(CheckerContext *ctx, AstAsmRegister *asm_reg) { +gb_internal bool check_register(AstAsmRegister *asm_reg) { String name = asm_reg->name.string; auto r = g_asm_amd64.register_lookup(name); if (r) { @@ -306,10 +306,11 @@ enum CheckMnemomicResult { CheckMnemomic_Prefix, }; -gb_internal CheckMnemomicResult check_mnemonic(CheckerContext *ctx, AstAsmInstruction *instruction) { - String name = instruction->name->Ident.token.string; +gb_internal CheckMnemomicResult check_mnemonic_name(AstAsmInstruction *instr, u16 *mnemonic_) { + String name = instr->name->Ident.token.string; auto m = g_asm_amd64.mnemonic_lookup(name); if (m) { + if (mnemonic_) *mnemonic_ = cast(u16)m; return CheckMnemomic_Mnemonic; } auto p = g_asm_amd64.prefix_lookup(name); @@ -317,14 +318,50 @@ gb_internal CheckMnemomicResult check_mnemonic(CheckerContext *ctx, AstAsmInstru return CheckMnemomic_Prefix; } - if (instruction->operands.count == 0) { - error(instruction->name, "Unknown mnemonic/prefix for this target platform: %%%.*s", LIT(name)); + if (instr->operands.count == 0) { + error(instr->name, "Unknown mnemonic/prefix for this target platform: %%%.*s", LIT(name)); } else { - error(instruction->name, "Unknown mnemonic for this target platform: %%%.*s", LIT(name)); + error(instr->name, "Unknown mnemonic for this target platform: %%%.*s", LIT(name)); } return CheckMnemomic_Invalid; } +gb_internal void check_mnemonic(CheckerContext *ctx, AstAsmInstruction *instr, u16 mnemonic, Slice const &operands) { + GB_ASSERT(mnemonic > 0); + auto forms = g_asm_amd64.encoding_forms(mnemonic); + String name = g_asm_amd64.mnemonic_strings[mnemonic]; + + int min_count = I32_MAX; + int max_count = -1; + + bool ok = false; + + for (auto form : forms) { + int explicit_count = cast(int)form.explicit_count(); + min_count = gb_min(min_count, explicit_count); + max_count = gb_max(min_count, explicit_count); + if (operands.count != explicit_count) { + continue; + } + ok = true; // pretend for the time being + } + + if (operands.count < min_count || operands.count > max_count) { + if (min_count == max_count) { + error(instr->name, "The asm instruction '%.*s' expects %d operands, got %d", LIT(name), max_count, operands.count); + } else { + error(instr->name, "The asm instruction '%.*s' expects %d..=%d operands, got %d", LIT(name), min_count, max_count, operands.count); + } + return; + } + if (ok) { + return; + } + + error(instr->name, "The operands to '%.*s' matched non of the expected encoding forms", LIT(name)); +} + + gb_internal void check_asm_instruction_operand(CheckerContext *ctx, Entity *entity, Operand *operand, Ast *expr, bool allow_memory_operands) { if (expr == nullptr) { @@ -360,7 +397,7 @@ gb_internal void check_asm_instruction_operand(CheckerContext *ctx, Entity *enti return; case_end; case_ast_node(asm_reg, AsmRegister, expr); - check_register(ctx, asm_reg); + check_register(asm_reg); return; case_end; case_ast_node(mem_op, AsmMemoryOperand, expr); @@ -378,7 +415,7 @@ gb_internal void check_asm_instruction_operand(CheckerContext *ctx, Entity *enti for (int i = 0; base.expr && i == 0; i++) { if (base.expr->kind == Ast_AsmRegister) { - check_register(ctx, &base.expr->AsmRegister); + check_register(&base.expr->AsmRegister); } else { Entity *param_entity = entity_of_node(base.expr); if (param_entity == nullptr || param_entity->kind != Entity_Variable) { @@ -399,7 +436,7 @@ gb_internal void check_asm_instruction_operand(CheckerContext *ctx, Entity *enti for (int i = 0; index.expr && i == 0; i++) { if (index.expr->kind == Ast_AsmRegister) { - check_register(ctx, &index.expr->AsmRegister); + check_register(&index.expr->AsmRegister); } else { Entity *param_entity = entity_of_node(index.expr); if (param_entity == nullptr || param_entity->kind != Entity_Variable) { @@ -459,7 +496,7 @@ gb_internal void check_asm_instruction_operand(CheckerContext *ctx, Entity *enti for (int i = 0; disp.expr && i == 0; i++) { if (disp.expr->kind == Ast_AsmRegister) { - check_register(ctx, &disp.expr->AsmRegister); + check_register(&disp.expr->AsmRegister); } else { Entity *param_entity = entity_of_node(disp.expr); if (disp.mode == Addressing_Constant) { @@ -559,7 +596,7 @@ gb_internal void check_asm_template(CheckerContext *ctx, Entity *entity, DeclInf switch (clobber->value->kind) { case_ast_node(asm_reg, AsmRegister, clobber->value) String reg = asm_reg->name.string; - if (check_register(ctx, asm_reg)) { + if (check_register(asm_reg)) { if (string_set_update(®_set, reg)) { error(clobber->value, "#clobber %%%.*s has already been defined", LIT(reg)); } @@ -614,21 +651,33 @@ gb_internal void check_asm_template(CheckerContext *ctx, Entity *entity, DeclInf } + Array operands = {}; + operands.allocator = heap_allocator(); + array_reserve(&operands, 16); + defer (array_free(&operands)); + for (Ast *instruction_ : at->instructions) { switch (instruction_->kind) { case_ast_node(instr, AsmInstruction, instruction_); GB_ASSERT(instr->name->kind == Ast_Ident); - CheckMnemomicResult res = check_mnemonic(ctx, instr); + u16 mnemonic = 0; + CheckMnemomicResult res = check_mnemonic_name(instr, &mnemonic); + + + array_clear(&operands); for (Ast *expr : instr->operands) { Operand operand = {}; check_asm_instruction_operand(ctx, entity, &operand, expr, /*allow_memory_operands*/true); + array_add(&operands, operand); } if (res == CheckMnemomic_Prefix) { if (instr->operands.count != 0) { error(instr->name, "A prefix must not have any operands, and be separate from the instruction it is prefixing"); } + } else if (res == CheckMnemomic_Mnemonic) { + check_mnemonic(ctx, instr, mnemonic, slice_from_array(operands)); } case_end;