Add more MOV forms; fix liveness check for lanes

This commit is contained in:
gingerBill
2026-08-31 17:51:24 +01:00
parent 07b5c2ee0f
commit 4f8a91092b
12 changed files with 2044 additions and 1937 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -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);

View File

@@ -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);
}
}