mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-14 01:34:35 +00:00
Check asm instruction operand count
This commit is contained in:
@@ -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> mnemonic_map;\n")
|
||||
strings.write_string(&sb, "\tStringMap<Prefix> prefix_map;\n")
|
||||
strings.write_string(&sb, "\tStringMap<Register> register_map;\n")
|
||||
strings.write_string(&sb, "\tSlice<EncodeRun> ENCODE_RUNS;\n")
|
||||
strings.write_string(&sb, "\tSlice<Encoding> 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> encoding_forms(Mnemonic m) const {\n")
|
||||
strings.write_string(&sb, "\tSlice<Encoding> 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<Encoding>{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")
|
||||
}
|
||||
{
|
||||
|
||||
@@ -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> mnemonic_map;
|
||||
StringMap<Prefix> prefix_map;
|
||||
StringMap<Register> register_map;
|
||||
Slice<EncodeRun> ENCODE_RUNS;
|
||||
Slice<Encoding> 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> encoding_forms(Mnemonic m) const {
|
||||
EncodeRun r = ENCODE_RUNS[m];
|
||||
return slice_lower_and_count(ENCODE_FORMS, r.start, r.count);
|
||||
Slice<Encoding> encoding_forms(/*Mnemonic*/ u16 m) const {
|
||||
EncodeRun r = raw_encode_runs[m];
|
||||
Encoding *ENCODE_FORMS = cast(Encoding *)raw_encode_forms;
|
||||
return Slice<Encoding>{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 {
|
||||
|
||||
@@ -290,7 +290,7 @@ gb_internal void check_asm_specs(CheckerContext *ctx, Scope *scope, Slice<Ast *>
|
||||
}
|
||||
}
|
||||
|
||||
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<Operand> 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<Operand> 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;
|
||||
|
||||
Reference in New Issue
Block a user