mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-18 03:12:10 +00:00
Check memory operand parameters better
This commit is contained in:
@@ -133,9 +133,6 @@ gb_internal bool check_asm_operand_size_class(AsmCtx *asm_ctx, typename AsmCtx::
|
||||
AsmOperandKind slot_kind = asm_ctx->kind_from_operand_type(slot);
|
||||
if (slot_kind == AsmOperand_Immediate) {
|
||||
i32 want_w = asm_ctx->operand_type_bit_width(slot); // 32 for OP_IMM32
|
||||
if (want_w == 0) {
|
||||
GB_PANIC("HERE: %d", slot);
|
||||
}
|
||||
if (want_bits_) *want_bits_ = want_w;
|
||||
if (operand->mode != Addressing_Constant) {
|
||||
return true; // $-immediate, bound per instantiation; defer
|
||||
@@ -215,6 +212,47 @@ gb_internal bool check_asm_operand_size_class(AsmCtx *asm_ctx, typename AsmCtx::
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
enum AsmAddrRole {
|
||||
AsmAddr_Base,
|
||||
AsmAddr_Index,
|
||||
};
|
||||
|
||||
// Validate that a resolved base/index operand is a 32- or 64-bit integer register.
|
||||
// `reg_name` is the literal register string when the operand was an AstAsmRegister
|
||||
// (so rsp/esp-as-index can be caught), else the empty string.
|
||||
gb_internal bool check_asm_addr_register(Operand const *operand, AsmAddrRole role, String reg_name, i32 *width_) {
|
||||
char const *role_name = (role == AsmAddr_Base) ? "base" : "index";
|
||||
|
||||
AsmRegClass cls = check_asm_reg_class_from_type(operand->type);
|
||||
i32 w = check_asm_operand_bit_width(operand->type);
|
||||
if (width_) *width_ = w;
|
||||
|
||||
if (cls != AsmRegClass_Integer) {
|
||||
char const *got = "non-integer";
|
||||
if (cls == AsmRegClass_Vector) {
|
||||
got = "vector";
|
||||
} else if (cls == AsmRegClass_Mask) {
|
||||
got = "mask";
|
||||
}
|
||||
error(operand->expr, "A memory operand's %s must be an integer register, got a %s value", role_name, got);
|
||||
return false;
|
||||
}
|
||||
if (w != 32 && w != 64) {
|
||||
error(operand->expr, "A memory operand's %s must be a 32-bit or 64-bit register, got a %d-bit register", role_name, cast(int)w);
|
||||
return false;
|
||||
}
|
||||
if (role == AsmAddr_Index && reg_name.len != 0) {
|
||||
// rsp/esp cannot be encoded as an index register.
|
||||
if (reg_name == "rsp" || reg_name == "esp") {
|
||||
error(operand->expr, "%%%.*s cannot be used as an index register", LIT(reg_name));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
gb_internal Type *check_asm_template_signature_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool input_parameters, Array<AsmTemplateEntityDecl> *asm_template_entity_decls) {
|
||||
Type *tuple = alloc_type_tuple();
|
||||
if (_params == nullptr) {
|
||||
@@ -375,7 +413,7 @@ gb_internal void check_asm_specs(CheckerContext *ctx, Scope *scope, Slice<Ast *>
|
||||
Type *type = check_type(ctx, spec->type);
|
||||
if (!is_valid_asm_parameter_type(type)) {
|
||||
gbString s = type_to_string(type);
|
||||
error(spec->type, "Invalid type for an asm template. It must be an integer, float, boolean, pointer, multi-pointer, or #simd vector, got '%s'", type);
|
||||
error(spec->type, "Invalid type for an asm template. It must be an integer, float, boolean, pointer, multi-pointer, or #simd vector, got '%s'", s);
|
||||
gb_string_free(s);
|
||||
continue;
|
||||
}
|
||||
@@ -768,7 +806,7 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, AstAsmInst
|
||||
}
|
||||
gb_string_free(vs);
|
||||
} else if (m == AsmMismatch_ImmType) {
|
||||
error(operands[i].expr, "'%.*s'' operand-%td a floating-point constant cannot be used as an immediate",
|
||||
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, "'%.*s' operand-%td has the wrong size: expected a %u-bit operand, got %u-bit",
|
||||
@@ -842,55 +880,92 @@ gb_internal void check_asm_instruction_operand(AsmCtx *asm_ctx, CheckerContext *
|
||||
check_asm_instruction_operand(asm_ctx, ctx, entity, &scale, mem_op->scale, false);
|
||||
check_asm_instruction_operand(asm_ctx, ctx, entity, &disp, mem_op->disp, false);
|
||||
|
||||
for (int i = 0; base.expr && i == 0; i++) {
|
||||
i32 base_w = 0;
|
||||
i32 index_w = 0;
|
||||
bool have_base = false;
|
||||
bool have_index = false;
|
||||
|
||||
// base: must resolve to a 32/64-bit integer register
|
||||
if (base.expr) {
|
||||
String reg_name = {};
|
||||
bool ok_kind = true;
|
||||
if (base.expr->kind == Ast_AsmRegister) {
|
||||
check_register(asm_ctx, &base, &base.expr->AsmRegister);
|
||||
reg_name = base.expr->AsmRegister.name.string;
|
||||
ok_kind = check_register(asm_ctx, &base, &base.expr->AsmRegister);
|
||||
} else {
|
||||
Entity *param_entity = entity_of_node(base.expr);
|
||||
if (param_entity == nullptr || param_entity->kind != Entity_Variable) {
|
||||
gbString s = expr_to_string(base.expr);
|
||||
error(base.expr, "A base value must be a memory parameter, got %s", s);
|
||||
error(base.expr, "A base value must be a register parameter, got %s", s);
|
||||
gb_string_free(s);
|
||||
break;
|
||||
}
|
||||
auto kind = check_asm_find_kind(param_entity, ate->decls);
|
||||
if (kind != AsmTemplateEntityDecl_Memory) {
|
||||
gbString s = expr_to_string(base.expr);
|
||||
error(base.expr, "A base value must be a memory parameter, got %s", s);
|
||||
gb_string_free(s);
|
||||
break;
|
||||
ok_kind = false;
|
||||
} else {
|
||||
auto kind = check_asm_find_kind(param_entity, ate->decls);
|
||||
// A pointer/integer parameter used as an address base lowers to a
|
||||
// register operand, so accept both Register and Memory kinds here.
|
||||
if (kind != AsmTemplateEntityDecl_Register && kind != AsmTemplateEntityDecl_Memory) {
|
||||
gbString s = expr_to_string(base.expr);
|
||||
error(base.expr, "A base value must be a register parameter, got %s", s);
|
||||
gb_string_free(s);
|
||||
ok_kind = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ok_kind) {
|
||||
have_base = check_asm_addr_register(&base, AsmAddr_Base, reg_name, &base_w);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; index.expr && i == 0; i++) {
|
||||
// index: must resolve to a 32/64-bit integer register, and not rsp/esp
|
||||
if (index.expr) {
|
||||
String reg_name = {};
|
||||
bool ok_kind = true;
|
||||
if (index.expr->kind == Ast_AsmRegister) {
|
||||
check_register(asm_ctx, &index, &index.expr->AsmRegister);
|
||||
reg_name = index.expr->AsmRegister.name.string;
|
||||
ok_kind = check_register(asm_ctx, &index, &index.expr->AsmRegister);
|
||||
} else {
|
||||
Entity *param_entity = entity_of_node(index.expr);
|
||||
if (param_entity == nullptr || param_entity->kind != Entity_Variable) {
|
||||
gbString s = expr_to_string(index.expr);
|
||||
error(index.expr, "An index value must an integer, got %s", s);
|
||||
error(index.expr, "An index value must be an integer register, got %s", s);
|
||||
gb_string_free(s);
|
||||
break;
|
||||
}
|
||||
auto kind = check_asm_find_kind(param_entity, ate->decls);
|
||||
switch (kind) {
|
||||
case AsmTemplateEntityDecl_Register:
|
||||
case AsmTemplateEntityDecl_Immediate:
|
||||
// okay:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
gbString s = expr_to_string(index.expr);
|
||||
error(index.expr, "An index must be an integer value, got %s", s);
|
||||
gb_string_free(s);
|
||||
ok_kind = false;
|
||||
} else {
|
||||
auto kind = check_asm_find_kind(param_entity, ate->decls);
|
||||
switch (kind) {
|
||||
case AsmTemplateEntityDecl_Register:
|
||||
case AsmTemplateEntityDecl_Immediate:
|
||||
// okay
|
||||
break;
|
||||
default:
|
||||
{
|
||||
gbString s = expr_to_string(index.expr);
|
||||
error(index.expr, "An index must be an integer register, got %s", s);
|
||||
gb_string_free(s);
|
||||
ok_kind = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ok_kind) {
|
||||
have_index = check_asm_addr_register(&index, AsmAddr_Index, reg_name, &index_w);
|
||||
}
|
||||
}
|
||||
|
||||
// base and index must be the same width
|
||||
if (have_base && have_index && base_w != index_w) {
|
||||
Ast *at = mem_op->base ? mem_op->base : expr;
|
||||
error(at, "A memory operand's base and index registers must be the same width, got a %d-bit base and a %d-bit index",
|
||||
cast(int)base_w, cast(int)index_w);
|
||||
}
|
||||
|
||||
// a scale factor is meaningless without an index
|
||||
if (scale.expr && !index.expr) {
|
||||
error(scale.expr, "A scale factor requires an index register");
|
||||
}
|
||||
|
||||
// scale: constant 1/2/4/8, or an immediate parameter
|
||||
for (int i = 0; scale.expr && i == 0; i++) {
|
||||
if (!is_type_integer(scale.type)) {
|
||||
gbString s = expr_to_string(scale.expr);
|
||||
@@ -933,40 +1008,50 @@ gb_internal void check_asm_instruction_operand(AsmCtx *asm_ctx, CheckerContext *
|
||||
}
|
||||
}
|
||||
|
||||
// displacement: an integer that fits a signed 32-bit value
|
||||
for (int i = 0; disp.expr && i == 0; i++) {
|
||||
if (disp.expr->kind == Ast_AsmRegister) {
|
||||
check_register(asm_ctx, &disp, &disp.expr->AsmRegister);
|
||||
} else {
|
||||
Entity *param_entity = entity_of_node(disp.expr);
|
||||
if (disp.mode == Addressing_Constant) {
|
||||
if (is_type_integer(disp.type)) {
|
||||
break;
|
||||
error(disp.expr, "A displacement must be an integer value, got a register");
|
||||
break;
|
||||
}
|
||||
|
||||
Entity *param_entity = entity_of_node(disp.expr);
|
||||
if (disp.mode == Addressing_Constant) {
|
||||
if (disp.value.kind == ExactValue_Integer) {
|
||||
AsmMismatch m = AsmMismatch_None;
|
||||
i32 needed = 0;
|
||||
if (!check_asm_immediate_value_fits(disp.value, 32, &needed, &m)) {
|
||||
gbString vs = exact_value_to_string(disp.value);
|
||||
error(disp.expr, "A memory displacement must fit in a signed 32-bit value, got %s (needs %d bits)", vs, cast(int)needed);
|
||||
gb_string_free(vs);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (param_entity == nullptr) {
|
||||
}
|
||||
|
||||
if (param_entity == nullptr) {
|
||||
gbString s = expr_to_string(disp.expr);
|
||||
error(disp.expr, "A displacement value must be an integer, got %s", s);
|
||||
gb_string_free(s);
|
||||
break;
|
||||
}
|
||||
auto kind = check_asm_find_kind(param_entity, ate->decls);
|
||||
switch (kind) {
|
||||
case AsmTemplateEntityDecl_Register:
|
||||
case AsmTemplateEntityDecl_Immediate:
|
||||
if (is_type_integer(disp.type)) {
|
||||
break;
|
||||
}
|
||||
/*fallthrough*/
|
||||
default:
|
||||
{
|
||||
gbString s = expr_to_string(disp.expr);
|
||||
error(disp.expr, "An displacement value must an integer, got %s", s);
|
||||
gbString t = type_to_string(disp.type);
|
||||
error(disp.expr, "A displacement must be an integer value, got %s of type %s", s, t);
|
||||
gb_string_free(t);
|
||||
gb_string_free(s);
|
||||
break;
|
||||
}
|
||||
auto kind = check_asm_find_kind(param_entity, ate->decls);
|
||||
switch (kind) {
|
||||
case AsmTemplateEntityDecl_Register:
|
||||
case AsmTemplateEntityDecl_Immediate:
|
||||
if (is_type_integer(disp.type)) {
|
||||
break;
|
||||
}
|
||||
/*fallthrough*/
|
||||
default:
|
||||
{
|
||||
gbString s = expr_to_string(disp.expr);
|
||||
gbString t = type_to_string(disp.type);
|
||||
error(disp.expr, "An displacement must be an integer value, got %s of type %s", s, t);
|
||||
gb_string_free(t);
|
||||
gb_string_free(s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user