Add extra checks for asm memory operand scale in frontend and backend

This commit is contained in:
gingerBill
2026-08-11 16:00:03 +01:00
parent a76664ed2a
commit 6a20d21358
2 changed files with 58 additions and 34 deletions

View File

@@ -740,10 +740,10 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, AstAsmInst
}
{
if (best_score == operands.count*2 - 1) {
error(instr->name, "Asm operands to '%.*s' nearly matched the expected encoding forms", LIT(name));
if (best_score >= gb_max(operands.count*2 - 2, 0)) {
error(instr->name, "'%.*s' operands nearly matched the expected encoding forms", LIT(name));
} else {
error(instr->name, "Asm operands to '%.*s' matched none of the expected encoding forms", LIT(name));
error(instr->name, "'%.*s' operands matched none of the expected encoding forms", LIT(name));
}
for_array(i, valid_spots) {
if (valid_spots[i] || i >= operands.count) {
@@ -760,33 +760,27 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, AstAsmInst
i32 bits_required = 0;
check_asm_immediate_value_fits(ev, want_bits[i], &bits_required, nullptr);
if (bits_required > 0) {
error(operands[i].expr,
"Operand %td of '%.*s' is a %d-bit immediate value, but the value %s does not fit in the %d-bit immediate this form encodes",
i, LIT(name), bits_required, vs, cast(int)want_bits[i]);
error(operands[i].expr, "'%.*s' operand-%td is a %d-bit immediate value, but the value %s does not fit in the %d-bit immediate this form encodes",
LIT(name), i, bits_required, vs, cast(int)want_bits[i]);
} else {
error(operands[i].expr,
"Operand %td of '%.*s' is an immediate value, but the value %s does not fit in the %d-bit immediate this form encodes",
i, LIT(name), vs, cast(int)want_bits[i]);
error(operands[i].expr, "'%.*s' operand-%td is an immediate value, but the value %s does not fit in the %d-bit immediate this form encodes",
LIT(name), i, vs, cast(int)want_bits[i]);
}
gb_string_free(vs);
} else if (m == AsmMismatch_ImmType) {
error(operands[i].expr,
"Operand %td of '%.*s': a floating-point constant cannot be used as an immediate",
i, LIT(name));
error(operands[i].expr, "'%.*s'' operand-%td a floating-point constant cannot be used as an immediate",
LIT(name), i);
} else if (m == AsmMismatch_Size && want_bits[i] && got_bits[i]) {
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]);
error(operands[i].expr, "'%.*s' operand-%td has the wrong size: expected a %u-bit operand, got %u-bit",
LIT(name), i, cast(unsigned)want_bits[i], cast(unsigned)got_bits[i]);
} 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]));
error(operands[i].expr, "'%.*s' operand-%td is in the wrong register class, expected %.*s operand, got %.*s",
LIT(name), i, LIT(asm_operand_kind_expected_strings[dst]), LIT(asm_operand_kind_expected_strings[src]));
} else if (dst) {
error(operands[i].expr,
"Operand %td of '%.*s' has an invalid kind, expected %.*s operand",
i, LIT(name), LIT(asm_operand_kind_expected_strings[dst]));
error(operands[i].expr, "'%.*s' operand-%td has an invalid kind, expected %.*s operand",
LIT(name), i, LIT(asm_operand_kind_expected_strings[dst]));
} else {
error(operands[i].expr, "Operand %td of '%.*s' has an invalid kind", i, LIT(name));
error(operands[i].expr, "'%.*s' operand-%td has an invalid kind", LIT(name), i);
}
}
}
@@ -905,11 +899,21 @@ gb_internal void check_asm_instruction_operand(AsmCtx *asm_ctx, CheckerContext *
break;
}
if (scale.mode == Addressing_Constant) {
gbString s = exact_value_to_string(scale.value);
defer (gb_string_free(s));
if (scale.value.kind != ExactValue_Integer) {
gbString s = exact_value_to_string(scale.value);
error(scale.expr, "A scale must be a constant integer or an immediate, got %s", s);
gb_string_free(s);
break;
} else {
i64 v = exact_value_to_i64(scale.value);
switch (v) {
case 1: case 2: case 4: case 8:
// okay
break;
default:
error(scale.expr, "A scale must be a constant integer or an immediate with the value 1, 2, 4, or 8, got %s", s);
break;
}
}
} else {
Entity *param_entity = entity_of_node(scale.expr);

View File

@@ -3,6 +3,14 @@ struct lbAsmGenerate {
AstAsmTemplate * tmpl_node;
Array<AsmTemplateEntityDecl> *ops;
enum WriteOperandFlags : u32 {
WriteOperandFlag_PrintPrefixes = 1<<0,
WriteOperandFlag_IsScale = 1<<1,
WriteOperandFlag_NONE = 0,
WriteOperandFlag_DEFAULT = WriteOperandFlag_PrintPrefixes,
};
void init(Entity *entity) {
this->tmpl_entity = entity;
GB_ASSERT(this->tmpl_entity != nullptr);
@@ -34,7 +42,7 @@ struct lbAsmGenerate {
return nullptr;
}
gbString write_operand(gbString asm_string, Array<i32> op_number, Ast *op, bool print_prefixes=true) {
gbString write_operand(gbString asm_string, Array<i32> op_number, Ast *op, u32 flags) {
switch (op->kind) {
case_ast_node(i, Ident, op);
Entity *e = entity_of_node(op);
@@ -45,7 +53,7 @@ struct lbAsmGenerate {
asm_string = gb_string_append_fmt(asm_string, "$%d", idx);
case_end;
case_ast_node(mem_op, AsmMemoryOperand, op);
asm_string = this->write_memory_operand(asm_string, op_number, mem_op, false);
asm_string = this->write_memory_operand(asm_string, op_number, mem_op, flags&~WriteOperandFlag_PrintPrefixes);
case_end;
case_ast_node(bl, BasicLit, op);
@@ -55,11 +63,23 @@ struct lbAsmGenerate {
switch (ev.kind) {
case ExactValue_Integer: {
String s = big_int_to_string(heap_allocator(), &ev.value_integer, 10);
if (print_prefixes) {
if (flags & WriteOperandFlag_PrintPrefixes) {
asm_string = gb_string_appendc(asm_string, "$$");
}
asm_string = gb_string_append_length(asm_string, s.text, s.len);
gb_free(heap_allocator(), s.text);
if (flags & WriteOperandFlag_IsScale) {
i64 val = exact_value_to_i64(ev);
switch (val) {
case 1: case 2: case 4: case 8:
// okay
break;
default:
error(op, "A scale must be a constant integer or an immediate with the value 1, 2, 4, or 8, got %.*s", LIT(s));
break;
}
}
break;
}
case ExactValue_Float:
@@ -100,24 +120,24 @@ struct lbAsmGenerate {
};
virtual gbString write_memory_operand(gbString asm_string, Array<i32> const &op_number, AstAsmMemoryOperand *mem_op, bool print_prefixes=true) = 0;
virtual gbString write_memory_operand(gbString asm_string, Array<i32> const &op_number, AstAsmMemoryOperand *mem_op, u32 flags) = 0;
virtual lbValue emit_call(lbProcedure *p, Array<lbValue> const &args) = 0;
};
struct lbAsmGenerate_amd64 : lbAsmGenerate {
gbString write_memory_operand(gbString asm_string, Array<i32> const &op_number, AstAsmMemoryOperand *mem_op, bool print_prefixes=true) override {
gbString write_memory_operand(gbString asm_string, Array<i32> const &op_number, AstAsmMemoryOperand *mem_op, u32 flags) override {
if (mem_op->disp) {
asm_string = this->write_operand(asm_string, op_number, mem_op->disp, /*print_prefixes*/false);
asm_string = this->write_operand(asm_string, op_number, mem_op->disp, flags&~WriteOperandFlag_PrintPrefixes);
}
asm_string = gb_string_appendc(asm_string, "(");
GB_ASSERT(mem_op->base != nullptr);
asm_string = this->write_operand(asm_string, op_number, mem_op->base);
asm_string = this->write_operand(asm_string, op_number, mem_op->base, flags);
if (mem_op->index) {
asm_string = gb_string_appendc(asm_string, ",");
asm_string = this->write_operand(asm_string, op_number, mem_op->index);
asm_string = this->write_operand(asm_string, op_number, mem_op->index, flags);
if (mem_op->scale) {
asm_string = gb_string_appendc(asm_string, ",");
asm_string = this->write_operand(asm_string, op_number, mem_op->scale, /*print_prefixes*/false);
asm_string = this->write_operand(asm_string, op_number, mem_op->scale, (flags|WriteOperandFlag_IsScale)&~WriteOperandFlag_PrintPrefixes);
}
}
asm_string = gb_string_appendc(asm_string, ")");
@@ -273,7 +293,7 @@ struct lbAsmGenerate_amd64 : lbAsmGenerate {
if (j < instr->operands.count-1) {
asm_string = gb_string_appendc(asm_string, ", ");
}
asm_string = this->write_operand(asm_string, op_number, op);
asm_string = this->write_operand(asm_string, op_number, op, WriteOperandFlag_DEFAULT);
}
case_end;
case_ast_node(label, AsmLabelDecl, instr_);