Support pseudo macro mnemonics (for RISC-V) in the frontend

This commit is contained in:
gingerBill
2026-08-21 11:47:41 +01:00
parent d0fb380c20
commit a604ddceb1
5 changed files with 139 additions and 0 deletions

View File

@@ -485,6 +485,21 @@ main :: proc() {
return m;
}
enum PseudoMacroMnemonic : u8 {
PseudoMacroMnemonic_INVALID,
PseudoMacroMnemonic_LI,
PseudoMacroMnemonic_LA,
PseudoMacroMnemonic_LLA,
PseudoMacroMnemonic_COUNT
};
PseudoMacroMnemonic pseudo_macro_mnemonic_lookup(String const &name) {
if (name == \"li\") { return PseudoMacroMnemonic_LI; }
if (name == \"la\") { return PseudoMacroMnemonic_LA; }
if (name == \"lla\") { return PseudoMacroMnemonic_LLA; }
return PseudoMacroMnemonic_INVALID;
}
Mnemonic mnemonic_lookup(String const &name) {
Mnemonic *found = string_map_get(&mnemonic_map, name);
return found ? *found : M_INVALID;

View File

@@ -494,6 +494,14 @@ main :: proc() {
return M_INVALID;
}
enum PseudoMacroMnemonic : u8 {
PseudoMacroMnemonic_INVALID,
PseudoMacroMnemonic_COUNT
};
PseudoMacroMnemonic pseudo_macro_mnemonic_lookup(String const &name) {
return PseudoMacroMnemonic_INVALID;
}
Mnemonic mnemonic_lookup(String const &name) {
Mnemonic *found = string_map_get(&mnemonic_map, name);

View File

@@ -522,6 +522,14 @@ struct Asm_amd64 {
return M_INVALID;
}
enum PseudoMacroMnemonic : u8 {
PseudoMacroMnemonic_INVALID,
PseudoMacroMnemonic_COUNT
};
PseudoMacroMnemonic pseudo_macro_mnemonic_lookup(String const &name) {
return PseudoMacroMnemonic_INVALID;
}
Mnemonic mnemonic_lookup(String const &name) {
Mnemonic *found = string_map_get(&mnemonic_map, name);

View File

@@ -455,6 +455,21 @@ struct Asm_riscv {
return m;
}
enum PseudoMacroMnemonic : u8 {
PseudoMacroMnemonic_INVALID,
PseudoMacroMnemonic_LI,
PseudoMacroMnemonic_LA,
PseudoMacroMnemonic_LLA,
PseudoMacroMnemonic_COUNT
};
PseudoMacroMnemonic pseudo_macro_mnemonic_lookup(String const &name) {
if (name == "li") { return PseudoMacroMnemonic_LI; }
if (name == "la") { return PseudoMacroMnemonic_LA; }
if (name == "lla") { return PseudoMacroMnemonic_LLA; }
return PseudoMacroMnemonic_INVALID;
}
Mnemonic mnemonic_lookup(String const &name) {
Mnemonic *found = string_map_get(&mnemonic_map, name);
return found ? *found : M_INVALID;

View File

@@ -495,6 +495,14 @@ gb_internal AsmTemplateEntityDeclKind check_asm_find_kind(Entity *entity, Array<
return AsmTemplateEntityDecl_Invalid;
};
gb_internal bool check_asm_is_immediate_param(Entity *tmpl_entity, Operand const *o) {
Entity *pe = entity_of_node(o->expr);
if (pe != nullptr && pe->kind == Entity_Variable) {
return check_asm_find_kind(pe, tmpl_entity->AsmTemplate.decls) == AsmTemplateEntityDecl_Immediate;
}
return false;
}
template <typename AsmCtx>
gb_internal void check_asm_specs(AsmCtx *asm_ctx, CheckerContext *ctx, Scope *scope, Slice<Ast *> const &specs, Array<AsmTemplateEntityDecl> *asm_template_entity_decls) {
@@ -851,9 +859,81 @@ enum CheckMnemomicResult {
CheckMnemomic_Invalid,
CheckMnemomic_Mnemonic,
CheckMnemomic_PseudoMnemonic,
CheckMnemomic_PseudoMacroMnemonic,
CheckMnemomic_Prefix,
};
template <typename AsmCtx>
gb_internal bool check_pseudo_macro_mnemonic(AsmCtx *asm_ctx, Entity *tmpl_entity,
AstAsmInstruction *instr, Slice<Operand> const &operands) {
if (build_context.metrics.arch != TargetArch_riscv64) {
return false;
}
/*
NOTE(bill): this is probably not even a complete list when it comes to
of the pseudo macro mnemonics, but this currently covers most of them.
It just handles those edge cases directly as the LLVM assembler will
handle them directly any way.
*/
int const XLEN = cast(int)(build_context.metrics.ptr_size*8);
String name = instr->name->Ident.token.string;
auto want_int_reg = [&](Operand const *o, char const *role) {
if (determine_asm_operand_kind(o) != AsmOperand_Register ||
check_asm_reg_class_from_type(o->type) != AsmRegClass_Integer) {
error(o->expr, "'%.*s' %s must be an integer register", LIT(name), role);
return false;
}
int width = check_asm_operand_bit_width(o->type);
if (width > XLEN) {
error(o->expr, "'%.*s' %s is wider than the %d-bit register width, got %d-bits", LIT(name), role, XLEN, width);
return false;
}
return true;
};
if (name == "li") { // li rd, imm — dest reg + assemble-time integer that fits XLEN (or a $-immediate)
if (operands.count != 2) {
error(instr->name, "'%.*s' expects 2 operands, got %td", LIT(name), operands.count);
return true; // it exists but incorrectly handled
}
want_int_reg(&operands[0], "destination");
Operand const *imm = &operands[1];
if (imm->mode == Addressing_Constant) {
ExactValue ev = exact_value_to_integer(imm->value);
if (ev.kind != ExactValue_Integer) {
error(imm->expr, "'%.*s' immediate must be an integer constant", LIT(name));
return true;
}
AsmMismatch m = AsmMismatch_None; i32 needed = 0;
if (!check_asm_immediate_value_fits(ev, XLEN, &needed, &m)) {
gbString vs = exact_value_to_string(ev);
error(imm->expr, "'%.*s' immediate %s does not fit in a %d-bit register (needs %d bits)", LIT(name), vs, XLEN, needed);
gb_string_free(vs);
}
} else if (!check_asm_is_immediate_param(tmpl_entity, imm)) {
error(imm->expr, "'li' source must be a constant integer or a $ immediate parameter");
}
return true;
} else if (name == "la" || name == "lla") { // la / lla rd, symbol — dest reg + a label (or symbol, once representable)
if (operands.count != 2) {
error(instr->name, "'%.*s' expects 2 operands, got %td", LIT(name), operands.count);
// NOTE(bill): it exists but incorrectly handled
return true;
}
want_int_reg(&operands[0], "destination");
if (determine_asm_operand_kind(&operands[1]) != AsmOperand_Label) {
error(operands[1].expr, "'%.*s' source must be a label", LIT(name));
}
return true;
}
return false;
}
template <typename AsmCtx>
gb_internal CheckMnemomicResult check_mnemonic_name(AsmCtx *asm_ctx, AstAsmInstruction *instr, u16 *mnemonic_, u8 *suffix_flags_) {
Token token = instr->name->Ident.token;
@@ -883,6 +963,11 @@ gb_internal CheckMnemomicResult check_mnemonic_name(AsmCtx *asm_ctx, AstAsmInstr
return CheckMnemomic_Mnemonic;
}
auto pmm = asm_ctx->pseudo_macro_mnemonic_lookup(name);
if (pmm) {
if (mnemonic_) *mnemonic_ = cast(u16)pmm;
return CheckMnemomic_PseudoMacroMnemonic;
}
ERROR_BLOCK();
if (instr->operands.count == 0) {
@@ -1974,6 +2059,14 @@ gb_internal void check_asm_template(AsmCtx *asm_ctx, CheckerContext *ctx, Entity
asm_acc.saw_any_instructions = true;
previous_prefix = 0;
previous_prefix_instr = nullptr;
} else if (res == CheckMnemomic_PseudoMacroMnemonic) {
instr->suffix_flags = suffix_flags;
check_pseudo_macro_mnemonic(asm_ctx, entity, instr, slice_from_array(operands));
asm_acc.saw_any_instructions = true;
previous_prefix = 0;
previous_prefix_instr = nullptr;
} else {