Check asm instruction operand count

This commit is contained in:
gingerBill
2026-08-11 11:11:53 +01:00
parent 70ca5c0936
commit 5ca7638bb2
3 changed files with 77 additions and 30 deletions

View File

@@ -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")
}
{

View File

@@ -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(&register_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 {

View File

@@ -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(&reg_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;