mirror of
https://github.com/odin-lang/Odin.git
synced 2026-09-03 18:50:20 +00:00
Add more MOV forms; fix liveness check for lanes
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -319,7 +319,8 @@ gb_internal bool check_asm_operand_size_class(AsmCtx *asm_ctx, typename AsmCtx::
|
||||
// must not be held against the slot's register class. Only width matters for the
|
||||
// memory interpretation. Register operands still get the full class check.
|
||||
if (want_class != AsmRegClass_Unknown && !is_memory) {
|
||||
if (!asm_reg_class_compatible(want_class, got_class)) {
|
||||
bool is_lane = asm_ctx->operand_type_is_lane(slot);
|
||||
if (!is_lane && !asm_reg_class_compatible(want_class, got_class)) {
|
||||
if (mismatch_) *mismatch_ = AsmMismatch_Class;
|
||||
return false;
|
||||
}
|
||||
@@ -1687,7 +1688,6 @@ gb_internal bool check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm
|
||||
|
||||
u16 written_ops = cast(u16)clobber.written;
|
||||
u16 pinned_param_writes = 0;
|
||||
auto const &decls = tmpl_entity->AsmTemplate.decls;
|
||||
for_array(i, operands) {
|
||||
int tslot = user_operand_target_index(cast(int)i);
|
||||
if (tslot < 0 || tslot >= 4 || (written_ops & (1u << tslot)) == 0) {
|
||||
@@ -1700,12 +1700,15 @@ gb_internal bool check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm
|
||||
explicit_writes |= b;
|
||||
continue;
|
||||
}
|
||||
Entity *pe = entity_of_node(operands[i].expr);
|
||||
if (pe != nullptr && pe->kind == Entity_Variable) {
|
||||
i32 di = -1;
|
||||
check_asm_find_group(pe, decls, &di); // reuse existing index finder
|
||||
pinned_param_writes |= asm_decl_resolve_pin_bit(asm_ctx, decls, di);
|
||||
// NOTE(bill): A lane operand `v[idx]` is an IndexExpr with no entity of its own
|
||||
Ast *op_expr = operands[i].expr;
|
||||
if (op_expr != nullptr && op_expr->kind == Ast_IndexExpr) {
|
||||
op_expr = op_expr->IndexExpr.expr;
|
||||
}
|
||||
Entity *pe = entity_of_node(op_expr);
|
||||
if (pe == nullptr || pe->kind != Entity_Variable) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_pseudo &&
|
||||
@@ -2251,7 +2254,11 @@ gb_internal void check_asm_instruction_operand(AsmCtx *asm_ctx, CheckerContext *
|
||||
}
|
||||
|
||||
// base and index must be the same width
|
||||
if (have_base && have_index && base_w != index_w) {
|
||||
// AArch64 register-offset addressing allows a 32-bit index with a 64-bit base
|
||||
// (extended-register form: uxtw/sxtw). Other targets require equal widths.
|
||||
bool ext_index_ok = build_context.metrics.arch == TargetArch_arm64 &&
|
||||
base_w == 64 && index_w == 32;
|
||||
if (have_base && have_index && base_w != index_w && !ext_index_ok) {
|
||||
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);
|
||||
|
||||
@@ -1470,6 +1470,27 @@ struct lbAsmGenerate_arm64 : lbAsmGenerate {
|
||||
}
|
||||
}
|
||||
|
||||
char const *arm64_index_extend(AstAsmMemoryOperand *mem_op) {
|
||||
Ast *idx = mem_op->index;
|
||||
if (idx == nullptr) {
|
||||
return "lsl";
|
||||
}
|
||||
// Resolve the index operand's type: explicit reg -> its reg width; a param ->
|
||||
// its Odin type.
|
||||
Type *ty = idx->tav.type;
|
||||
if (ty == nullptr) {
|
||||
return "lsl";
|
||||
}
|
||||
i32 w = check_asm_operand_bit_width(ty);
|
||||
if (w == 64) {
|
||||
return "lsl";
|
||||
}
|
||||
// 32-bit index: pick zero/sign extend from the type's signedness.
|
||||
Type *bt = base_type(ty);
|
||||
bool is_signed = is_type_integer(bt) && !is_type_unsigned(bt);
|
||||
return is_signed ? "sxtw" : "uxtw";
|
||||
}
|
||||
|
||||
// ARM64 addressing: `[base]`, `[base, #disp]`, `[base, Xindex]`, or
|
||||
// `[base, Xindex, LSL #n]`. Base+index and base+disp are mutually exclusive
|
||||
// addressing modes, so an index precludes a displacement.
|
||||
@@ -1486,10 +1507,19 @@ struct lbAsmGenerate_arm64 : lbAsmGenerate {
|
||||
write_cstr(", ");
|
||||
this->write_operand(op_number, mem_op->index, flags&~WriteOperandFlag_PrintPrefixes);
|
||||
if (mem_op->scale != nullptr) {
|
||||
i64 shift = this->arm64_scale_shift_amount(mem_op);
|
||||
// Omit the no-op shift so `[x0, x1, LSL #0]` prints as the canonical
|
||||
// `[x0, x1]`.
|
||||
if (shift != 0) {
|
||||
// A 32-bit index needs an extend specifier (uxtw/sxtw); a 64-bit index
|
||||
// uses lsl. The extend for a w-index is mandatory even at shift 0.
|
||||
char const *extend = this->arm64_index_extend(mem_op); // "lsl", "uxtw", or "sxtw"
|
||||
i64 shift = (mem_op->scale != nullptr) ? this->arm64_scale_shift_amount(mem_op) : 0;
|
||||
bool is_lsl = (extend[0] == 'l');
|
||||
if (!is_lsl) {
|
||||
// w-index: always print the extend; shift optional.
|
||||
if (shift != 0) {
|
||||
asm_string = gb_string_append_fmt(asm_string, ", %s #%lld", extend, cast(long long)shift);
|
||||
} else {
|
||||
asm_string = gb_string_append_fmt(asm_string, ", %s", extend);
|
||||
}
|
||||
} else if (shift != 0) {
|
||||
asm_string = gb_string_append_fmt(asm_string, ", lsl #%lld", cast(long long)shift);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user