Validate the names of mnemonics, prefixes, and registers for amd64

This commit is contained in:
gingerBill
2026-08-11 10:44:34 +01:00
parent d827be77cd
commit 70ca5c0936
5 changed files with 776 additions and 616 deletions

View File

@@ -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> mnemonic_map;\n")
strings.write_string(&sb, "\tStringMap<Prefix> prefix_map;\n")
strings.write_string(&sb, "\tStringMap<Register> register_map;\n")
strings.write_string(&sb, "\tSlice<EncodeRun> ENCODE_RUNS;\n")
strings.write_string(&sb, "\tSlice<Encoding> 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(&register_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(&register_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(&register_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(&register_map, name);\n")
strings.write_string(&sb, "\t\treturn found ? *found : REG_INVALID;\n")
}
{
strings.write_string(&sb, "\tSlice<Encoding> 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,
}

View File

@@ -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> mnemonic_map;
StringMap<Prefix> prefix_map;
StringMap<Register> register_map;
Slice<EncodeRun> ENCODE_RUNS;
Slice<Encoding> 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(&register_map, REG_COUNT*2);
for (u16 r = REG_INVALID; r < REG_COUNT; r++) {
string_map_set(&register_map, register_names[r], cast(Register)r);
for (u16 r = REG_INVALID+1; r < REG_COUNT; r++) {
string_map_set(&register_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(&register_map, name);
return found ? *found : REG_INVALID;
}
Slice<Encoding> 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"),

643
src/check_asm.cpp Normal file
View File

@@ -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<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();
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<AsmTemplateEntityDecl> 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<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) {
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(&reg_set, 16);
defer (string_set_destroy(&reg_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(&reg_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;
}
}
}

View File

@@ -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<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();
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<AsmTemplateEntityDecl> 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<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) {
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(&reg_set, 16);
defer (string_set_destroy(&reg_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(&reg_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) {

View File

@@ -4313,6 +4313,11 @@ int main(int arg_count, char const **arg_ptr) {
Checker *checker = permanent_alloc_item<Checker>();
bool failed_to_cache_parsing = false;
TIME_SECTION("init asm tables");
{
g_asm_amd64.init();
}
MAIN_TIME_SECTION("parse files");
if (!init_parser(parser)) {