mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-18 03:12:10 +00:00
Support asm template groups
This commit is contained in:
@@ -1037,6 +1037,14 @@ gb_internal void check_asm_instruction_operand(AsmCtx *asm_ctx, CheckerContext *
|
||||
gb_unused(label_scope);
|
||||
|
||||
switch (expr->kind) {
|
||||
case_ast_node(pe, ParenExpr, expr);
|
||||
check_expr(ctx, operand, expr);
|
||||
if (operand->mode != Addressing_Constant) {
|
||||
error(expr, "Asm operands within parentheses can only compile time constants");
|
||||
}
|
||||
return;
|
||||
case_end;
|
||||
|
||||
case_ast_node(i, Ident, expr);
|
||||
Entity *found = scope_lookup_current(param_scope, i->interned, i->hash);
|
||||
if (found != nullptr) {
|
||||
|
||||
@@ -1985,6 +1985,151 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D
|
||||
check_objc_methods(ctx, pg_entity, ac);
|
||||
}
|
||||
|
||||
gb_internal void check_asm_group_decl(CheckerContext *ctx, Entity *asm_entity, DeclInfo *d) {
|
||||
GB_ASSERT(asm_entity->kind == Entity_ProcGroup);
|
||||
auto *pge = &asm_entity->ProcGroup;
|
||||
String proc_group_name = asm_entity->token.string;
|
||||
|
||||
ast_node(pg, AsmGroup, d->init_expr);
|
||||
|
||||
pge->entities = array_make<Entity*>(permanent_allocator(), 0, pg->args.count);
|
||||
|
||||
// NOTE(bill): This must be set here to prevent cycles in checking if someone
|
||||
// places the entity within itself
|
||||
asm_entity->type = t_invalid;
|
||||
|
||||
PtrSet<Entity *> entity_set = {};
|
||||
ptr_set_init(&entity_set, 2*pg->args.count);
|
||||
|
||||
for (Ast *arg_ : pg->args) {
|
||||
Ast *arg = arg_;
|
||||
Entity *e = nullptr;
|
||||
Operand o = {};
|
||||
if (arg->kind == Ast_BinaryExpr && arg->BinaryExpr.op.kind == Token_where) {
|
||||
Ast *cond_expr = arg->BinaryExpr.right;
|
||||
Operand cond = {};
|
||||
check_expr(ctx, &cond, cond_expr);
|
||||
if (cond.mode != Addressing_Invalid) {
|
||||
if (cond.mode != Addressing_Constant || !is_type_boolean(cond.type) || cond.value.kind != ExactValue_Bool) {
|
||||
error(arg, "Expected a constant binary expression for the 'where' clause");
|
||||
} else if (!cond.value.value_bool) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
arg = arg->BinaryExpr.left;
|
||||
}
|
||||
|
||||
if (arg->kind == Ast_Ident) {
|
||||
e = check_ident(ctx, &o, arg, nullptr, nullptr, true);
|
||||
} else if (arg->kind == Ast_SelectorExpr) {
|
||||
e = check_selector(ctx, &o, arg, nullptr);
|
||||
}
|
||||
if (e == nullptr) {
|
||||
error(arg, "Expected a valid entity name in asm group, got %.*s", LIT(ast_strings[arg->kind]));
|
||||
continue;
|
||||
}
|
||||
if (e->kind != Entity_AsmTemplate) {
|
||||
error(arg, "Expected an asm template");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ptr_set_update(&entity_set, e)) {
|
||||
error(arg, "Previous use of `%.*s` in asm group", LIT(e->token.string));
|
||||
continue;
|
||||
}
|
||||
array_add(&pge->entities, e);
|
||||
}
|
||||
|
||||
ptr_set_destroy(&entity_set);
|
||||
|
||||
for (isize j = 0; j < pge->entities.count; j++) {
|
||||
Entity *p = pge->entities[j];
|
||||
if (p->type == t_invalid) {
|
||||
// NOTE(bill): This invalid overload has already been handled
|
||||
continue;
|
||||
}
|
||||
|
||||
if (p->flags & EntityFlag_Disabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = p->token.string;
|
||||
|
||||
for (isize k = j+1; k < pge->entities.count; k++) {
|
||||
Entity *q = pge->entities[k];
|
||||
GB_ASSERT(p != q);
|
||||
|
||||
bool is_invalid = false;
|
||||
|
||||
TokenPos pos = q->token.pos;
|
||||
|
||||
if (q->type == nullptr || q->type == t_invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
ERROR_BLOCK();
|
||||
|
||||
if (q->flags & EntityFlag_Disabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type);
|
||||
bool both_have_where_clauses = false;
|
||||
if (p->decl_info != nullptr && q->decl_info != nullptr &&
|
||||
p->decl_info->proc_lit != nullptr && q->decl_info->proc_lit != nullptr) {
|
||||
GB_ASSERT(p->decl_info->proc_lit->kind == Ast_ProcLit);
|
||||
GB_ASSERT(q->decl_info->proc_lit->kind == Ast_ProcLit);
|
||||
auto pl = &p->decl_info->proc_lit->ProcLit;
|
||||
auto ql = &q->decl_info->proc_lit->ProcLit;
|
||||
|
||||
// Allow collisions if the procedures both have 'where' clauses and are both polymorphic
|
||||
bool pw = pl->where_token.kind != Token_Invalid && is_type_polymorphic(p->type, true);
|
||||
bool qw = ql->where_token.kind != Token_Invalid && is_type_polymorphic(q->type, true);
|
||||
both_have_where_clauses = pw && qw;
|
||||
}
|
||||
|
||||
if (!both_have_where_clauses) switch (kind) {
|
||||
case ProcOverload_Identical:
|
||||
error(p->token, "Overloaded asm template '%.*s' has the same type as another asm template in the asm template group '%.*s'", LIT(name), LIT(proc_group_name));
|
||||
is_invalid = true;
|
||||
break;
|
||||
// case ProcOverload_CallingConvention:
|
||||
// error(p->token, "Overloaded asm template '%.*s' has the same type as another asm template in the asm template group '%.*s'", LIT(name), LIT(proc_group_name));
|
||||
// is_invalid = true;
|
||||
// break;
|
||||
case ProcOverload_ParamVariadic:
|
||||
error(p->token, "Overloaded asm template '%.*s' has the same type as another asm template in the asm template group '%.*s'", LIT(name), LIT(proc_group_name));
|
||||
is_invalid = true;
|
||||
break;
|
||||
case ProcOverload_ResultCount:
|
||||
case ProcOverload_ResultTypes:
|
||||
error(p->token, "Overloaded asm template '%.*s' has the same parameters but different results in the asm template group '%.*s'", LIT(name), LIT(proc_group_name));
|
||||
is_invalid = true;
|
||||
break;
|
||||
case ProcOverload_Polymorphic:
|
||||
break;
|
||||
case ProcOverload_ParamCount:
|
||||
case ProcOverload_ParamTypes:
|
||||
case ProcOverload_TargetFeatures:
|
||||
// This is okay :)
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (is_invalid) {
|
||||
error_line("\tprevious asm template at %s\n", token_pos_to_string(pos));
|
||||
q->type = t_invalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AttributeContext ac = {};
|
||||
check_decl_attributes(ctx, d->attributes, proc_group_attribute, &ac);
|
||||
check_objc_methods(ctx, asm_entity, ac);
|
||||
}
|
||||
|
||||
#include "check_asm.cpp"
|
||||
|
||||
|
||||
@@ -2061,7 +2206,11 @@ gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d,
|
||||
check_proc_decl(&c, e, d);
|
||||
break;
|
||||
case Entity_ProcGroup:
|
||||
check_proc_group_decl(&c, e, d);
|
||||
if (e->ProcGroup.is_asm_group) {
|
||||
check_asm_group_decl(&c, e, d);
|
||||
} else {
|
||||
check_proc_group_decl(&c, e, d);
|
||||
}
|
||||
break;
|
||||
|
||||
case Entity_AsmTemplate:
|
||||
|
||||
@@ -12518,6 +12518,12 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
|
||||
o->mode = Addressing_Invalid;
|
||||
case_end;
|
||||
|
||||
case_ast_node(ag, AsmGroup, node);
|
||||
error(node, "Illegal use of a asm group");
|
||||
o->mode = Addressing_Invalid;
|
||||
case_end;
|
||||
|
||||
|
||||
case_ast_node(pl, ProcLit, node);
|
||||
CheckerContext ctx = *c;
|
||||
|
||||
@@ -13039,6 +13045,16 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
|
||||
str = gb_string_append_rune(str, '}');
|
||||
case_end;
|
||||
|
||||
case_ast_node(pg, AsmGroup, node);
|
||||
str = gb_string_appendc(str, "asm{");
|
||||
for_array(i, pg->args) {
|
||||
if (i > 0) str = gb_string_appendc(str, ", ");
|
||||
str = write_expr_to_string(str, pg->args[i], shorthand);
|
||||
}
|
||||
str = gb_string_append_rune(str, '}');
|
||||
case_end;
|
||||
|
||||
|
||||
case_ast_node(pl, ProcLit, node);
|
||||
str = write_expr_to_string(str, pl->type, shorthand);
|
||||
if (pl->body) {
|
||||
|
||||
@@ -5064,7 +5064,14 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
|
||||
if (fl != nullptr) {
|
||||
error(name, "Procedure groups are not allowed within a foreign block");
|
||||
}
|
||||
} else if (init->kind == Ast_AsmTemplate) {
|
||||
} else if (init->kind == Ast_AsmGroup) {
|
||||
ast_node(ag, AsmGroup, init);
|
||||
e = alloc_entity_proc_group(d->scope, token, nullptr);
|
||||
e->ProcGroup.is_asm_group = true;
|
||||
if (fl != nullptr) {
|
||||
error(name, "Asm template groups are not allowed within a foreign block");
|
||||
}
|
||||
}else if (init->kind == Ast_AsmTemplate) {
|
||||
if (c->scope->flags&ScopeFlag_Type) {
|
||||
error(name, "Asm templates are not allowed within a struct");
|
||||
continue;
|
||||
|
||||
@@ -316,6 +316,7 @@ struct Entity {
|
||||
bool is_objc_class_method : 1;
|
||||
} Procedure;
|
||||
struct {
|
||||
bool is_asm_group;
|
||||
Array<Entity *> entities;
|
||||
} ProcGroup;
|
||||
struct {
|
||||
|
||||
@@ -237,6 +237,9 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
|
||||
case Ast_ProcGroup:
|
||||
n->ProcGroup.args = clone_ast_array(n->ProcGroup.args, f);
|
||||
break;
|
||||
case Ast_AsmGroup:
|
||||
n->AsmGroup.args = clone_ast_array(n->AsmGroup.args, f);
|
||||
break;
|
||||
case Ast_ProcLit:
|
||||
n->ProcLit.type = clone_ast(n->ProcLit.type, f);
|
||||
n->ProcLit.body = clone_ast(n->ProcLit.body, f);
|
||||
@@ -975,6 +978,15 @@ gb_internal Ast *ast_proc_group(AstFile *f, Token token, Token open, Token close
|
||||
return result;
|
||||
}
|
||||
|
||||
gb_internal Ast *ast_asm_group(AstFile *f, Token token, Token open, Token close, Array<Ast *> const &args) {
|
||||
Ast *result = alloc_ast_node(f, Ast_AsmGroup);
|
||||
result->AsmGroup.token = token;
|
||||
result->AsmGroup.open = open;
|
||||
result->AsmGroup.close = close;
|
||||
result->AsmGroup.args = slice_from_array(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
gb_internal Ast *ast_proc_lit(AstFile *f, Ast *type, Ast *body, u64 tags, Token where_token, Array<Ast *> const &where_clauses) {
|
||||
Ast *result = alloc_ast_node(f, Ast_ProcLit);
|
||||
result->ProcLit.type = type;
|
||||
@@ -2466,6 +2478,8 @@ gb_internal Ast *parse_asm_operand(AstFile *f, bool allow_memory_operand) {
|
||||
case Token_Float:
|
||||
case Token_Rune:
|
||||
return ast_basic_lit(f, advance_token(f));
|
||||
case Token_OpenParen:
|
||||
return parse_expr(f, false);
|
||||
case Token_OpenBracket:
|
||||
if (allow_memory_operand) {
|
||||
Token open = expect_token(f, Token_OpenBracket);
|
||||
@@ -3484,6 +3498,36 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
|
||||
}
|
||||
|
||||
case Token_asm:
|
||||
if (peek_token(f).kind == Token_OpenBrace) { // asm group
|
||||
Token token = expect_token(f, Token_asm);
|
||||
Token open = expect_token(f, Token_OpenBrace);
|
||||
|
||||
auto args = array_make<Ast *>(ast_allocator(f));
|
||||
|
||||
while (f->curr_token.kind != Token_CloseBrace &&
|
||||
f->curr_token.kind != Token_EOF) {
|
||||
Ast *elem = parse_expr(f, false);
|
||||
|
||||
if (f->curr_token.kind == Token_where) {
|
||||
Token where = expect_token(f, Token_where);
|
||||
Ast *cond = parse_expr(f, false);
|
||||
elem = ast_binary_expr(f, where, elem, cond);
|
||||
}
|
||||
|
||||
array_add(&args, elem);
|
||||
if (!allow_field_separator(f)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Token close = expect_token(f, Token_CloseBrace);
|
||||
|
||||
if (args.count == 0) {
|
||||
syntax_error(token, "Expected a least 1 argument in a procedure group");
|
||||
}
|
||||
|
||||
return ast_asm_group(f, token, open, close, args);
|
||||
}
|
||||
return parse_asm_template(f);
|
||||
}
|
||||
|
||||
|
||||
@@ -451,6 +451,12 @@ struct AstSplitArgs {
|
||||
Token close; \
|
||||
Slice<Ast *> args; \
|
||||
}) \
|
||||
AST_KIND(AsmGroup, "asm group", struct { \
|
||||
Token token; \
|
||||
Token open; \
|
||||
Token close; \
|
||||
Slice<Ast *> args; \
|
||||
}) \
|
||||
AST_KIND(ProcLit, "procedure literal", struct { \
|
||||
Ast *type; \
|
||||
Ast *body; \
|
||||
|
||||
@@ -6,6 +6,7 @@ gb_internal Token ast_token(Ast *node) {
|
||||
case Ast_BasicLit: return node->BasicLit.token;
|
||||
case Ast_BasicDirective: return node->BasicDirective.token;
|
||||
case Ast_ProcGroup: return node->ProcGroup.token;
|
||||
case Ast_AsmGroup: return node->AsmGroup.token;
|
||||
case Ast_ProcLit: return ast_token(node->ProcLit.type);
|
||||
case Ast_CompoundLit:
|
||||
if (node->CompoundLit.type != nullptr) {
|
||||
@@ -164,6 +165,7 @@ Token ast_end_token(Ast *node) {
|
||||
case Ast_BasicLit: return node->BasicLit.token;
|
||||
case Ast_BasicDirective: return node->BasicDirective.token;
|
||||
case Ast_ProcGroup: return node->ProcGroup.close;
|
||||
case Ast_AsmGroup: return node->AsmGroup.close;
|
||||
case Ast_ProcLit:
|
||||
if (node->ProcLit.body) {
|
||||
return ast_end_token(node->ProcLit.body);
|
||||
|
||||
Reference in New Issue
Block a user