mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-25 06:21:35 +00:00
Verify the asm instruction operands against the Encoding.ops
This commit is contained in:
@@ -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,
|
||||
|
||||
10
src/asm_tables.cpp
Normal file
10
src/asm_tables.cpp
Normal file
@@ -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"
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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<Operand> 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<Operand> 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<bool>(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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user