Add #no_init suffix directive for asm specifications.

This commit is contained in:
gingerBill
2026-08-24 19:10:32 +01:00
parent dfa473f2c8
commit 3e159a4e10
4 changed files with 93 additions and 43 deletions

View File

@@ -793,6 +793,26 @@ gb_internal void check_asm_specs(AsmCtx *asm_ctx, CheckerContext *ctx, Scope *sc
error(spec->value, "Input parameters, and thus tied parameters, cannot be pinned to a flag style register");
}
}
for (Ast *dir_ : spec->directives) {
ast_node(dir, BasicDirective, dir_);
String name = dir->name.string;
if (name == "no_init") {
i32 input_index = -1;
check_asm_find_group(input, *asm_template_entity_decls, &input_index);
if (input_index >= 0) {
auto *i = &(*asm_template_entity_decls)[input_index];
i->no_init = true;
if (i->tie >= 0) {
auto *o = &(*asm_template_entity_decls)[i->tie];
o->no_init = true;
}
}
} else {
error(dir_, "Invalid directive for an asm specification, got '#%.*s'", LIT(name));
}
}
}
}
@@ -2451,6 +2471,9 @@ gb_internal void check_asm_template(AsmCtx *asm_ctx, CheckerContext *ctx, Entity
// straight-line instruction stream. Seeded with input-pinned registers (they
// carry their argument at entry); grows as instructions write registers.
for (auto const &ed : ate->decls) {
if (ed.no_init) {
ptr_set_add(&asm_acc.defined_params, ed.entity);
}
switch (ed.param_group) {
case AsmTemplateEntityDeclParamGroup_Input:
if (ed.pin.len != 0) {
@@ -2683,6 +2706,9 @@ gb_internal void check_asm_template(AsmCtx *asm_ctx, CheckerContext *ctx, Entity
if (ed.tie >= 0) {
continue;
}
if (ed.no_init) {
continue;
}
if (!asm_acc.straight_line) {
continue;
}

View File

@@ -196,6 +196,8 @@ struct AsmTemplateEntityDecl {
i32 view_of; // total_index of the source operand this is a width-view of, else -1
i32 view_bits; // the view width in bits, otherwise 0
bool no_init;
};
// An Entity is a named "thing" in the language

View File

@@ -533,10 +533,11 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
case Ast_AsmRegister:
break;
case Ast_AsmSpec:
n->AsmSpec.name = clone_ast(n->AsmSpec.name, f);
n->AsmSpec.tied_name = clone_ast(n->AsmSpec.tied_name, f);
n->AsmSpec.type = clone_ast(n->AsmSpec.type, f);
n->AsmSpec.value = clone_ast(n->AsmSpec.value, f);
n->AsmSpec.name = clone_ast(n->AsmSpec.name, f);
n->AsmSpec.tied_name = clone_ast(n->AsmSpec.tied_name, f);
n->AsmSpec.type = clone_ast(n->AsmSpec.type, f);
n->AsmSpec.value = clone_ast(n->AsmSpec.value, f);
n->AsmSpec.directives = clone_ast_array(n->AsmSpec.directives, f);
break;
case Ast_AsmClobber:
n->AsmClobber.value = clone_ast(n->AsmClobber.value, f);
@@ -2694,6 +2695,60 @@ gb_internal Ast *parse_asm_signature(AstFile *f, Token asm_token) {
return ast_proc_type(f, asm_token, params, results, tags, cc, is_generic, diverging);
}
gb_internal Ast *parse_asm_spec(AstFile *f) {
Ast *name = parse_ident(f);
Ast *tied_name = nullptr;
Ast *type = nullptr;
Ast *value = nullptr;
if (allow_token(f, Token_ArrowRight)) {
tied_name = parse_ident(f);
}
if (allow_token(f, Token_Colon)) {
type = parse_type(f);
}
if (allow_token(f, Token_Eq)) {
if (f->curr_token.kind == Token_Ident) {
value = parse_ident(f);
} else if (f->curr_token.kind == Token_Mod) {
value = parse_asm_register(f);
} else {
error(f->curr_token, "Expected a register or scratch parameter");
Ast *dummy = parse_expr(f, true);
gb_unused(dummy);
}
}
if (tied_name != nullptr) {
if (type != nullptr) {
syntax_error(f->curr_token, "An asm specification for tied values cannot declare a type");
}
} else if (type == nullptr && value == nullptr) {
syntax_error(f->curr_token, "An asm specification must specify at least either a type or a value if the value is not tied");
}
Array<Ast *> directives = {};
directives.allocator = heap_allocator();
while (f->curr_token.kind == Token_Hash) {
Token token = expect_token(f, Token_Hash);
Token name = expect_token_after(f, Token_Ident, "hash for directive");
if (name.kind == Token_Ident) {
Ast *directive = ast_basic_directive(f, token, name);
array_add(&directives, directive);
}
}
Ast *spec = alloc_ast_node(f, Ast_AsmSpec);
spec->AsmSpec.name = name;
spec->AsmSpec.tied_name = tied_name;
spec->AsmSpec.type = type;
spec->AsmSpec.value = value;
spec->AsmSpec.directives = directives;
return spec;
}
gb_internal Ast *parse_asm_template(AstFile *f) {
Token token = expect_token(f, Token_asm);
@@ -2718,41 +2773,7 @@ gb_internal Ast *parse_asm_template(AstFile *f) {
f->curr_token.kind != Token_EOF) {
Ast *spec = nullptr;
if (f->curr_token.kind == Token_Ident) {
Ast *name = parse_ident(f);
Ast *tied_name = nullptr;
Ast *type = nullptr;
Ast *value = nullptr;
if (allow_token(f, Token_ArrowRight)) {
tied_name = parse_ident(f);
}
if (allow_token(f, Token_Colon)) {
type = parse_type(f);
}
if (allow_token(f, Token_Eq)) {
if (f->curr_token.kind == Token_Ident) {
value = parse_ident(f);
} else if (f->curr_token.kind == Token_Mod) {
value = parse_asm_register(f);
} else {
error(f->curr_token, "Expected a register or scratch parameter");
Ast *dummy = parse_expr(f, true);
gb_unused(dummy);
}
}
if (tied_name != nullptr) {
if (type != nullptr) {
syntax_error(f->curr_token, "An asm specification for tied values cannot declare a type");
}
} else if (type == nullptr && value == nullptr) {
syntax_error(f->curr_token, "An asm specification must specify at least either a type or a value if the value is not tied");
}
spec = alloc_ast_node(f, Ast_AsmSpec);
spec->AsmSpec.name = name;
spec->AsmSpec.tied_name = tied_name;
spec->AsmSpec.type = type;
spec->AsmSpec.value = value;
spec = parse_asm_spec(f);
} else if (f->curr_token.kind == Token_Hash) {
Token hash = expect_token(f, Token_Hash);
Token name = expect_token(f, Token_Ident);

View File

@@ -489,10 +489,11 @@ struct AstSplitArgs {
Token flag; \
}) \
AST_KIND(AsmSpec, "asm specification", struct { \
Ast *name; \
Ast *tied_name; \
Ast *type; \
Ast *value; \
Ast * name; \
Ast * tied_name; \
Ast * type; \
Ast * value; \
Array<Ast *> directives; \
}) \
AST_KIND(AsmClobber, "asm clobber", struct { \
Token token; \