From 5ac28cdecdbf38824cc6179add56f2c09234aa32 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 9 Aug 2026 18:24:56 +0100 Subject: [PATCH] Begin work on semantically type checking `asm` templates --- src/check_decl.cpp | 513 +++++++++++++++++++++++++++++++++++++++++++- src/check_expr.cpp | 137 ++++++++++++ src/checker.cpp | 14 ++ src/entity.cpp | 48 ++++- src/exact_value.cpp | 29 +-- src/parser.cpp | 42 +++- src/parser.hpp | 11 +- src/parser_pos.cpp | 4 +- 8 files changed, 769 insertions(+), 29 deletions(-) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index edb9f4883..f1ab06a52 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -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 *asm_template_entity_decls) { + Type *tuple = alloc_type_tuple(); + if (_params == nullptr) { + return tuple; + } + ast_node(field_list, FieldList, _params); + Slice params = field_list->list; + + Array 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 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 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 const &specs, Array *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; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index e73ec65c4..566a0ff28 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -361,6 +361,7 @@ gb_internal void check_scope_decls(CheckerContext *c, Slice 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; diff --git a/src/checker.cpp b/src/checker.cpp index 23ac59a3e..0d8c6a841 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -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); } diff --git a/src/entity.cpp b/src/entity.cpp index d3170c823..c439404b6 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -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 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); diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 2f82a52cf..2895f7e59 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -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; diff --git a/src/parser.cpp b/src/parser.cpp index 0e2f77a93..39d87669d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -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(¶ms->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 asm_specs = {}; Slice asm_clobbers = {}; + if (file_allow_newline(f)) { + skip_possible_newline(f); + } + if (f->curr_token.kind == Token_OpenBracket) { Array 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); diff --git a/src/parser.hpp b/src/parser.hpp index af8c3af09..0390937c0 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -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 operands; \ }) \ AST_KIND(AsmMemoryOperand, "asm memory operand", struct { \ diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp index 0fe27d2cc..3313d831d 100644 --- a/src/parser_pos.cpp +++ b/src/parser_pos.cpp @@ -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;