diff --git a/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin b/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin index 3eb57294a..89c3cf596 100644 --- a/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin +++ b/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin @@ -57,7 +57,30 @@ main :: proc() { strings.write_string(&sb, "\t\tMNEMONIC_COUNT\n"); } + { + strings.write_string(&sb, "\tenum Prefix : u8 {\n") + defer strings.write_string(&sb, "\t};\n"); + + count := uint(0) + ROW_COUNT :: 16 + for prefix in Prefix { + if count == 0 { + strings.write_string(&sb, "\t\t") + } + fmt.sbprintf(&sb, "PREFIX_%s, ", prefix) + + if count == ROW_COUNT-1 { + strings.write_string(&sb, "\n") + } + + count = (count + 1) % ROW_COUNT + } + strings.write_string(&sb, "\n\n"); + strings.write_string(&sb, "\t\tPREFIX_COUNT\n"); + } + strings.write_string(&sb, "\tstatic String const mnemonic_strings[MNEMONIC_COUNT];\n") + strings.write_string(&sb, "\tstatic String const prefix_strings[PREFIX_COUNT];\n") { strings.write_string(&sb, "\n"); @@ -102,7 +125,7 @@ main :: proc() { strings.write_string(&sb, "\tstatic u16 const register_codes[REG_COUNT];\n") - strings.write_string(&sb, "\tstatic String const register_names[REG_COUNT];\n") + strings.write_string(&sb, "\tstatic String const register_strings[REG_COUNT];\n") strings.write_string(&sb, "\n\n") { @@ -178,6 +201,7 @@ main :: proc() { fmt.sbprintf(&sb, "\tstatic u8 const raw_encode_forms[%d];\n", len(raw_encode_forms)) strings.write_string(&sb, "\tStringMap mnemonic_map;\n") + strings.write_string(&sb, "\tStringMap prefix_map;\n") strings.write_string(&sb, "\tStringMap register_map;\n") strings.write_string(&sb, "\tSlice ENCODE_RUNS;\n") strings.write_string(&sb, "\tSlice ENCODE_FORMS;\n") @@ -187,22 +211,40 @@ main :: proc() { defer strings.write_string(&sb, "\t}\n") strings.write_string(&sb, "\t\tstring_map_init(&mnemonic_map, MNEMONIC_COUNT*2);\n") - strings.write_string(&sb, "\t\tfor (u16 m = M_INVALID; m < MNEMONIC_COUNT; m++) {\n") + strings.write_string(&sb, "\t\tfor (u16 m = M_INVALID+1; m < MNEMONIC_COUNT; m++) {\n") strings.write_string(&sb, "\t\t\tstring_map_set(&mnemonic_map, mnemonic_strings[m], cast(Mnemonic)m);\n") strings.write_string(&sb, "\t\t}\n") + strings.write_string(&sb, "\t\tstring_map_init(&prefix_map, PREFIX_COUNT*2);\n") + strings.write_string(&sb, "\t\tfor (u8 r = PREFIX_INVALID+1; r < PREFIX_COUNT; r++) {\n") + strings.write_string(&sb, "\t\t\tstring_map_set(&prefix_map, prefix_strings[r], cast(Prefix)r);\n") + strings.write_string(&sb, "\t\t}\n") strings.write_string(&sb, "\t\tstring_map_init(®ister_map, REG_COUNT*2);\n") - strings.write_string(&sb, "\t\tfor (u16 r = REG_INVALID; r < REG_COUNT; r++) {\n") - strings.write_string(&sb, "\t\t\tstring_map_set(®ister_map, register_names[r], cast(Register)r);\n") + strings.write_string(&sb, "\t\tfor (u16 r = REG_INVALID+1; r < REG_COUNT; r++) {\n") + strings.write_string(&sb, "\t\t\tstring_map_set(®ister_map, register_strings[r], cast(Register)r);\n") strings.write_string(&sb, "\t\t}\n") strings.write_string(&sb, "\t\treturn true;\n") } { - strings.write_string(&sb, "\tMnemonic lookup(String const &name) {\n") + strings.write_string(&sb, "\tMnemonic mnemonic_lookup(String const &name) {\n") defer strings.write_string(&sb, "\t}\n") strings.write_string(&sb, "\t\tMnemonic *found = string_map_get(&mnemonic_map, name);\n") strings.write_string(&sb, "\t\treturn found ? *found : M_INVALID;\n") } + { + strings.write_string(&sb, "\tPrefix prefix_lookup(String const &name) {\n") + defer strings.write_string(&sb, "\t}\n") + + strings.write_string(&sb, "\t\tPrefix *found = string_map_get(&prefix_map, name);\n") + strings.write_string(&sb, "\t\treturn found ? *found : PREFIX_INVALID;\n") + } + { + strings.write_string(&sb, "\tRegister register_lookup(String const &name) {\n") + defer strings.write_string(&sb, "\t}\n") + + strings.write_string(&sb, "\t\tRegister *found = string_map_get(®ister_map, name);\n") + strings.write_string(&sb, "\t\treturn found ? *found : REG_INVALID;\n") + } { strings.write_string(&sb, "\tSlice encoding_forms(Mnemonic m) const {\n") defer strings.write_string(&sb, "\t}\n") @@ -244,12 +286,15 @@ main :: proc() { strings.write_string(&sb, "};\n") strings.write_string(&sb, "\n\n\n") + + fmt.sbprintf(&sb, "gb_internal Asm_{0:s} g_asm_{0:s};\n", ISA_NAME) + + strings.write_string(&sb, "\n\n\n") + { fmt.sbprintf(&sb, "String const Asm_{0:s}::mnemonic_strings[Asm_{0:s}::MNEMONIC_COUNT] {{\n", ISA_NAME) defer strings.write_string(&sb, "};\n"); - iota := 0 - count := uint(0) ROW_COUNT :: 16 for mnemonic in gen.Mnemonic { @@ -271,7 +316,33 @@ main :: proc() { strings.write_string(&sb, "\n") } - iota += 1 + count = (count + 1) % ROW_COUNT + } + strings.write_string(&sb, "\n"); + } + { + fmt.sbprintf(&sb, "String const Asm_{0:s}::prefix_strings[Asm_{0:s}::PREFIX_COUNT] {{\n", ISA_NAME) + defer strings.write_string(&sb, "};\n"); + + count := uint(0) + ROW_COUNT :: 16 + for prefix in Prefix { + if count == 0 { + strings.write_string(&sb, "\t") + } + + if prefix == .INVALID { + strings.write_string(&sb, "str_lit(\"\"), ") + } else { + str := strings.to_lower(reflect.enum_string(prefix)) + fmt.sbprintf(&sb, "str_lit(%q), ", str) + delete(str) + } + + if count == ROW_COUNT-1 { + strings.write_string(&sb, "\n") + } + count = (count + 1) % ROW_COUNT } strings.write_string(&sb, "\n"); @@ -300,7 +371,7 @@ main :: proc() { } { - fmt.sbprintf(&sb, "String const Asm_{0:s}::register_names[Asm_{0:s}::REG_COUNT] {{\n", ISA_NAME) + fmt.sbprintf(&sb, "String const Asm_{0:s}::register_strings[Asm_{0:s}::REG_COUNT] {{\n", ISA_NAME) defer strings.write_string(&sb, "};\n"); count := uint(0) @@ -377,7 +448,21 @@ main :: proc() { } } - +Prefix :: enum u8 { + INVALID, + ES, + CS, + SS, + DS, + REX, + EVEX, + FS, + GS, + VEX, + LOCK, + REPNE, + REP, +} diff --git a/src/asm_tables_amd64.cpp b/src/asm_tables_amd64.cpp index 7dfcad374..2d8ba1557 100644 --- a/src/asm_tables_amd64.cpp +++ b/src/asm_tables_amd64.cpp @@ -88,7 +88,13 @@ struct Asm_amd64 { MNEMONIC_COUNT }; + enum Prefix : u8 { + PREFIX_INVALID, PREFIX_ES, PREFIX_CS, PREFIX_SS, PREFIX_DS, PREFIX_REX, PREFIX_EVEX, PREFIX_FS, PREFIX_GS, PREFIX_VEX, PREFIX_LOCK, PREFIX_REPNE, PREFIX_REP, + + PREFIX_COUNT + }; static String const mnemonic_strings[MNEMONIC_COUNT]; + static String const prefix_strings[PREFIX_COUNT]; // Register classes (upper byte) static const u16 REG_CLASS_NONE = 0x000; @@ -126,7 +132,7 @@ struct Asm_amd64 { REG_COUNT }; static u16 const register_codes[REG_COUNT]; - static String const register_names[REG_COUNT]; + static String const register_strings[REG_COUNT]; enum OperandType : u8 { @@ -240,24 +246,37 @@ struct Asm_amd64 { static EncodeRun const raw_encode_runs[1176]; static u8 const raw_encode_forms[37680]; StringMap mnemonic_map; + StringMap prefix_map; StringMap register_map; Slice ENCODE_RUNS; Slice ENCODE_FORMS; bool init() { string_map_init(&mnemonic_map, MNEMONIC_COUNT*2); - for (u16 m = M_INVALID; m < MNEMONIC_COUNT; m++) { + for (u16 m = M_INVALID+1; m < MNEMONIC_COUNT; m++) { string_map_set(&mnemonic_map, mnemonic_strings[m], cast(Mnemonic)m); } + string_map_init(&prefix_map, PREFIX_COUNT*2); + for (u8 r = PREFIX_INVALID+1; r < PREFIX_COUNT; r++) { + string_map_set(&prefix_map, prefix_strings[r], cast(Prefix)r); + } string_map_init(®ister_map, REG_COUNT*2); - for (u16 r = REG_INVALID; r < REG_COUNT; r++) { - string_map_set(®ister_map, register_names[r], cast(Register)r); + for (u16 r = REG_INVALID+1; r < REG_COUNT; r++) { + string_map_set(®ister_map, register_strings[r], cast(Register)r); } return true; } - Mnemonic lookup(String const &name) { + Mnemonic mnemonic_lookup(String const &name) { Mnemonic *found = string_map_get(&mnemonic_map, name); return found ? *found : M_INVALID; } + Prefix prefix_lookup(String const &name) { + Prefix *found = string_map_get(&prefix_map, name); + return found ? *found : PREFIX_INVALID; + } + Register register_lookup(String const &name) { + Register *found = string_map_get(®ister_map, name); + return found ? *found : REG_INVALID; + } Slice encoding_forms(Mnemonic m) const { EncodeRun r = ENCODE_RUNS[m]; return slice_lower_and_count(ENCODE_FORMS, r.start, r.count); @@ -290,6 +309,10 @@ struct Asm_amd64 { +gb_internal Asm_amd64 g_asm_amd64; + + + String const Asm_amd64::mnemonic_strings[Asm_amd64::MNEMONIC_COUNT] { str_lit(""), str_lit("mov"), str_lit("movabs"), str_lit("movzx"), str_lit("movsx"), str_lit("movsxd"), str_lit("xchg"), str_lit("push"), str_lit("pop"), str_lit("lea"), str_lit("add"), str_lit("adc"), str_lit("sub"), str_lit("sbb"), str_lit("mul"), str_lit("imul"), str_lit("div"), str_lit("idiv"), str_lit("inc"), str_lit("dec"), str_lit("neg"), str_lit("cmp"), str_lit("and"), str_lit("or"), str_lit("xor"), str_lit("not"), str_lit("test"), str_lit("shl"), str_lit("shr"), str_lit("sar"), str_lit("rol"), str_lit("ror"), @@ -366,6 +389,9 @@ String const Asm_amd64::mnemonic_strings[Asm_amd64::MNEMONIC_COUNT] { str_lit("xsaves64"), str_lit("xrstors"), str_lit("xrstors64"), str_lit("prefetcht0"), str_lit("prefetcht1"), str_lit("prefetcht2"), str_lit("prefetchnta"), str_lit("prefetchw"), str_lit("clflushopt"), str_lit("clwb"), str_lit("cldemote"), str_lit("bswap"), str_lit("cmpxchg"), str_lit("cmpxchg8b"), str_lit("cmpxchg16b"), str_lit("xadd"), str_lit("bound"), str_lit("enter"), str_lit("leave"), str_lit("xlat"), str_lit("xlatb"), str_lit("movbe"), str_lit("rdrand"), str_lit("rdseed"), }; +String const Asm_amd64::prefix_strings[Asm_amd64::PREFIX_COUNT] { + str_lit(""), str_lit("es"), str_lit("cs"), str_lit("ss"), str_lit("ds"), str_lit("rex"), str_lit("evex"), str_lit("fs"), str_lit("gs"), str_lit("vex"), str_lit("lock"), str_lit("repne"), str_lit("rep"), +}; u16 const Asm_amd64::register_codes[Asm_amd64::REG_COUNT] { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, @@ -382,7 +408,7 @@ u16 const Asm_amd64::register_codes[Asm_amd64::REG_COUNT] { 3330, 3331, 3584, 3585, 3586, 3587, 3588, 3589, 3590, 3591, 3840, 3841, 3842, 3843, 3844, 3845, 3846, 3847, 65534, }; -String const Asm_amd64::register_names[Asm_amd64::REG_COUNT] { +String const Asm_amd64::register_strings[Asm_amd64::REG_COUNT] { str_lit(""), str_lit("rax"), str_lit("rcx"), str_lit("rdx"), str_lit("rbx"), str_lit("rsp"), str_lit("rbp"), str_lit("rsi"), str_lit("rdi"), str_lit("r8"), str_lit("r9"), str_lit("r10"), str_lit("r11"), str_lit("r12"), str_lit("r13"), str_lit("r14"), str_lit("r15"), str_lit("eax"), str_lit("ecx"), str_lit("edx"), str_lit("ebx"), str_lit("esp"), str_lit("ebp"), str_lit("esi"), str_lit("edi"), str_lit("r8d"), str_lit("r9d"), str_lit("r10d"), str_lit("r11d"), str_lit("r12d"), str_lit("r13d"), str_lit("r14d"), str_lit("r15d"), str_lit("ax"), str_lit("cx"), str_lit("dx"), str_lit("bx"), str_lit("sp"), str_lit("bp"), str_lit("si"), str_lit("di"), str_lit("r8w"), str_lit("r9w"), str_lit("r10w"), str_lit("r11w"), str_lit("r12w"), str_lit("r13w"), str_lit("r14w"), diff --git a/src/check_asm.cpp b/src/check_asm.cpp new file mode 100644 index 000000000..828d6af93 --- /dev/null +++ b/src/check_asm.cpp @@ -0,0 +1,643 @@ + +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 AsmRegClass check_asm_reg_class_from_type(Type *type) { + if (is_type_integer(type)) { + return AsmRegClass_Integer; + } + if (is_type_float(type)) { + return AsmRegClass_Float; + } + if (is_type_boolean(type)) { + return AsmRegClass_Integer; + } + if (is_type_pointer(type) || is_type_multi_pointer(type)) { + return AsmRegClass_Integer; + } + if (is_type_simd_vector(type)) { + return AsmRegClass_Vector; + } + return AsmRegClass_Unknown; +} + +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(); + + i32 param_index = 0; + 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 = asm_template_entity_decl_default(entity); + if (is_poly_name) { + ed.kind = AsmTemplateEntityDecl_Immediate; + } + if (input_parameters) { + ed.param_group = AsmTemplateEntityDeclParamGroup_Input; + ed.param_index = param_index++; + ed.result_index = -1; + } else { + ed.param_group = AsmTemplateEntityDeclParamGroup_Output; + ed.param_index = -1; + ed.result_index = param_index++; + } + + ed.total_index = cast(i32)asm_template_entity_decls->count; + 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, i32 *index_) { + for_array(i, asm_template_entity_decls) { + auto const &ed = asm_template_entity_decls[i]; + if (ed.entity == entity) { + if (index_) *index_ = cast(i32)i; + return ed.param_group; + } + } + if (index_) *index_ = -1; + 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) { + StringSet pin_set = {}; + string_set_init(&pin_set, specs.count); + defer (string_set_destroy(&pin_set)); + + 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; + + String pin = {}; + if (spec->value != nullptr) { + if (spec->value->kind != Ast_AsmRegister) { + gbString s = expr_to_string(spec->value); + error(spec->value, "Expected an asm register, got %s", s); + gb_string_free(s); + continue; + } + + ast_node(reg, AsmRegister, spec->value); + pin = reg->name.string; + if (pin == "any") { + pin = {}; + } + if (pin.len != 0) { + if (string_set_update(&pin_set, pin)) { + error(spec->value, "Pinned register %%%.*s has already be assigned", LIT(pin)); + } + } + } + + 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 = asm_template_entity_decl_default(entity); + ed.param_group = AsmTemplateEntityDeclParamGroup_Scratch; + ed.total_index = cast(i32)asm_template_entity_decls->count; + ed.pin = pin; + 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 { + i32 index = -1; + auto group = check_asm_find_group(input, *asm_template_entity_decls, &index); + gb_unused(group); + GB_ASSERT(index >= 0); + auto *i = &(*asm_template_entity_decls)[index]; + if (i->pin.len == 0) { + i->pin = pin; + } else { + error(spec_, "Asm register has already been pinned"); + } + } + + } 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; + } + + i32 input_index = -1; + i32 output_index = -1; + + auto input_group = check_asm_find_group(input, *asm_template_entity_decls, &input_index); + auto output_group = check_asm_find_group(output, *asm_template_entity_decls, &output_index); + 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; + } + + GB_ASSERT(input_index >= 0); + GB_ASSERT(output_index >= 0); + + auto *i = &(*asm_template_entity_decls)[input_index]; + auto *o = &(*asm_template_entity_decls)[output_index]; + + i->tie = output_index; + o->tie = input_index; + + i->pin = pin; + o->pin = pin; + + + must_check_value = true; + } + } +} + +gb_internal bool check_register(CheckerContext *ctx, AstAsmRegister *asm_reg) { + String name = asm_reg->name.string; + auto r = g_asm_amd64.register_lookup(name); + if (r) { + return true; + } + error(asm_reg->name, "Unknown register for this target platform: %%%.*s", LIT(name)); + return false; +} + +enum CheckMnemomicResult { + CheckMnemomic_Invalid, + CheckMnemomic_Mnemonic, + CheckMnemomic_Prefix, +}; + +gb_internal CheckMnemomicResult check_mnemonic(CheckerContext *ctx, AstAsmInstruction *instruction) { + String name = instruction->name->Ident.token.string; + auto m = g_asm_amd64.mnemonic_lookup(name); + if (m) { + return CheckMnemomic_Mnemonic; + } + auto p = g_asm_amd64.prefix_lookup(name); + if (p) { + return CheckMnemomic_Prefix; + } + + if (instruction->operands.count == 0) { + error(instruction->name, "Unknown mnemonic/prefix for this target platform: %%%.*s", LIT(name)); + } else { + error(instruction->name, "Unknown mnemonic for this target platform: %%%.*s", LIT(name)); + } + return CheckMnemomic_Invalid; +} + + +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(asm_reg, AsmRegister, expr); + check_register(ctx, asm_reg); + 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) { + check_register(ctx, &base.expr->AsmRegister); + } 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) { + check_register(ctx, &index.expr->AsmRegister); + } 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) { + check_register(ctx, &disp.expr->AsmRegister); + } else { + Entity *param_entity = entity_of_node(disp.expr); + if (disp.mode == Addressing_Constant) { + if (is_type_integer(disp.type)) { + break; + } + } + if (param_entity == nullptr) { + 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: + if (is_type_integer(disp.type)) { + break; + } + /*fallthrough*/ + default: + { + gbString s = expr_to_string(disp.expr); + gbString t = type_to_string(disp.type); + error(disp.expr, "An displacement must be an integer value, got %s of type %s", s, t); + gb_string_free(t); + 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; + + entity->type = type; + + check_asm_specs(ctx, ate->param_scope, at->specs, &ate->decls); + { // check clobbers + StringSet reg_set = {}; + string_set_init(®_set, 16); + defer (string_set_destroy(®_set)); + + bool clobber_cc = false; + bool clobber_memory = false; + + for (Ast *clobber_ : at->clobbers) { + ast_node(clobber, AsmClobber, clobber_); + switch (clobber->value->kind) { + case_ast_node(asm_reg, AsmRegister, clobber->value) + String reg = asm_reg->name.string; + if (check_register(ctx, asm_reg)) { + if (string_set_update(®_set, reg)) { + error(clobber->value, "#clobber %%%.*s has already been defined", LIT(reg)); + } + } + case_end; + case_ast_node(ident, Ident, clobber->value); + String str = ident->token.string; + if (str == "cc") { + if (clobber_cc) { + error(clobber->value, "#clobber cc has already been defined"); + } + clobber_cc = true; + } else if (str == "memory") { + if (clobber_memory) { + error(clobber->value, "#clobber memory has already been defined"); + } + clobber_memory = true; + } else { + error(clobber->value, "Expected either a register, 'cc', or 'memory' for a '#clobber' specification, got '%.*s'", LIT(str)); + } + case_end; + 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); + + CheckMnemomicResult res = check_mnemonic(ctx, instr); + + for (Ast *expr : instr->operands) { + Operand operand = {}; + check_asm_instruction_operand(ctx, entity, &operand, expr, /*allow_memory_operands*/true); + } + if (res == CheckMnemomic_Prefix) { + if (instr->operands.count != 0) { + error(instr->name, "A prefix must not have any operands, and be separate from the instruction it is prefixing"); + } + } + + case_end; + case_ast_node(label, AsmLabelDecl, instruction_); + // already done + case_end; + default: + error(instruction_, "Unexpected instruction in asm template"); + break; + } + } +} \ No newline at end of file diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 8f6412b31..c70672036 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1985,606 +1985,7 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D 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 AsmRegClass check_asm_reg_class_from_type(Type *type) { - if (is_type_integer(type)) { - return AsmRegClass_Integer; - } - if (is_type_float(type)) { - return AsmRegClass_Float; - } - if (is_type_boolean(type)) { - return AsmRegClass_Integer; - } - if (is_type_pointer(type) || is_type_multi_pointer(type)) { - return AsmRegClass_Integer; - } - if (is_type_simd_vector(type)) { - return AsmRegClass_Vector; - } - return AsmRegClass_Unknown; -} - -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(); - - i32 param_index = 0; - 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 = asm_template_entity_decl_default(entity); - if (is_poly_name) { - ed.kind = AsmTemplateEntityDecl_Immediate; - } - if (input_parameters) { - ed.param_group = AsmTemplateEntityDeclParamGroup_Input; - ed.param_index = param_index++; - ed.result_index = -1; - } else { - ed.param_group = AsmTemplateEntityDeclParamGroup_Output; - ed.param_index = -1; - ed.result_index = param_index++; - } - - ed.total_index = cast(i32)asm_template_entity_decls->count; - 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, i32 *index_) { - for_array(i, asm_template_entity_decls) { - auto const &ed = asm_template_entity_decls[i]; - if (ed.entity == entity) { - if (index_) *index_ = cast(i32)i; - return ed.param_group; - } - } - if (index_) *index_ = -1; - 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) { - StringSet pin_set = {}; - string_set_init(&pin_set, specs.count); - defer (string_set_destroy(&pin_set)); - - 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; - - String pin = {}; - if (spec->value != nullptr) { - if (spec->value->kind != Ast_AsmRegister) { - gbString s = expr_to_string(spec->value); - error(spec->value, "Expected an asm register, got %s", s); - gb_string_free(s); - continue; - } - - ast_node(reg, AsmRegister, spec->value); - pin = reg->name.string; - if (pin == "any") { - pin = {}; - } - if (pin.len != 0) { - if (string_set_update(&pin_set, pin)) { - error(spec->value, "Pinned register %%%.*s has already be assigned", LIT(pin)); - } - } - } - - 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 = asm_template_entity_decl_default(entity); - ed.param_group = AsmTemplateEntityDeclParamGroup_Scratch; - ed.total_index = cast(i32)asm_template_entity_decls->count; - ed.pin = pin; - 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 { - i32 index = -1; - auto group = check_asm_find_group(input, *asm_template_entity_decls, &index); - gb_unused(group); - GB_ASSERT(index >= 0); - auto *i = &(*asm_template_entity_decls)[index]; - if (i->pin.len == 0) { - i->pin = pin; - } else { - error(spec_, "Asm register has already been pinned"); - } - } - - } 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; - } - - i32 input_index = -1; - i32 output_index = -1; - - auto input_group = check_asm_find_group(input, *asm_template_entity_decls, &input_index); - auto output_group = check_asm_find_group(output, *asm_template_entity_decls, &output_index); - 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; - } - - GB_ASSERT(input_index >= 0); - GB_ASSERT(output_index >= 0); - - auto *i = &(*asm_template_entity_decls)[input_index]; - auto *o = &(*asm_template_entity_decls)[output_index]; - - i->tie = output_index; - o->tie = input_index; - - i->pin = pin; - o->pin = pin; - - - must_check_value = true; - } - } -} - -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 (disp.mode == Addressing_Constant) { - if (is_type_integer(disp.type)) { - break; - } - } - if (param_entity == nullptr) { - 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: - if (is_type_integer(disp.type)) { - break; - } - /*fallthrough*/ - default: - { - gbString s = expr_to_string(disp.expr); - gbString t = type_to_string(disp.type); - error(disp.expr, "An displacement must be an integer value, got %s of type %s", s, t); - gb_string_free(t); - 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; - - entity->type = type; - - check_asm_specs(ctx, ate->param_scope, at->specs, &ate->decls); - { // check clobbers - StringSet reg_set = {}; - string_set_init(®_set, 16); - defer (string_set_destroy(®_set)); - - bool clobber_cc = false; - bool clobber_memory = false; - - for (Ast *clobber_ : at->clobbers) { - ast_node(clobber, AsmClobber, clobber_); - switch (clobber->value->kind) { - case Ast_AsmRegister: - { - String reg = clobber->value->AsmRegister.name.string; - if (string_set_update(®_set, reg)) { - error(clobber->value, "#clobber %%%.*s has already been defined", LIT(reg)); - } - // TODO(bill): register check for validity - } - break; - case Ast_Ident: - { - String str = clobber->value->Ident.token.string; - if (str == "cc") { - if (clobber_cc) { - error(clobber->value, "#clobber cc has already been defined"); - } - clobber_cc = true; - } else if (str == "memory") { - if (clobber_memory) { - error(clobber->value, "#clobber memory has already been defined"); - } - clobber_memory = true; - } 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; - } - } -} +#include "check_asm.cpp" gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_type) { diff --git a/src/main.cpp b/src/main.cpp index d3e4c2b60..6afaf08df 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4313,6 +4313,11 @@ int main(int arg_count, char const **arg_ptr) { Checker *checker = permanent_alloc_item(); bool failed_to_cache_parsing = false; + TIME_SECTION("init asm tables"); + { + g_asm_amd64.init(); + } + MAIN_TIME_SECTION("parse files"); if (!init_parser(parser)) {