Begin work on semantically type checking asm templates

This commit is contained in:
gingerBill
2026-08-09 18:24:56 +01:00
parent 5e49e6c1a5
commit 5ac28cdecd
8 changed files with 769 additions and 29 deletions

View File

@@ -1983,10 +1983,515 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D
AttributeContext ac = {};
check_decl_attributes(ctx, d->attributes, proc_group_attribute, &ac);
check_objc_methods(ctx, pg_entity, ac);
}
gb_internal bool is_valid_asm_parameter_type(Type *type) {
if (is_type_integer(type)) {
return true;
}
if (is_type_float(type)) {
return true;
}
if (is_type_boolean(type)) {
return true;
}
if (is_type_pointer(type) || is_type_multi_pointer(type)) {
return true;
}
if (is_type_simd_vector(type)) {
return true;
}
return false;
}
gb_internal Type *check_asm_template_signature_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool input_parameters, Array<AsmTemplateEntityDecl> *asm_template_entity_decls) {
Type *tuple = alloc_type_tuple();
if (_params == nullptr) {
return tuple;
}
ast_node(field_list, FieldList, _params);
Slice<Ast *> params = field_list->list;
Array<Entity *> variables = {};
variables.allocator = heap_allocator();
for (Ast *param : params) {
ast_node(field, Field, param);
bool prev = ctx->allow_polymorphic_types;
ctx->allow_polymorphic_types = false;
Type *type = check_type(ctx, field->type);
ctx->allow_polymorphic_types = prev;
if (!is_valid_asm_parameter_type(type)) {
gbString s = type_to_string(type);
error(field->type, "Invalid type for an asm template. It must be an integer, float, boolean, pointer, multi-pointer, or #simd vector, got '%s'", type);
gb_string_free(s);
continue;
}
for_array(j, field->names) {
Ast *name = field->names[j];
bool is_poly_name = false;
switch (name->kind) {
case Ast_Ident:
break;
case Ast_PolyType:
GB_ASSERT(name->PolyType.specialization == nullptr);
is_poly_name = true;
name = name->PolyType.type;
break;
}
if (!ast_node_expect(name, Ast_Ident)) {
continue;
}
if (is_blank_ident(name)) {
error(name, "All parameters must have a name in an asm template");
continue;
}
Token name_token = name->Ident.token;
Entity *entity = alloc_entity_param(scope, name_token, type, false, /*is_value*/true);
entity->flags |= EntityFlag_Used;
if (is_poly_name) {
entity->flags |= EntityFlag_PolyConst;
if (is_type_internally_pointer_like(type)) {
error(name, "Parameters with a pointer-like type cannot be used as $ immediates");
}
}
Entity *found = scope_insert(scope, entity);
if (found == nullptr) {
array_add(&variables, entity);
AsmTemplateEntityDecl ed = {};
ed.entity = entity;
ed.kind = AsmTemplateEntityDecl_Register;
if (is_type_internally_pointer_like(type)) {
ed.kind = AsmTemplateEntityDecl_Memory;
}
if (is_poly_name) {
ed.kind = AsmTemplateEntityDecl_Immediate;
}
if (input_parameters) {
ed.param_group = AsmTemplateEntityDeclParamGroup_Input;
} else {
ed.param_group = AsmTemplateEntityDeclParamGroup_Output;
}
array_add(asm_template_entity_decls, ed);
} else {
TokenPos pos = found->token.pos;
error(name_token,
"Redeclaration of '%.*s' in this scope\n"
"\tat %s",
LIT(name_token.string), token_pos_to_string(pos));
entity = found;
}
}
}
tuple->Tuple.variables = slice_from_array(variables);
return tuple;
}
gb_internal AsmTemplateEntityDeclParamGroup check_asm_find_group(Entity *entity, Array<AsmTemplateEntityDecl> const &asm_template_entity_decls) {
for (auto const &ed : asm_template_entity_decls) {
if (ed.entity == entity) {
return ed.param_group;
}
}
return AsmTemplateEntityDeclParamGroup_Unknown;
};
gb_internal AsmTemplateEntityDeclKind check_asm_find_kind(Entity *entity, Array<AsmTemplateEntityDecl> const &asm_template_entity_decls) {
for (auto const &ed : asm_template_entity_decls) {
if (ed.entity == entity) {
return ed.kind;
}
}
return AsmTemplateEntityDecl_Invalid;
};
gb_internal void check_asm_specs(CheckerContext *ctx, Scope *scope, Slice<Ast *> const &specs, Array<AsmTemplateEntityDecl> *asm_template_entity_decls) {
for (Ast *spec_ : specs) {
if (spec_->kind != Ast_AsmSpec) {
continue;
}
ast_node(spec, AsmSpec, spec_);
GB_ASSERT(spec->name->kind == Ast_Ident);
Entity *input = scope_lookup(scope, spec->name->Ident.interned, spec->name->Ident.hash);
bool must_check_value = false;
if (spec->tied_name == nullptr) {
if (spec->type != nullptr) {
Type *type = check_type(ctx, spec->type);
if (!is_valid_asm_parameter_type(type)) {
gbString s = type_to_string(type);
error(spec->type, "Invalid type for an asm template. It must be an integer, float, boolean, pointer, multi-pointer, or #simd vector, got '%s'", type);
gb_string_free(s);
continue;
}
Token name_token = spec->name->Ident.token;
Entity *entity = alloc_entity_param(scope, name_token, type, false, /*is_value*/true);
entity->flags |= EntityFlag_Used;
Entity *found = scope_insert(scope, entity);
if (found == nullptr) {
AsmTemplateEntityDecl ed = {};
ed.entity = entity;
ed.kind = AsmTemplateEntityDecl_Register;
if (is_type_internally_pointer_like(type)) {
ed.kind = AsmTemplateEntityDecl_Memory;
}
ed.param_group = AsmTemplateEntityDeclParamGroup_Scratch;
array_add(asm_template_entity_decls, ed);
} else {
TokenPos pos = found->token.pos;
error(name_token,
"Redeclaration of '%.*s' in this scope\n"
"\tat %s",
LIT(name_token.string), token_pos_to_string(pos));
entity = found;
continue;
}
} else if (input == nullptr) {
error(spec->name, "Undefined parameter declaration '%.*s'", LIT(spec->name->Ident.token.string));
continue;
}
} else {
GB_ASSERT(spec->tied_name->kind == Ast_Ident);
if (spec->type != nullptr) {
error(spec->type, "Tied register definitions cannot have a defined type since the values are already defined");
}
if (input == nullptr) {
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);
if (output == nullptr) {
error(spec->name, "Undefined parameter declaration '%.*s'", LIT(spec->name->Ident.token.string));
continue;
}
auto input_group = check_asm_find_group(input, *asm_template_entity_decls);
auto output_group = check_asm_find_group(output, *asm_template_entity_decls);
if (input_group != AsmTemplateEntityDeclParamGroup_Input) {
error(input->token, "Parameter tied with '%.*s' must be an input parameter", LIT(output->token.string));
continue;
}
if (output_group != AsmTemplateEntityDeclParamGroup_Output) {
error(output->token, "Parameter tied with '%.*s' must be an output parameter", LIT(input->token.string));
continue;
}
must_check_value = true;
}
if (spec->value != nullptr) {
// TODO(bill): check registers
}
}
}
gb_internal void check_asm_instruction_operand(CheckerContext *ctx, Entity *entity, Operand *operand, Ast *expr, bool allow_memory_operands) {
if (expr == nullptr) {
return;
}
operand->expr = expr;
operand->mode = Addressing_Invalid;
operand->type = t_invalid;
GB_ASSERT(entity->kind == Entity_AsmTemplate);
auto *ate = &entity->AsmTemplate;
Scope *param_scope = ate->param_scope;
Scope *label_scope = ate->label_scope;
gb_unused(param_scope);
gb_unused(label_scope);
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));
return;
}
i->entity = found;
operand->mode = Addressing_Value;
operand->type = found->type;
return;
case_end;
case_ast_node(bl, BasicLit, expr);
check_expr(ctx, operand, expr);
return;
case_end;
case_ast_node(i, AsmRegister, expr);
// TODO(bill): Check asm register
return;
case_end;
case_ast_node(mem_op, AsmMemoryOperand, expr);
if (!allow_memory_operands) {
break;
}
Operand base = {};
Operand index = {};
Operand scale = {};
Operand disp = {};
check_asm_instruction_operand(ctx, entity, &base, mem_op->base, false);
check_asm_instruction_operand(ctx, entity, &index, mem_op->index, false);
check_asm_instruction_operand(ctx, entity, &scale, mem_op->scale, false);
check_asm_instruction_operand(ctx, entity, &disp, mem_op->disp, false);
for (int i = 0; base.expr && i == 0; i++) {
if (base.expr->kind == Ast_AsmRegister) {
// Okay for now
} else {
Entity *param_entity = entity_of_node(base.expr);
if (param_entity == nullptr || param_entity->kind != Entity_Variable) {
gbString s = expr_to_string(base.expr);
error(base.expr, "A base value must a memory parameter, got %s", s);
gb_string_free(s);
break;
}
auto kind = check_asm_find_kind(param_entity, ate->decls);
if (kind != AsmTemplateEntityDecl_Memory) {
gbString s = expr_to_string(base.expr);
error(base.expr, "A scale must be a memory parameter, got %s", s);
gb_string_free(s);
break;
}
}
}
for (int i = 0; index.expr && i == 0; i++) {
if (index.expr->kind == Ast_AsmRegister) {
// Okay for now
} else {
Entity *param_entity = entity_of_node(index.expr);
if (param_entity == nullptr || param_entity->kind != Entity_Variable) {
gbString s = expr_to_string(index.expr);
error(index.expr, "An index value must an integer, got %s", s);
gb_string_free(s);
break;
}
auto kind = check_asm_find_kind(param_entity, ate->decls);
switch (kind) {
case AsmTemplateEntityDecl_Register:
case AsmTemplateEntityDecl_Immediate:
// okay:
break;
default:
{
gbString s = expr_to_string(index.expr);
error(index.expr, "An index must be an integer value, got %s", s);
gb_string_free(s);
}
break;
}
}
}
for (int i = 0; scale.expr && i == 0; i++) {
if (!is_type_integer(scale.type)) {
gbString s = expr_to_string(scale.expr);
error(scale.expr, "A scale must be a constant integer or an immediate, got %s", s);
gb_string_free(s);
break;
}
if (scale.mode == Addressing_Constant) {
if (scale.value.kind != ExactValue_Integer) {
gbString s = exact_value_to_string(scale.value);
error(scale.expr, "A scale must be a constant integer or an immediate, got %s", s);
gb_string_free(s);
break;
}
} else {
Entity *param_entity = entity_of_node(scale.expr);
if (param_entity == nullptr || param_entity->kind != Entity_Variable) {
gbString s = expr_to_string(scale.expr);
error(scale.expr, "A scale must be a constant integer or an immediate, got %s", s);
gb_string_free(s);
break;
}
auto kind = check_asm_find_kind(param_entity, ate->decls);
if (kind != AsmTemplateEntityDecl_Immediate) {
gbString s = expr_to_string(scale.expr);
error(scale.expr, "A scale must be a constant integer or an immediate, got %s", s);
gb_string_free(s);
break;
}
}
}
for (int i = 0; disp.expr && i == 0; i++) {
if (disp.expr->kind == Ast_AsmRegister) {
// Okay for now
} else {
Entity *param_entity = entity_of_node(disp.expr);
if (param_entity == nullptr || param_entity->kind != Entity_Variable) {
gbString s = expr_to_string(disp.expr);
error(disp.expr, "An displacement value must an integer, got %s", s);
gb_string_free(s);
break;
}
auto kind = check_asm_find_kind(param_entity, ate->decls);
switch (kind) {
case AsmTemplateEntityDecl_Register:
case AsmTemplateEntityDecl_Immediate:
// okay:
break;
default:
{
gbString s = expr_to_string(disp.expr);
error(disp.expr, "An displacement must be an integer value, got %s", s);
gb_string_free(s);
}
break;
}
}
}
return;
case_end;
case_ast_node(label, AsmLabelDecl, expr);
ast_node(name, Ident, label->name);
Entity *found = scope_lookup(label_scope, name->interned, name->hash);
if (found == nullptr) {
error(expr, "Undeclared asm label '.%.*s'", LIT(name->token.string));
}
name->entity = found;
return;
case_end;
}
{
gbString s = expr_to_string(expr);
error(expr, "Invalid asm operand, got %s", s);
gb_string_free(s);
}
return;
}
gb_internal void check_asm_template(CheckerContext *ctx, Entity *entity, DeclInfo *d) {
GB_ASSERT(entity->kind == Entity_AsmTemplate);
auto *ate = &entity->AsmTemplate;
String asm_template_name = entity->token.string;
gb_unused(asm_template_name);
ast_node(at, AsmTemplate, d->init_expr);
GB_ASSERT(at->signature != nullptr);
if (at->signature->kind != Ast_ProcType) {
error(at->signature, "Expected a valid signature, got %.*s", LIT(ast_strings[at->signature->kind]));
return;
}
AstProcType *pt = &at->signature->ProcType;
ate->param_scope = create_scope(nullptr, nullptr);
ate->label_scope = create_scope(nullptr, nullptr);
ate->decls.allocator = heap_allocator();
Type *params = check_asm_template_signature_params(ctx, ate->param_scope, pt->params, true, &ate->decls);
Type *results = check_asm_template_signature_params(ctx, ate->param_scope, pt->results, false, &ate->decls);
Type *type = alloc_type_proc(ate->param_scope, params, params->Tuple.variables.count, results, results->Tuple.variables.count, false, pt->calling_convention);
type->Proc.diverging = pt->diverging;
check_asm_specs(ctx, ate->param_scope, at->specs, &ate->decls);
{ // check clobbers
for (Ast *clobber_ : at->clobbers) {
ast_node(clobber, AsmClobber, clobber_);
switch (clobber->value->kind) {
case Ast_AsmRegister:
// TODO(bill): register check
break;
case Ast_Ident:
{
String str = clobber->value->Ident.token.string;
if (str == "cc") {
// okay
} else if (str == "memory") {
// okay
} else {
error(clobber->value, "Expected either a register, 'cc', or 'memory' for a '#clobber' specification, got '%.*s'", LIT(str));
}
break;
}
default:
error(clobber->value, "Expected either a register, 'cc', or 'memory' for a '#clobber' specification");
break;
}
}
}
// collect label decls
for (Ast *instruction_ : at->instructions) {
switch (instruction_->kind) {
case_ast_node(label, AsmLabelDecl, instruction_);
GB_ASSERT(label->name->kind == Ast_Ident);
Ast *name = label->name;
if (is_blank_ident(name)) {
error(name, "Asm label definition cannot be '_'");
continue;
}
Entity *label_entity = alloc_entity_label(ate->label_scope, name->Ident.token, nullptr, instruction_, nullptr);
Entity *found = scope_insert(ate->label_scope, label_entity);
if (found != nullptr) {
TokenPos pos = found->token.pos;
error(name,
"Redeclaration of the label '%.*s' in this scope\n"
"\tat %s",
LIT(name->Ident.token.string), token_pos_to_string(pos));
continue;
}
name->Ident.entity = label_entity;
case_end;
}
}
for (Ast *instruction_ : at->instructions) {
switch (instruction_->kind) {
case_ast_node(instr, AsmInstruction, instruction_);
GB_ASSERT(instr->name->kind == Ast_Ident);
for (Ast *expr : instr->operands) {
Operand operand = {};
check_asm_instruction_operand(ctx, entity, &operand, expr, /*allow_memory_operands*/true);
}
case_end;
case_ast_node(label, AsmLabelDecl, instruction_);
// already done
case_end;
default:
error(instruction_, "Unexpected instruction in asm template");
break;
}
}
}
gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_type) {
if (e->state == EntityState_Resolved) {
return;
@@ -2062,6 +2567,10 @@ gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d,
case Entity_ProcGroup:
check_proc_group_decl(&c, e, d);
break;
case Entity_AsmTemplate:
check_asm_template(&c, e, d);
break;
}
e->state = EntityState_Resolved;

View File

@@ -361,6 +361,7 @@ gb_internal void check_scope_decls(CheckerContext *c, Slice<Ast *> const &nodes,
case Entity_Constant:
case Entity_TypeName:
case Entity_Procedure:
case Entity_AsmTemplate:
break;
default:
continue;
@@ -2067,6 +2068,10 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
o->mode = Addressing_Value;
break;
case Entity_AsmTemplate:
o->mode = Addressing_Value;
break;
default:
compiler_error("Unknown EntityKind %.*s", LIT(entity_strings[e->kind]));
break;
@@ -12340,6 +12345,11 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
return kind;
case_end;
case_ast_node(asm_template, AsmTemplate, node);
error(node, "Illegal use of an asm template outside of a named constant value declaration");
o->mode = Addressing_Invalid;
case_end;
case_ast_node(i, Implicit, node);
switch (i->kind) {
case Token_context:
@@ -13585,6 +13595,133 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
}
str = gb_string_appendc(str, "}");
case_end;
case_ast_node(at, AsmTemplate, node);
str = gb_string_appendc(str, "asm");
{
ast_node(pt, ProcType, at->signature);
str = gb_string_appendc(str, "(");
str = write_expr_to_string(str, pt->params, shorthand);
str = gb_string_appendc(str, ")");
if (pt->results != nullptr) {
str = gb_string_appendc(str, " -> ");
bool parens_needed = false;
if (pt->results && pt->results->kind == Ast_FieldList) {
for (Ast *field : pt->results->FieldList.list) {
ast_node(f, Field, field);
if (f->names.count != 0) {
parens_needed = true;
break;
}
}
}
if (parens_needed) {
str = gb_string_append_rune(str, '(');
}
str = write_expr_to_string(str, pt->results, shorthand);
if (parens_needed) {
str = gb_string_append_rune(str, ')');
}
}
}
if (at->has_side_effects) {
str = gb_string_appendc(str, " #side_effects");
}
if (at->is_align_stack) {
str = gb_string_appendc(str, " #align_stack");
}
if (at->specs.count) {
str = gb_string_append_rune(str, '[');
for_array(j, at->specs) {
if (j > 0) {
str = gb_string_appendc(str, ", ");
}
Ast *spec = at->specs[j];
str = write_expr_to_string(str, spec, shorthand);
}
str = gb_string_append_rune(str, ']');
}
str = gb_string_append_rune(str, '{');
for_array(j, at->instructions) {
if (j > 0) {
str = gb_string_appendc(str, "; ");
}
Ast *instr = at->instructions[j];
str = write_expr_to_string(str, instr, shorthand);
if (instr->kind == Ast_AsmLabelDecl) {
str = gb_string_appendc(str, ":");
}
}
str = gb_string_append_rune(str, '}');
case_end;
case_ast_node(ar, AsmRegister, node);
str = gb_string_appendc(str, "%");
str = gb_string_append_length(str, ar->name.string.text, ar->name.string.len);
case_end;
case_ast_node(spec, AsmSpec, node);
if (spec->name) {
str = write_expr_to_string(str, spec->name, shorthand);
if (spec->tied_name) {
str = gb_string_appendc(str, " -> ");
str = write_expr_to_string(str, spec->tied_name, shorthand);
}
}
if (spec->type) {
str = gb_string_appendc(str, ": ");
str = write_expr_to_string(str, spec->type, shorthand);
}
if (spec->value) {
str = gb_string_appendc(str, " = ");
str = write_expr_to_string(str, spec->value, shorthand);
}
case_end;
case_ast_node(clobber, AsmClobber, node);
str = gb_string_appendc(str, "#clobber ");
str = write_expr_to_string(str, clobber->value, shorthand);
case_end;
case_ast_node(label, AsmLabelDecl, node);
str = gb_string_appendc(str, ".");
str = write_expr_to_string(str, label->name, shorthand);
case_end;
case_ast_node(instr, AsmInstruction, node);
str = write_expr_to_string(str, instr->name, shorthand);
for_array(j, instr->operands) {
if (j == 0) {
str = gb_string_appendc(str, " ");
} else {
str = gb_string_appendc(str, ", ");
}
Ast *operand = instr->operands[j];
str = write_expr_to_string(str, operand, shorthand);
}
case_end;
case_ast_node(op, AsmMemoryOperand, node);
str = gb_string_appendc(str, "[");
str = write_expr_to_string(str, op->base, shorthand);
if (op->index) {
str = gb_string_appendc(str, " + ");
str = write_expr_to_string(str, op->index, shorthand);
if (op->scale) {
str = gb_string_appendc(str, "*");
str = write_expr_to_string(str, op->scale, shorthand);
}
}
if (op->disp) {
str = gb_string_appendc(str, " + ");
str = write_expr_to_string(str, op->disp, shorthand);
}
str = gb_string_appendc(str, "]");
case_end;
}
return str;

View File

@@ -1834,6 +1834,9 @@ retry:;
expr = we->cond->tav.value.value_bool ? we->x : we->y;
goto retry;
case_end;
case_ast_node(label, AsmLabelDecl, expr);
return entity_of_node(label->name);
case_end;
}
return nullptr;
}
@@ -5060,6 +5063,17 @@ 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) {
if (c->scope->flags&ScopeFlag_Type) {
error(name, "Asm templates are not allowed within a struct");
continue;
}
ast_node(at, AsmTemplate, init);
e = alloc_entity_asm_template(d->scope, token, nullptr, init);
if (fl != nullptr) {
error(name, "Asm templates are not allowed within a foreign block");
}
d->init_expr = init;
} else {
e = alloc_entity_constant(d->scope, token, nullptr, empty_exact_value);
}

View File

@@ -15,7 +15,8 @@ struct DeclInfo;
ENTITY_KIND(ImportName) \
ENTITY_KIND(LibraryName) \
ENTITY_KIND(Nil) \
ENTITY_KIND(Label)
ENTITY_KIND(Label) \
ENTITY_KIND(AsmTemplate)
enum EntityKind {
#define ENTITY_KIND(k) GB_JOIN2(Entity_, k),
@@ -159,6 +160,32 @@ gb_internal TypeNameObjCMetadata *create_type_name_obj_c_metadata() {
return md;
}
enum AsmTemplateEntityDeclKind : u8 {
AsmTemplateEntityDecl_Invalid,
AsmTemplateEntityDecl_Register,
AsmTemplateEntityDecl_Memory,
AsmTemplateEntityDecl_Immediate,
AsmTemplateEntityDecl_COUNT
};
enum AsmTemplateEntityDeclParamGroup : u8 {
AsmTemplateEntityDeclParamGroup_Unknown,
AsmTemplateEntityDeclParamGroup_Input,
AsmTemplateEntityDeclParamGroup_Output,
AsmTemplateEntityDeclParamGroup_Scratch,
AsmTemplateEntityDeclParamGroup_COUNT
};
struct AsmTemplateEntityDecl {
Entity * entity;
Entity * tied_entity;
AsmTemplateEntityDeclKind kind;
AsmTemplateEntityDeclParamGroup param_group;
u16 register_map;
};
// An Entity is a named "thing" in the language
struct Entity {
EntityKind kind;
@@ -300,6 +327,15 @@ struct Entity {
Ast *node;
Ast *parent;
} Label;
struct {
Ast *node;
bool has_side_effects;
bool is_align_stack;
Scope *param_scope;
Scope *label_scope;
Array<AsmTemplateEntityDecl> decls;
} AsmTemplate;
};
};
@@ -477,8 +513,14 @@ gb_internal Entity *alloc_entity_library_name(Scope *scope, Token token, Type *t
}
gb_internal Entity *alloc_entity_asm_template(Scope *scope, Token token, Type *type, Ast *node) {
GB_ASSERT(node->kind == Ast_AsmTemplate);
Entity *entity = alloc_entity(Entity_AsmTemplate, scope, token, type);
entity->AsmTemplate.node = node;
entity->AsmTemplate.has_side_effects = node->AsmTemplate.has_side_effects;
entity->AsmTemplate.is_align_stack = node->AsmTemplate.is_align_stack;
return entity;
}
gb_internal Entity *alloc_entity_nil(String name, Type *type) {
Entity *entity = alloc_entity(Entity_Nil, nullptr, make_token_ident(name), type);

View File

@@ -15,19 +15,20 @@ struct Quaternion256 {
};
enum ExactValueKind {
ExactValue_Invalid = 0,
ExactValue_Invalid = 0,
ExactValue_Bool = 1,
ExactValue_String = 2,
ExactValue_Integer = 3,
ExactValue_Float = 4,
ExactValue_Complex = 5,
ExactValue_Quaternion = 6,
ExactValue_Pointer = 7,
ExactValue_Compound = 8,
ExactValue_Procedure = 9,
ExactValue_Typeid = 10,
ExactValue_String16 = 11,
ExactValue_Bool = 1,
ExactValue_String = 2,
ExactValue_Integer = 3,
ExactValue_Float = 4,
ExactValue_Complex = 5,
ExactValue_Quaternion = 6,
ExactValue_Pointer = 7,
ExactValue_Compound = 8,
ExactValue_Procedure = 9,
ExactValue_Typeid = 10,
ExactValue_String16 = 11,
ExactValue_AsmTemplate = 12,
ExactValue_Count,
};
@@ -62,6 +63,7 @@ struct ExactValue {
Ast * value_procedure;
Type * value_typeid;
String16 value_string16;
Ast * value_asm_template;
};
};
@@ -107,6 +109,9 @@ gb_internal uintptr hash_exact_value(ExactValue v) {
case ExactValue_Procedure:
res = ptr_map_hash_key(v.value_procedure);
break;
case ExactValue_AsmTemplate:
res = ptr_map_hash_key(v.value_asm_template);
break;
case ExactValue_Typeid:
res = ptr_map_hash_key(v.value_typeid);
break;

View File

@@ -542,6 +542,7 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
n->AsmLabelDecl.name = clone_ast(n->AsmLabelDecl.name, f);
break;
case Ast_AsmInstruction:
n->AsmInstruction.name = clone_ast(n->AsmInstruction.name, f);
n->AsmInstruction.operands = clone_ast_array(n->AsmInstruction.operands, f);
break;
case Ast_AsmMemoryOperand:
@@ -2512,7 +2513,7 @@ gb_internal Ast *parse_asm_instruction(AstFile *f) {
/*fallthrough*/
case Token_Ident:
{
Token name = advance_token(f);
Ast *name = parse_ident(f);
auto operands = parse_asm_operands(f);
Ast *instruction = alloc_ast_node(f, Ast_AsmInstruction);
instruction->AsmInstruction.name = name;
@@ -2535,6 +2536,33 @@ gb_internal Ast *parse_asm_instruction(AstFile *f) {
return nullptr;
}
gb_internal Ast *parse_results(AstFile *f, bool *diverging);
gb_internal bool is_field_list_generic(AstFieldList *field_list, bool check_names);
gb_internal Ast *parse_asm_signature(AstFile *f, Token asm_token) {
Ast *params = nullptr;
Ast *results = nullptr;
bool diverging = false;
ProcCallingConvention cc = ProcCC_InlineAsm;
expect_token(f, Token_OpenParen);
f->expr_level += 1;
params = parse_field_list(f, nullptr, FieldFlag_Signature, Token_CloseParen, false, false);
if (file_allow_newline(f)) {
skip_possible_newline(f);
}
f->expr_level -= 1;
expect_token_after(f, Token_CloseParen, "asm template parameter list");
results = parse_results(f, &diverging);
u64 tags = 0;
bool is_generic = is_field_list_generic(&params->FieldList, true);
if (!is_generic && (results != nullptr)) {
is_generic = is_field_list_generic(&results->FieldList, false);
}
return ast_proc_type(f, asm_token, params, results, tags, cc, is_generic, diverging);
}
gb_internal Ast *parse_asm_template(AstFile *f) {
Token token = expect_token(f, Token_asm);
@@ -2542,6 +2570,8 @@ gb_internal Ast *parse_asm_template(AstFile *f) {
bool has_side_effects = false;
bool is_align_stack = false;
Ast *signature = parse_asm_signature(f, token);
while (f->curr_token.kind == Token_Hash) {
advance_token(f);
if (f->curr_token.kind == Token_Ident) {
@@ -2565,12 +2595,13 @@ gb_internal Ast *parse_asm_template(AstFile *f) {
}
}
Ast *signature = parse_proc_type(f, token);
Slice<Ast *> asm_specs = {};
Slice<Ast *> asm_clobbers = {};
if (file_allow_newline(f)) {
skip_possible_newline(f);
}
if (f->curr_token.kind == Token_OpenBracket) {
Array<Ast *> specs = {};
specs.allocator = heap_allocator();
@@ -2630,6 +2661,9 @@ gb_internal Ast *parse_asm_template(AstFile *f) {
}
}
Token close = expect_token(f, Token_CloseBracket);
if (file_allow_newline(f)) {
skip_possible_newline(f);
}
asm_specs = slice_from_array(specs);
asm_clobbers = slice_from_array(clobbers);

View File

@@ -483,11 +483,10 @@ struct AstSplitArgs {
Token name; \
}) \
AST_KIND(AsmSpec, "asm specification", struct { \
Ast *name; \
Ast *tied_name; \
Ast *type; \
Ast *value; \
bool is_temporary_decl; \
Ast *name; \
Ast *tied_name; \
Ast *type; \
Ast *value; \
}) \
AST_KIND(AsmClobber, "asm clobber", struct { \
Token token; \
@@ -498,7 +497,7 @@ struct AstSplitArgs {
Ast * name; \
}) \
AST_KIND(AsmInstruction, "asm instruction", struct { \
Token name; \
Ast * name; \
Slice<Ast *> operands; \
}) \
AST_KIND(AsmMemoryOperand, "asm memory operand", struct { \

View File

@@ -24,7 +24,7 @@ gb_internal Token ast_token(Ast *node) {
case Ast_AsmLabelDecl:
return node->AsmLabelDecl.token;
case Ast_AsmInstruction:
return node->AsmInstruction.name;
return ast_token(node->AsmInstruction.name);
case Ast_AsmMemoryOperand:
return node->AsmMemoryOperand.open;
@@ -194,7 +194,7 @@ Token ast_end_token(Ast *node) {
if (node->AsmInstruction.operands.count > 0) {
return ast_end_token(node->AsmInstruction.operands[node->AsmInstruction.operands.count-1]);
}
return node->AsmInstruction.name;
return ast_end_token(node->AsmInstruction.name);
case Ast_AsmMemoryOperand:
return node->AsmMemoryOperand.close;