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 8129f4fbc..a005a4258 100644 --- a/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin +++ b/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin @@ -365,6 +365,27 @@ main :: proc() { strings.write_string(&sb, "\t\treturn 0; // OP_M (sizeless), OP_IMM*, OP_REL*, OP_K (opmask width is data-dependent), etc.\n") strings.write_string(&sb, "\t}\n") } + strings.write_string(&sb, "\n") + { + strings.write_string(&sb, "\tint form_explicit_slot(Encoding const &form, int explicit_index) {\n") + strings.write_string(&sb, "\t\tint seen = 0;\n") + strings.write_string(&sb, "\t\tfor (int j = 0; j < gb_count_of(form.ops); j++) {\n") + strings.write_string(&sb, "\t\t\tauto t = form.ops[j];\n") + strings.write_string(&sb, "\t\t\tif (!t) {\n") + strings.write_string(&sb, "\t\t\t\tbreak;\n") + strings.write_string(&sb, "\t\t\t}\n") + strings.write_string(&sb, "\t\t\tif (operand_type_is_implicit(t)) {\n") + strings.write_string(&sb, "\t\t\t\tcontinue;\n") + strings.write_string(&sb, "\t\t\t}\n") + strings.write_string(&sb, "\t\t\tif (seen == explicit_index) {\n") + strings.write_string(&sb, "\t\t\t\treturn j;\n") + strings.write_string(&sb, "\t\t\t}\n") + strings.write_string(&sb, "\t\t\tseen += 1;\n") + strings.write_string(&sb, "\t\t}\n") + strings.write_string(&sb, "\t\treturn -1;\n") + strings.write_string(&sb, "\t}\n") + + } strings.write_string(&sb, "};\n") diff --git a/src/asm_tables.cpp b/src/asm_tables.cpp index 561da7523..e599ff1b5 100644 --- a/src/asm_tables.cpp +++ b/src/asm_tables.cpp @@ -36,4 +36,8 @@ gb_global String const asm_operand_kind_expected_strings[AsmOperand_COUNT] = { str_lit("a label"), }; -#include "asm_tables_amd64.cpp" \ No newline at end of file +#include "asm_tables_amd64.cpp" + +void init_asm_tables() { + g_asm_amd64.init(); +} \ No newline at end of file diff --git a/src/asm_tables_amd64.cpp b/src/asm_tables_amd64.cpp index a64c87750..1a2f93b81 100644 --- a/src/asm_tables_amd64.cpp +++ b/src/asm_tables_amd64.cpp @@ -382,6 +382,24 @@ struct Asm_amd64 { } return 0; // OP_M (sizeless), OP_IMM*, OP_REL*, OP_K (opmask width is data-dependent), etc. } + + int form_explicit_slot(Encoding const &form, int explicit_index) { + int seen = 0; + for (int j = 0; j < gb_count_of(form.ops); j++) { + auto t = form.ops[j]; + if (!t) { + break; + } + if (operand_type_is_implicit(t)) { + continue; + } + if (seen == explicit_index) { + return j; + } + seen += 1; + } + return -1; + } }; diff --git a/src/check_asm.cpp b/src/check_asm.cpp index 9edc806fe..d6624ed44 100644 --- a/src/check_asm.cpp +++ b/src/check_asm.cpp @@ -14,26 +14,6 @@ gb_internal i32 check_asm_operand_bit_width(Type *type) { return cast(i32)(sz * 8); } -// Map the user's explicit operand index -> index into form.ops[], skipping implicit slots. -// Returns -1 if there is no such explicit slot (shouldn't happen once explicit_count matches). -gb_internal int asm_form_explicit_slot(Asm_amd64::Encoding const &form, int explicit_index) { - int seen = 0; - for (int j = 0; j < gb_count_of(form.ops); j++) { - auto t = form.ops[j]; - if (!t) { - break; - } - if (g_asm_amd64.operand_type_is_implicit(t)) { - continue; - } - if (seen == explicit_index) { - return j; - } - seen += 1; - } - return -1; -} - gb_internal bool is_valid_asm_parameter_type(Type *type) { if (is_type_integer(type)) { return true; @@ -73,13 +53,19 @@ gb_internal AsmRegClass check_asm_reg_class_from_type(Type *type) { } +enum AsmMismatch : u8 { + AsmMismatch_None, + AsmMismatch_Size, + AsmMismatch_Class, +}; + // Returns true if the operand's Odin type is size/class-compatible with the form's slot. -// On mismatch, fills *reason (static string) for a precise diagnostic. `type` here is the +// On mismatch, fills *mismatch_ for a precise diagnostic. `slot` here is the // resolved OperandType at the correct (implicit-skipped) slot. -template -gb_internal bool check_asm_operand_size_class(T *asm_ctx, typename T::OperandType slot, Operand const *operand, - String *reason_, i32 *want_bits_, i32 *got_bits_) { - if (reason_) *reason_ = {}; +template +gb_internal bool check_asm_operand_size_class(AsmCtx *asm_ctx, typename AsmCtx::OperandType slot, Operand const *operand, + AsmMismatch *mismatch_, i32 *want_bits_, i32 *got_bits_) { + if (mismatch_) *mismatch_ = AsmMismatch_None; // Only register/memory-sized slots constrain width & class. Immediates/labels/sizeless-mem: skip here. AsmRegClass want_class = asm_ctx->operand_type_reg_class(slot); i32 want_w = asm_ctx->operand_type_bit_width(slot); @@ -89,13 +75,8 @@ gb_internal bool check_asm_operand_size_class(T *asm_ctx, typename T::OperandTyp return true; } - AsmRegClass got_class = AsmRegClass_Unknown; - i32 got_w = 0; - if (operand->expr->kind == Ast_AsmRegister) { - - } - got_class = check_asm_reg_class_from_type(operand->type); - got_w = check_asm_operand_bit_width(operand->type); + AsmRegClass got_class = check_asm_reg_class_from_type(operand->type); + i32 got_w = check_asm_operand_bit_width(operand->type); if (got_w < 0) { // TODO(bill): determine the correct width from the untyped constant value got_w = want_w; @@ -123,14 +104,14 @@ gb_internal bool check_asm_operand_size_class(T *asm_ctx, typename T::OperandTyp } if (!class_ok) { - if (reason_) *reason_ = str_lit("register class"); + if (mismatch_) *mismatch_ = AsmMismatch_Class; return false; } } // Width check (only when the slot pins a width and we could size the type). if (want_w != 0 && got_w != 0 && want_w != got_w) { - if (reason_) *reason_ = str_lit("size"); + if (mismatch_) *mismatch_ = AsmMismatch_Size; return false; } return true; @@ -385,12 +366,21 @@ gb_internal void check_asm_specs(CheckerContext *ctx, Scope *scope, Slice } } -gb_internal bool check_register(Operand *operand, AstAsmRegister *asm_reg) { +template +gb_internal bool check_register(AsmCtx *asm_ctx, Operand *operand, AstAsmRegister *asm_reg) { String name = asm_reg->name.string; - auto r = g_asm_amd64.register_lookup(name); + auto r = asm_ctx->register_lookup(name); if (r) { operand->mode = Addressing_Value; - u16 width_in_bits = g_asm_amd64.reg_size(r); + + u16 reg_class = asm_ctx->reg_class(r); + if (reg_class == asm_ctx->REG_CLASS_K) { + // Opmask register: classify as a mask, not a 64-bit integer. + // operand->type = t_asm_mask; // see note if this type does not yet exist + // return true; + } + + u16 width_in_bits = asm_ctx->reg_size(r); switch (width_in_bits) { case 8: operand->type = t_u8; @@ -433,14 +423,15 @@ enum CheckMnemomicResult { CheckMnemomic_Prefix, }; -gb_internal CheckMnemomicResult check_mnemonic_name(AstAsmInstruction *instr, u16 *mnemonic_) { +template +gb_internal CheckMnemomicResult check_mnemonic_name(AsmCtx *asm_ctx, AstAsmInstruction *instr, u16 *mnemonic_) { String name = instr->name->Ident.token.string; - auto m = g_asm_amd64.mnemonic_lookup(name); + auto m = asm_ctx->mnemonic_lookup(name); if (m) { if (mnemonic_) *mnemonic_ = cast(u16)m; return CheckMnemomic_Mnemonic; } - auto p = g_asm_amd64.prefix_lookup(name); + auto p = asm_ctx->prefix_lookup(name); if (p) { if (mnemonic_) *mnemonic_ = cast(u16)p; return CheckMnemomic_Prefix; @@ -453,15 +444,15 @@ gb_internal CheckMnemomicResult check_mnemonic_name(AstAsmInstruction *instr, u1 error(instr->name, "Unknown mnemonic for this target platform: %.*s", LIT(name)); } - auto dym = did_you_mean_make(heap_allocator(), g_asm_amd64.MNEMONIC_COUNT, name); + auto dym = did_you_mean_make(heap_allocator(), asm_ctx->MNEMONIC_COUNT, name); defer (did_you_mean_destroy(&dym)); - for (u16 i = g_asm_amd64.M_INVALID+1; i < g_asm_amd64.MNEMONIC_COUNT; i++) { - String str = g_asm_amd64.mnemonic_strings[i]; + for (u16 i = asm_ctx->M_INVALID+1; i < asm_ctx->MNEMONIC_COUNT; i++) { + String str = asm_ctx->mnemonic_strings[i]; did_you_mean_append(&dym, str); } if (instr->operands.count == 0) { - for (u16 i = g_asm_amd64.PREFIX_INVALID+1; i < g_asm_amd64.PREFIX_COUNT; i++) { - String str = g_asm_amd64.prefix_strings[i]; + for (u16 i = asm_ctx->PREFIX_INVALID+1; i < asm_ctx->PREFIX_COUNT; i++) { + String str = asm_ctx->prefix_strings[i]; did_you_mean_append(&dym, str); } } @@ -500,10 +491,11 @@ gb_internal AsmOperandKind determine_asm_operand_kind(Operand const *operand) { } -gb_internal void check_mnemonic(CheckerContext *ctx, AstAsmInstruction *instr, u16 mnemonic, Slice const &operands, u8 previous_prefix) { +template +gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, AstAsmInstruction *instr, u16 mnemonic, Slice const &operands, u8 previous_prefix) { GB_ASSERT(mnemonic > 0); - auto forms = g_asm_amd64.encoding_forms(mnemonic); - String name = g_asm_amd64.mnemonic_strings[mnemonic]; + auto forms = asm_ctx->encoding_forms(mnemonic); + String name = asm_ctx->mnemonic_strings[mnemonic]; int min_count = I32_MAX; int max_count = -1; @@ -536,9 +528,10 @@ gb_internal void check_mnemonic(CheckerContext *ctx, AstAsmInstruction *instr, u int score = 0; for_array(i, operands) { - auto type = form.ops[i]; + int slot = asm_ctx->form_explicit_slot(form, cast(int)i); + auto type = (slot >= 0) ? form.ops[slot] : asm_ctx->OP_NONE; Operand const *operand = &operands[i]; - AsmOperandKind dst = g_asm_amd64.kind_from_operand_type(type); + AsmOperandKind dst = asm_ctx->kind_from_operand_type(type); AsmOperandKind src = determine_asm_operand_kind(operand); bool kind_ok = (dst == src) || @@ -550,7 +543,7 @@ gb_internal void check_mnemonic(CheckerContext *ctx, AstAsmInstruction *instr, u if (dst == AsmOperand_Register_Or_Memory && src == AsmOperand_Memory) { // No need to do an extra size class check, it accepts memory } else { - spot_ok = check_asm_operand_size_class(&g_asm_amd64, type, operand, nullptr, nullptr, nullptr); + spot_ok = check_asm_operand_size_class(asm_ctx, type, operand, nullptr, nullptr, nullptr); } } @@ -589,15 +582,15 @@ gb_internal void check_mnemonic(CheckerContext *ctx, AstAsmInstruction *instr, u // failure path enum { MAX_VARIANT_COUNT = 32 }; - String mismatch_reason[MAX_VARIANT_COUNT] = {}; // parallels valid_spots for the best form - i32 want_bits[MAX_VARIANT_COUNT] = {}; - i32 got_bits[MAX_VARIANT_COUNT] = {}; + AsmMismatch mismatch[MAX_VARIANT_COUNT] = {}; // parallels valid_spots for the best form + i32 want_bits[MAX_VARIANT_COUNT] = {}; + i32 got_bits[MAX_VARIANT_COUNT] = {}; if (best_form >= 0) { auto &form = forms[best_form]; for_array(i, operands) { - int slot = asm_form_explicit_slot(form, cast(int)i); - Asm_amd64::OperandType type = (slot >= 0) ? form.ops[slot] : Asm_amd64::OP_NONE; - AsmOperandKind dst = g_asm_amd64.kind_from_operand_type(type); + int slot = asm_ctx->form_explicit_slot(form, cast(int)i); + auto type = (slot >= 0) ? form.ops[slot] : asm_ctx->OP_NONE; + AsmOperandKind dst = asm_ctx->kind_from_operand_type(type); AsmOperandKind src = determine_asm_operand_kind(&operands[i]); possible_kinds[i] = dst; @@ -606,13 +599,13 @@ gb_internal void check_mnemonic(CheckerContext *ctx, AstAsmInstruction *instr, u if (!kind_ok) { valid_spots[i] = false; } else { - String reason = {}; + AsmMismatch m = AsmMismatch_None; i32 wb_ = 0; i32 gb_ = 0; - bool ok = check_asm_operand_size_class(&g_asm_amd64, type, &operands[i], &reason, &wb_, &gb_); + bool ok = check_asm_operand_size_class(asm_ctx, type, &operands[i], &m, &wb_, &gb_); valid_spots[i] = ok; if (!ok && i < MAX_VARIANT_COUNT) { - mismatch_reason[i] = reason; + mismatch[i] = m; want_bits[i] = wb_; got_bits[i] = gb_; } @@ -629,17 +622,14 @@ gb_internal void check_mnemonic(CheckerContext *ctx, AstAsmInstruction *instr, u auto dst = possible_kinds[i]; AsmOperandKind src = determine_asm_operand_kind(&operands[i]); - String reason = {}; - if (i < MAX_VARIANT_COUNT) { - reason = mismatch_reason[i]; - } + AsmMismatch m = (i < MAX_VARIANT_COUNT) ? mismatch[i] : AsmMismatch_None; - if (reason == "size" && want_bits[i] && got_bits[i]) { + if (m == AsmMismatch_Size && want_bits[i] && got_bits[i]) { // dst kind was right, width was wrong error(operands[i].expr, "Operand %td of '%.*s' has the wrong size: expected a %u-bit operand, got %u-bit", i, LIT(name), cast(unsigned)want_bits[i], cast(unsigned)got_bits[i]); - } else if (reason == "register class") { + } else if (m == AsmMismatch_Class) { error(operands[i].expr, "Operand %td of '%.*s' is in the wrong register class, expected %.*s operand, got %.*s", i, LIT(name), LIT(asm_operand_kind_expected_strings[dst]), LIT(asm_operand_kind_expected_strings[src])); @@ -655,8 +645,8 @@ gb_internal void check_mnemonic(CheckerContext *ctx, AstAsmInstruction *instr, u } - -gb_internal void check_asm_instruction_operand(CheckerContext *ctx, Entity *entity, Operand *operand, Ast *expr, bool allow_memory_operands) { +template +gb_internal void check_asm_instruction_operand(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *entity, Operand *operand, Ast *expr, bool allow_memory_operands) { if (expr == nullptr) { return; } @@ -690,7 +680,7 @@ gb_internal void check_asm_instruction_operand(CheckerContext *ctx, Entity *enti return; case_end; case_ast_node(asm_reg, AsmRegister, expr); - check_register(operand, asm_reg); + check_register(asm_ctx, operand, asm_reg); return; case_end; case_ast_node(mem_op, AsmMemoryOperand, expr); @@ -705,14 +695,14 @@ gb_internal void check_asm_instruction_operand(CheckerContext *ctx, Entity *enti Operand index = {}; Operand scale = {}; Operand disp = {}; - check_asm_instruction_operand(ctx, entity, &base, mem_op->base, false); - check_asm_instruction_operand(ctx, entity, &index, mem_op->index, false); - check_asm_instruction_operand(ctx, entity, &scale, mem_op->scale, false); - check_asm_instruction_operand(ctx, entity, &disp, mem_op->disp, false); + check_asm_instruction_operand(asm_ctx, ctx, entity, &base, mem_op->base, false); + check_asm_instruction_operand(asm_ctx, ctx, entity, &index, mem_op->index, false); + check_asm_instruction_operand(asm_ctx, ctx, entity, &scale, mem_op->scale, false); + check_asm_instruction_operand(asm_ctx, ctx, entity, &disp, mem_op->disp, false); for (int i = 0; base.expr && i == 0; i++) { if (base.expr->kind == Ast_AsmRegister) { - check_register(&base, &base.expr->AsmRegister); + check_register(asm_ctx, &base, &base.expr->AsmRegister); } else { Entity *param_entity = entity_of_node(base.expr); if (param_entity == nullptr || param_entity->kind != Entity_Variable) { @@ -733,7 +723,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(&index, &index.expr->AsmRegister); + check_register(asm_ctx, &index, &index.expr->AsmRegister); } else { Entity *param_entity = entity_of_node(index.expr); if (param_entity == nullptr || param_entity->kind != Entity_Variable) { @@ -793,7 +783,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(&disp, &disp.expr->AsmRegister); + check_register(asm_ctx, &disp, &disp.expr->AsmRegister); } else { Entity *param_entity = entity_of_node(disp.expr); if (disp.mode == Addressing_Constant) { @@ -850,7 +840,8 @@ gb_internal void check_asm_instruction_operand(CheckerContext *ctx, Entity *enti } -gb_internal void check_asm_template(CheckerContext *ctx, Entity *entity, DeclInfo *d) { +template +gb_internal void check_asm_template(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *entity, DeclInfo *d) { GB_ASSERT(entity->kind == Entity_AsmTemplate); auto *ate = &entity->AsmTemplate; @@ -894,7 +885,7 @@ gb_internal void check_asm_template(CheckerContext *ctx, Entity *entity, DeclInf case_ast_node(asm_reg, AsmRegister, clobber->value) String reg = asm_reg->name.string; Operand operand = {}; - if (check_register(&operand, asm_reg)) { + if (check_register(asm_ctx, &operand, asm_reg)) { if (string_set_update(®_set, reg)) { error(clobber->value, "#clobber %%%.*s has already been defined", LIT(reg)); } @@ -962,14 +953,14 @@ gb_internal void check_asm_template(CheckerContext *ctx, Entity *entity, DeclInf GB_ASSERT(instr->name->kind == Ast_Ident); u16 mnemonic = 0; - CheckMnemomicResult res = check_mnemonic_name(instr, &mnemonic); + CheckMnemomicResult res = check_mnemonic_name(asm_ctx, instr, &mnemonic); array_clear(&operands); for (Ast *expr : instr->operands) { Operand operand = {}; - check_asm_instruction_operand(ctx, entity, &operand, expr, /*allow_memory_operands*/true); + check_asm_instruction_operand(asm_ctx, ctx, entity, &operand, expr, /*allow_memory_operands*/true); array_add(&operands, operand); } if (res == CheckMnemomic_Prefix) { @@ -978,7 +969,7 @@ gb_internal void check_asm_template(CheckerContext *ctx, Entity *entity, DeclInf } previous_prefix = cast(u8)mnemonic; } else if (res == CheckMnemomic_Mnemonic) { - check_mnemonic(ctx, instr, mnemonic, slice_from_array(operands), previous_prefix); + check_mnemonic(asm_ctx, ctx, instr, mnemonic, slice_from_array(operands), previous_prefix); } case_end; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index c70672036..520aa5e5d 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -2065,7 +2065,11 @@ gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, break; case Entity_AsmTemplate: - check_asm_template(&c, e, d); + if (build_context.metrics.arch == TargetArch_amd64) { + check_asm_template(&g_asm_amd64, &c, e, d); + } else { + error(e->token, "asm templates are not currently supported for this target"); + } break; } diff --git a/src/main.cpp b/src/main.cpp index aeefd09b6..14d526043 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4314,9 +4314,7 @@ int main(int arg_count, char const **arg_ptr) { bool failed_to_cache_parsing = false; TIME_SECTION("init asm tables"); - { - g_asm_amd64.init(); - } + init_asm_tables(); MAIN_TIME_SECTION("parse files");