From d0c3b78ece85128297e817a12ec0442996805b69 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 17 Aug 2026 18:56:47 +0100 Subject: [PATCH] Allow (parent scope) constants within `asm` templates --- src/check_asm.cpp | 40 +++++++++----- src/llvm_backend_asm.cpp | 112 ++++++++++++++++++++++++--------------- 2 files changed, 98 insertions(+), 54 deletions(-) diff --git a/src/check_asm.cpp b/src/check_asm.cpp index 4f2a21bad..e3ad7d6e9 100644 --- a/src/check_asm.cpp +++ b/src/check_asm.cpp @@ -447,14 +447,14 @@ gb_internal void check_asm_specs(AsmCtx *asm_ctx, CheckerContext *ctx, Scope *sc GB_ASSERT(spec->name->kind == Ast_Ident); - Entity *input = scope_lookup(scope, spec->name->Ident.interned, spec->name->Ident.hash); + Entity *input = scope_lookup_current(scope, spec->name->Ident.interned, spec->name->Ident.hash); Entity *other_scratch = nullptr; String pin = {}; String pin_flag = {}; if (spec->value != nullptr) { if (spec->value->kind == Ast_Ident) { - other_scratch = scope_lookup(scope, spec->value->Ident.interned, spec->value->Ident.hash); + other_scratch = scope_lookup_current(scope, spec->value->Ident.interned, spec->value->Ident.hash); if (other_scratch) { auto group = check_asm_find_group(other_scratch, *asm_template_entity_decls, nullptr); if (!group) { @@ -621,7 +621,7 @@ gb_internal void check_asm_specs(AsmCtx *asm_ctx, CheckerContext *ctx, Scope *sc error(spec->name, "Undefined parameter declaration '%.*s'", LIT(spec->name->Ident.token.string)); continue; } - Entity *output = scope_lookup(scope, spec->tied_name->Ident.interned, spec->tied_name->Ident.hash); + Entity *output = scope_lookup_current(scope, spec->tied_name->Ident.interned, spec->tied_name->Ident.hash); if (output == nullptr) { error(spec->name, "Undefined parameter declaration '%.*s'", LIT(spec->name->Ident.token.string)); continue; @@ -1038,14 +1038,28 @@ gb_internal void check_asm_instruction_operand(AsmCtx *asm_ctx, CheckerContext * switch (expr->kind) { case_ast_node(i, Ident, expr); - Entity *found = scope_lookup(param_scope, i->interned, i->hash); - if (found == nullptr) { - error(expr, "Undeclared asm parameter '%.*s'", LIT(i->token.string)); + Entity *found = scope_lookup_current(param_scope, i->interned, i->hash); + if (found != nullptr) { + i->entity = found; + operand->mode = Addressing_Value; + operand->type = found->type; return; } - i->entity = found; - operand->mode = Addressing_Value; - operand->type = found->type; + found = scope_lookup(param_scope->parent, i->interned, i->hash); + if (found != nullptr) { + if (found->kind == Entity_Constant) { + i->entity = found; + operand->mode = Addressing_Constant; + operand->value = found->Constant.value; + operand->type = found->type; + + add_type_and_value(ctx, expr, operand->mode, operand->type, operand->value); + } else { + error(expr, "Only asm parameters or constants are allowed to be used within an 'asm' template"); + } + } else { + error(expr, "Undeclared asm parameter or constant '%.*s'", LIT(i->token.string)); + } return; case_end; case_ast_node(bl, BasicLit, expr); @@ -1286,11 +1300,13 @@ gb_internal void check_asm_instruction_operand(AsmCtx *asm_ctx, CheckerContext * case_end; case_ast_node(label, AsmLabelDecl, expr); ast_node(name, Ident, label->name); - Entity *found = scope_lookup(label_scope, name->interned, name->hash); + Entity *found = scope_lookup_current(label_scope, name->interned, name->hash); if (found == nullptr) { error(expr, "Undeclared asm label '.%.*s'", LIT(name->token.string)); } name->entity = found; + + add_type_and_value(ctx, expr, Addressing_Value, found->type, {}); return; case_end; } @@ -1321,8 +1337,8 @@ gb_internal void check_asm_template(AsmCtx *asm_ctx, CheckerContext *ctx, Entity } AstProcType *pt = &at->signature->ProcType; - ate->param_scope = create_scope(nullptr, nullptr); - ate->label_scope = create_scope(nullptr, nullptr); + ate->param_scope = create_scope(ctx->info, ctx->scope); + ate->label_scope = create_scope(ctx->info, ctx->scope); ate->decls.allocator = heap_allocator(); diff --git a/src/llvm_backend_asm.cpp b/src/llvm_backend_asm.cpp index bc6425147..c5eab544f 100644 --- a/src/llvm_backend_asm.cpp +++ b/src/llvm_backend_asm.cpp @@ -39,11 +39,64 @@ struct lbAsmGenerate { return &op; } } - GB_PANIC("Could not find asm entity %s", LIT(parameter->token.string)); + GB_PANIC("Could not find asm entity %.*s", LIT(parameter->token.string)); return nullptr; } + gbString write_constant_operand(gbString asm_string, Ast *op, u32 flags) { + GB_ASSERT(op->tav.mode == Addressing_Constant); + + op->tav.value = exact_value_to_integer(op->tav.value); + ExactValue ev = op->tav.value; + GB_ASSERT(ev.kind != ExactValue_Invalid); + switch (ev.kind) { + case ExactValue_Integer: { + i64 val = exact_value_to_i64(ev); + if (flags & WriteOperandFlag_IsScale) { + switch (val) { + case 1: case 2: case 4: case 8: + // okay + break; + default: + error(op, "A scale must be a constant integer or an immediate with the value 1, 2, 4, or 8, got %lld", cast(long long)val); + break; + } + } else if (flags & WriteOperandFlag_IsScaleLog2) { + switch (val) { + case 0: case 1: case 2: case 3: + // NOTE(bill): AMD64 only supports full scales + val = (cast(i64)1)< op_number, Ast *op, u32 flags) { + if (op->tav.mode == Addressing_Constant) { + return write_constant_operand(asm_string, op, flags); + } + switch (op->kind) { case_ast_node(i, Ident, op); Entity *e = entity_of_node(op); @@ -75,47 +128,7 @@ struct lbAsmGenerate { case_end; case_ast_node(bl, BasicLit, op); - op->tav.value = exact_value_to_integer(op->tav.value); - ExactValue ev = op->tav.value; - GB_ASSERT(ev.kind != ExactValue_Invalid); - switch (ev.kind) { - case ExactValue_Integer: { - i64 val = exact_value_to_i64(ev); - if (flags & WriteOperandFlag_IsScale) { - switch (val) { - case 1: case 2: case 4: case 8: - // okay - break; - default: - error(op, "A scale must be a constant integer or an immediate with the value 1, 2, 4, or 8, got %lld", cast(long long)val); - break; - } - } else if (flags & WriteOperandFlag_IsScaleLog2) { - switch (val) { - case 0: case 1: case 2: case 3: - // NOTE(bill): AMD64 only supports full scales - val = (cast(i64)1)<valid_form_index >= 0); // Otherwise derive from the matched form's operand widths. auto forms = g_asm_amd64.encoding_forms(instr->mnemonic); + if (forms.count <= 1) { + return 0; + } auto &form = forms[instr->valid_form_index]; i32 width = 0; @@ -224,6 +240,18 @@ struct lbAsmGenerate_amd64 : lbAsmGenerate { any_vector = true; continue; // xmm/ymm/zmm/k forms take no b/w/l/q suffix } + + // Only register and memory operands contribute an operand-size suffix. + // Relative branch targets (OP_REL8/REL32), immediates (OP_IMM*), and + // labels are NOT operand sizes -- jl/jmp/call/setcc must never get a + // b/w/l/q suffix from their displacement/immediate. + AsmOperandKind kind = g_asm_amd64.kind_from_operand_type(ot); + if (kind != AsmOperand_Register && + kind != AsmOperand_Memory && + kind != AsmOperand_Register_Or_Memory) { + continue; + } + i32 w = g_asm_amd64.operand_type_bit_width(ot); if (w == 8 || w == 16 || w == 32 || w == 64) { width = gb_max(width, w); // GP/memory width