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 8418b99b4..bd60c3f18 100644 --- a/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin +++ b/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin @@ -140,7 +140,7 @@ main :: proc() { { strings.write_string(&sb, "\tenum OperandEncoding : u8 {\n") defer strings.write_string(&sb, "\t};\n") - for op in type_of(gen.Encoding{}.enc[0]) { + for op in Operand_Encoding { fmt.sbprintf(&sb, "\t\tENC_%s,\n", op) } @@ -279,7 +279,41 @@ main :: proc() { strings.write_string(&sb, "\t\treturn 0;\n") strings.write_string(&sb, "\t}\n") } - + { + strings.write_string(&sb, "\tAsmOperandKind kind_from_operand_type(OperandType type) {\n") + strings.write_string(&sb, "\t\tswitch (type) {\n") + strings.write_string(&sb, "\t\tcase OP_R8: case OP_R16: case OP_R32: case OP_R64:\n") + strings.write_string(&sb, "\t\tcase OP_SREG: case OP_CR: case OP_DR:\n") + strings.write_string(&sb, "\t\tcase OP_XMM: case OP_YMM: case OP_ZMM:\n") + strings.write_string(&sb, "\t\tcase OP_MM: case OP_K: case OP_STI:\n") + strings.write_string(&sb, "\t\tcase OP_AL_IMPL: case OP_AX_IMPL: case OP_EAX_IMPL: case OP_RAX_IMPL:\n") + strings.write_string(&sb, "\t\tcase OP_CL_IMPL: case OP_DX_IMPL:\n") + strings.write_string(&sb, "\t\tcase OP_ST0_IMPL: case OP_XMM0_IMPL:\n") + strings.write_string(&sb, "\t\t\treturn AsmOperand_Register;\n") + strings.write_string(&sb, "\t\tcase OP_RM8: case OP_RM16: case OP_RM32: case OP_RM64:\n") + strings.write_string(&sb, "\t\tcase OP_XMM_M32: case OP_XMM_M64: case OP_XMM_M128:\n") + strings.write_string(&sb, "\t\tcase OP_YMM_M256: case OP_ZMM_M512:\n") + strings.write_string(&sb, "\t\tcase OP_MM_M64:\n") + strings.write_string(&sb, "\t\tcase OP_K_M8: case OP_K_M16: case OP_K_M32: case OP_K_M64:\n") + strings.write_string(&sb, "\t\t\treturn AsmOperand_Register_Or_Memory;\n") + strings.write_string(&sb, "\t\tcase OP_M: case OP_M8: case OP_M16: case OP_M32: case OP_M64:\n") + strings.write_string(&sb, "\t\tcase OP_M80: case OP_M128: case OP_M256: case OP_M512:\n") + strings.write_string(&sb, "\t\tcase OP_MOFFS8: case OP_MOFFS16: case OP_MOFFS32: case OP_MOFFS64:\n") + strings.write_string(&sb, "\t\tcase OP_M16_16: case OP_M16_32: case OP_M16_64:\n") + strings.write_string(&sb, "\t\t\treturn AsmOperand_Memory;\n") + strings.write_string(&sb, "\t\tcase OP_IMM8: case OP_IMM16: case OP_IMM32: case OP_IMM64:\n") + strings.write_string(&sb, "\t\tcase OP_IMM8SX:\n") + strings.write_string(&sb, "\t\tcase OP_ONE_IMPL:\n") + strings.write_string(&sb, "\t\tcase OP_PTR16_16: case OP_PTR16_32: case OP_PTR16_64:\n") + strings.write_string(&sb, "\t\t\treturn AsmOperand_Immediate;\n") + strings.write_string(&sb, "\t\tcase OP_REL8: case OP_REL32:\n") + strings.write_string(&sb, "\t\t\treturn AsmOperand_Label;\n") + strings.write_string(&sb, "\t\tcase OP_NONE:\n") + strings.write_string(&sb, "\t\tdefault:\n") + strings.write_string(&sb, "\t\t\treturn AsmOperand_Invalid;\n") + strings.write_string(&sb, "\t\t}\n") + strings.write_string(&sb, "\t}\n") + } strings.write_string(&sb, "};\n") @@ -447,6 +481,35 @@ main :: proc() { } } +Operand_Encoding :: type_of(gen.Encoding{}.enc[0]) + +Asm_Operand_Kind :: enum u8 { + Invalid, + Register, + Memory, + Register_Or_Memory, + Immediate, + Label, +} + + +@(rodata) +operand_encoding_to_kind := [Operand_Encoding]Asm_Operand_Kind{ + .NONE = .Invalid, + .MR = .Register_Or_Memory, + .REG = .Register, + .VVVV = .Register, + .OP_R = .Register, + .IS4 = .Register, + .AAA = .Register, + .IMPL = .Register, + .IB = .Immediate, + .IW = .Immediate, + .ID = .Immediate, + .IQ = .Immediate, +} + + Prefix :: enum u8 { INVALID, ES, diff --git a/src/asm_tables.cpp b/src/asm_tables.cpp new file mode 100644 index 000000000..335efe4d9 --- /dev/null +++ b/src/asm_tables.cpp @@ -0,0 +1,10 @@ +enum AsmOperandKind : u8 { + AsmOperand_Invalid, + AsmOperand_Register, + AsmOperand_Memory, + AsmOperand_Register_Or_Memory, + AsmOperand_Immediate, + AsmOperand_Label, +}; + +#include "asm_tables_amd64.cpp" \ No newline at end of file diff --git a/src/asm_tables_amd64.cpp b/src/asm_tables_amd64.cpp index 2c8848ba0..5d236e91f 100644 --- a/src/asm_tables_amd64.cpp +++ b/src/asm_tables_amd64.cpp @@ -304,6 +304,39 @@ struct Asm_amd64 { } return 0; } + AsmOperandKind kind_from_operand_type(OperandType type) { + switch (type) { + case OP_R8: case OP_R16: case OP_R32: case OP_R64: + case OP_SREG: case OP_CR: case OP_DR: + case OP_XMM: case OP_YMM: case OP_ZMM: + case OP_MM: case OP_K: case OP_STI: + case OP_AL_IMPL: case OP_AX_IMPL: case OP_EAX_IMPL: case OP_RAX_IMPL: + case OP_CL_IMPL: case OP_DX_IMPL: + case OP_ST0_IMPL: case OP_XMM0_IMPL: + return AsmOperand_Register; + case OP_RM8: case OP_RM16: case OP_RM32: case OP_RM64: + case OP_XMM_M32: case OP_XMM_M64: case OP_XMM_M128: + case OP_YMM_M256: case OP_ZMM_M512: + case OP_MM_M64: + case OP_K_M8: case OP_K_M16: case OP_K_M32: case OP_K_M64: + return AsmOperand_Register_Or_Memory; + case OP_M: case OP_M8: case OP_M16: case OP_M32: case OP_M64: + case OP_M80: case OP_M128: case OP_M256: case OP_M512: + case OP_MOFFS8: case OP_MOFFS16: case OP_MOFFS32: case OP_MOFFS64: + case OP_M16_16: case OP_M16_32: case OP_M16_64: + return AsmOperand_Memory; + case OP_IMM8: case OP_IMM16: case OP_IMM32: case OP_IMM64: + case OP_IMM8SX: + case OP_ONE_IMPL: + case OP_PTR16_16: case OP_PTR16_32: case OP_PTR16_64: + return AsmOperand_Immediate; + case OP_REL8: case OP_REL32: + return AsmOperand_Label; + case OP_NONE: + default: + return AsmOperand_Invalid; + } + } }; diff --git a/src/check_asm.cpp b/src/check_asm.cpp index 3b73d5114..6e0891793 100644 --- a/src/check_asm.cpp +++ b/src/check_asm.cpp @@ -315,6 +315,7 @@ gb_internal CheckMnemomicResult check_mnemonic_name(AstAsmInstruction *instr, u1 } auto p = g_asm_amd64.prefix_lookup(name); if (p) { + if (mnemonic_) *mnemonic_ = cast(u16)p; return CheckMnemomic_Prefix; } @@ -326,7 +327,38 @@ gb_internal CheckMnemomicResult check_mnemonic_name(AstAsmInstruction *instr, u1 return CheckMnemomic_Invalid; } -gb_internal void check_mnemonic(CheckerContext *ctx, AstAsmInstruction *instr, u16 mnemonic, Slice const &operands) { +gb_internal AsmOperandKind determine_asm_operand_kind(Operand const *operand) { + if (operand->mode == Addressing_Constant) { + return AsmOperand_Immediate; + } + Ast *expr = operand->expr; + switch (expr->kind) { + case_ast_node(label, AsmLabelDecl, expr); + return AsmOperand_Label; + case_end; + case_ast_node(reg, AsmRegister, expr); + return AsmOperand_Register; + case_end; + case_ast_node(reg, AsmMemoryOperand, expr); + return AsmOperand_Memory; + case_end; + case_ast_node(ident, Ident, expr); + // TODO(bill): Is this correct? + if (expr->tav.mode == Addressing_Constant) { + return AsmOperand_Immediate; + } + Entity *e = entity_of_node(expr); + if (e != nullptr && e->kind == Entity_Variable && (e->flags & EntityFlag_PolyConst) != 0) { + return AsmOperand_Immediate; + } + return AsmOperand_Register; + case_end; + } + return AsmOperand_Invalid; +} + + +gb_internal void check_mnemonic(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]; @@ -334,8 +366,20 @@ gb_internal void check_mnemonic(CheckerContext *ctx, AstAsmInstruction *instr, u 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); + } + min_count = gb_max(min_count, 0); + max_count = gb_max(max_count, 0); + isize valid_form_index = -1; + + auto valid_spots = slice_make(heap_allocator(), max_count); + defer (slice_free(&valid_spots, heap_allocator())); + + bool ok = true; for (auto form : forms) { int explicit_count = cast(int)form.explicit_count(); min_count = gb_min(min_count, explicit_count); @@ -343,7 +387,29 @@ gb_internal void check_mnemonic(CheckerContext *ctx, AstAsmInstruction *instr, u if (operands.count != explicit_count) { continue; } - ok = true; // pretend for the time being + + ok = true; + for_array(i, operands) { + auto type = form.ops[i]; + Operand const *operand = &operands[i]; + AsmOperandKind dst_kind = g_asm_amd64.kind_from_operand_type(type); + AsmOperandKind src_kind = determine_asm_operand_kind(operand); + if (dst_kind == src_kind) { + valid_spots[i] = true; + continue; + } + if (dst_kind == AsmOperand_Register_Or_Memory && + (src_kind == AsmOperand_Register || src_kind == AsmOperand_Memory)) { + valid_spots[i] = true; + continue; + } + ok = false; + break; + } + if (ok) { + // the result has been found to be correct + break; + } } if (operands.count < min_count || operands.count > max_count) { @@ -355,10 +421,21 @@ gb_internal void check_mnemonic(CheckerContext *ctx, AstAsmInstruction *instr, u return; } if (ok) { + if (valid_form_index >= 0 && previous_prefix > 0) { + // TODO(bill): validate the prefix for the selected form + } + return; } - error(instr->name, "The operands to '%.*s' matched non of the expected encoding forms", LIT(name)); + { + error(instr->name, "The operands to '%.*s' matched non of the expected encoding forms", LIT(name)); + for_array(i, valid_spots) { + if (!valid_spots[i] && i < operands.count) { + error(operands[i].expr, "Invalid operand kind for the asm instruction '%.*s'", LIT(name)); + } + } + } } @@ -657,6 +734,8 @@ gb_internal void check_asm_template(CheckerContext *ctx, Entity *entity, DeclInf defer (array_free(&operands)); for (Ast *instruction_ : at->instructions) { + u8 previous_prefix = 0; + switch (instruction_->kind) { case_ast_node(instr, AsmInstruction, instruction_); GB_ASSERT(instr->name->kind == Ast_Ident); @@ -676,8 +755,9 @@ gb_internal void check_asm_template(CheckerContext *ctx, Entity *entity, DeclInf if (instr->operands.count != 0) { error(instr->name, "A prefix must not have any operands, and be separate from the instruction it is prefixing"); } + previous_prefix = cast(u8)mnemonic; } else if (res == CheckMnemomic_Mnemonic) { - check_mnemonic(ctx, instr, mnemonic, slice_from_array(operands)); + check_mnemonic(ctx, instr, mnemonic, slice_from_array(operands), previous_prefix); } case_end; diff --git a/src/main.cpp b/src/main.cpp index 6afaf08df..aeefd09b6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -68,7 +68,7 @@ gb_global Timings global_timings = {0}; #include "parser.hpp" #include "checker.hpp" -#include "asm_tables_amd64.cpp" +#include "asm_tables.cpp" #include "parser.cpp" #include "checker.cpp"