Correct risc-v aliases

This commit is contained in:
gingerBill
2026-08-20 19:48:30 +01:00
parent 7a7bfea0bd
commit 09204e8982
3 changed files with 67 additions and 15 deletions

View File

@@ -341,7 +341,13 @@ struct Asm_amd64 {
}
enum AliasSrc : u8 {
AliasSrc_NONE, // slot unused
AliasSrc_NONE, // slot unused
AliasSrc_ARG0, // user's 1st operand
AliasSrc_ARG1, // user's 2nd operand
AliasSrc_ARG2, // user's 3rd operand
AliasSrc_ZERO, // hardwired zero
AliasSrc_LINK, // link register
AliasSrc_LIT,
};
struct PseudoAlias {

View File

@@ -203,8 +203,8 @@ struct Asm_riscv {
AliasSrc_ARG0, // user's 1st operand
AliasSrc_ARG1, // user's 2nd operand
AliasSrc_ARG2, // user's 3rd operand
AliasSrc_X0, // hardwired zero (x0)
AliasSrc_X1, // link register (ra / x1)
AliasSrc_ZERO, // hardwired zero (x0)
AliasSrc_LINK, // link register (ra / x1)
AliasSrc_LIT, // the `lit` field below (immediate literal)
AliasSrc_CSR_LIT, // the `csr` field below (fixed 12-bit CSR address)
};
@@ -395,7 +395,7 @@ struct Asm_riscv {
string_map_set(&mnemonic_map, mnemonic_strings[m], cast(Mnemonic)m);
}
string_map_init(&mnemonic_map, PSEUDO_MNEMONIC_COUNT*2);
string_map_init(&pseudo_mnemonic_map, PSEUDO_MNEMONIC_COUNT*2);
for (u16 m = PM_INVALID+1; m < PSEUDO_MNEMONIC_COUNT; m++) {
string_map_set(&pseudo_mnemonic_map, pseudo_mnemonic_strings[m], cast(PseudoMnemonic)m);
}

View File

@@ -932,6 +932,30 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm
name = asm_ctx->pseudo_mnemonic_strings[pseudo_mnemonic];
}
bool is_pseudo = pseudo_mnemonic != 0;
int target_explicit_count = is_pseudo ? alias.nargs : -1;
auto pseudo_alias_arg_operand_index = [](AsmCtx *asm_ctx, auto a, int arg_index) -> int {
if (arg_index < 0 || arg_index > 2) {
return -1;
}
auto want = cast(AsmCtx::AliasSrc)(asm_ctx->AliasSrc_ARG0 + arg_index);
for (int i = 0; i < gb_count_of(a.src); i++) {
if (a.src[i] == want) {
return i;
}
}
return -1;
};
auto user_operand_target_index = [&](int user_i) -> int {
if (!is_pseudo) {
return user_i;
}
return pseudo_alias_arg_operand_index(asm_ctx, alias, user_i);
};
int min_count = I32_MAX;
int max_count = -1;
@@ -943,12 +967,9 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm
min_count = gb_max(min_count, 0);
max_count = gb_max(max_count, 0);
if (pseudo_mnemonic) {
int alias_count = alias.nargs;
GB_ASSERT_MSG(alias_count <= min_count, "%d <= %d", alias_count, min_count);
GB_ASSERT_MSG(alias_count <= max_count, "%d <= %d", alias_count, max_count);
min_count = gb_min(min_count, alias_count);
max_count = gb_min(max_count, alias_count);
if (is_pseudo) {
min_count = gb_min(min_count, target_explicit_count);
max_count = gb_min(max_count, target_explicit_count);
}
// A prefix that none of this mnemonic's forms can take is unconditionally wrong,
@@ -988,8 +1009,15 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm
for_array(form_index, forms) {
auto &form = forms[form_index];
if (operands.count != cast(int)form.explicit_count()) {
continue;
if (is_pseudo) {
if (cast(int)form.explicit_count() < target_explicit_count) {
continue;
}
} else {
if (operands.count != cast(int)form.explicit_count()) {
continue;
}
}
int score = 0;
@@ -1141,7 +1169,8 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm
// don't tell us which physical register was written.
u16 written_ops = cast(u16)clobber.written;
for_array(i, operands) {
if (i >= 4 || (written_ops & (1u << i)) == 0) {
int tslot = user_operand_target_index(cast(int)i);
if (tslot < 0 || tslot >= 4 || (written_ops & (1u << tslot)) == 0) {
continue;
}
Ast *e = operands[i].expr;
@@ -1151,6 +1180,22 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm
explicit_writes |= b;
}
}
if (is_pseudo) {
// Synthesized register sources (e.g. ra in `jal off` -> `jal ra, off`) also
// write a physical register; record them so ra is treated as produced/clobbered.
u16 synth = 0;
for (int i = 0; i < gb_count_of(alias.src); i++) {
if ((written_ops & (1u << i)) == 0) {
continue;
}
if (alias.src[i] == asm_ctx->AliasSrc_LINK) {
GB_ASSERT(build_context.metrics.arch == TargetArch_riscv64);
synth |= 1<<0; // ClobberReg_RA
}
}
produced |= synth;
explicit_writes |= synth;
}
asm_acc->defined_regs |= produced;
// Registers this form clobbers implicitly (RDTSC->RAX:RDX, etc.), for the
@@ -1203,7 +1248,8 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm
if (best_form >= 0) {
auto &form = forms[best_form];
for_array(i, operands) {
int slot = asm_ctx->form_explicit_slot(form, cast(int)i);
int tei = user_operand_target_index(cast(int)i);
int slot = (tei >= 0) ? asm_ctx->form_explicit_slot(form, tei) : -1;
auto type = (slot >= 0) ? form.ops[slot] : asm_ctx->OP_NONE;
AsmOperandKind dst = asm_ctx->kind_from_operand_type(type);
AsmOperandKind src = determine_asm_operand_kind(&operands[i]);
@@ -1274,7 +1320,7 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm
} else if (dst == AsmOperand_Immediate) {
error(operands[i].expr, "'%.*s' operand-%td must be an assemble-time constant or a $ immediate parameter, got a %.*s",
LIT(name), i, LIT(asm_operand_kind_strings[src]));
}else if (dst) {
} else if (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 {