diff --git a/src/parser.cpp b/src/parser.cpp index d981491dd..0e2f77a93 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -520,8 +520,37 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) { n->MatrixType.column_count = clone_ast(n->MatrixType.column_count, f); n->MatrixType.elem = clone_ast(n->MatrixType.elem, f); break; - } + case Ast_AsmTemplate: + n->AsmTemplate.signature = clone_ast(n->AsmTemplate.signature, f); + n->AsmTemplate.specs = clone_ast_array(n->AsmTemplate.specs, f); + n->AsmTemplate.clobbers = clone_ast_array(n->AsmTemplate.clobbers, f); + n->AsmTemplate.instructions = clone_ast_array(n->AsmTemplate.instructions, f); + break; + case Ast_AsmRegister: + break; + case Ast_AsmSpec: + n->AsmSpec.name = clone_ast(n->AsmSpec.name, f); + n->AsmSpec.tied_name = clone_ast(n->AsmSpec.tied_name, f); + n->AsmSpec.type = clone_ast(n->AsmSpec.type, f); + n->AsmSpec.value = clone_ast(n->AsmSpec.value, f); + break; + case Ast_AsmClobber: + n->AsmClobber.value = clone_ast(n->AsmClobber.value, f); + break; + case Ast_AsmLabelDecl: + n->AsmLabelDecl.name = clone_ast(n->AsmLabelDecl.name, f); + break; + case Ast_AsmInstruction: + n->AsmInstruction.operands = clone_ast_array(n->AsmInstruction.operands, f); + break; + case Ast_AsmMemoryOperand: + n->AsmMemoryOperand.base = clone_ast(n->AsmMemoryOperand.base, f); + n->AsmMemoryOperand.index = clone_ast(n->AsmMemoryOperand.index, f); + n->AsmMemoryOperand.scale = clone_ast(n->AsmMemoryOperand.scale, f); + n->AsmMemoryOperand.disp = clone_ast(n->AsmMemoryOperand.disp, f); + break; + } return n; } @@ -1727,7 +1756,7 @@ gb_internal Token expect_operator(AstFile *f) { syntax_error(prev, "'..' for ranges are not allowed, did you mean '..<' or '..='?"); f->tokens[f->curr_token_index].flags |= TokenFlag_Replace; } - + advance_token(f); return prev; } @@ -2379,6 +2408,268 @@ gb_internal void parser_check_polymorphic_record_parameters(AstFile *f, Ast *pol } +gb_internal Ast *parse_asm_register(AstFile *f) { + Token token = expect_token(f, Token_Mod); + Token name = expect_token(f, Token_Ident); + Ast *reg = alloc_ast_node(f, Ast_AsmRegister); + reg->AsmRegister.token = token; + reg->AsmRegister.name = name; + return reg; +} +gb_internal Ast *parse_asm_operand(AstFile *f, bool allow_memory_operand) { + switch (f->curr_token.kind) { + case Token_Period: + { + Token token = expect_token(f, Token_Period); + Ast *name = parse_ident(f); + Ast *label_decl = alloc_ast_node(f, Ast_AsmLabelDecl); + label_decl->AsmLabelDecl.token = token; + label_decl->AsmLabelDecl.name = name; + return label_decl; + } + case Token_Ident: + return parse_ident(f); + case Token_Mod: + return parse_asm_register(f); + case Token_Integer: + case Token_Float: + case Token_Rune: + return ast_basic_lit(f, advance_token(f)); + case Token_OpenBracket: + if (allow_memory_operand) { + Token open = expect_token(f, Token_OpenBracket); + Ast *base = nullptr; + Ast *index = nullptr; + Ast *scale = nullptr; + Ast *disp = nullptr; + + base = parse_asm_operand(f, false); + + if (allow_token(f, Token_Add)) { + Ast *possible_index = parse_asm_operand(f, false); + if (allow_token(f, Token_Mul)) { + index = possible_index; + scale = parse_asm_operand(f, false); + if (allow_token(f, Token_Add)) { + disp = parse_asm_operand(f, false); + } + } else { + disp = possible_index; + } + } + + Token close = expect_token(f, Token_CloseBracket); + + Ast *mem = alloc_ast_node(f, Ast_AsmMemoryOperand); + mem->AsmMemoryOperand.open = open; + mem->AsmMemoryOperand.base = base; + mem->AsmMemoryOperand.index = index; + mem->AsmMemoryOperand.scale = scale; + mem->AsmMemoryOperand.disp = disp; + mem->AsmMemoryOperand.close = close; + + return mem; + } + break; + } + + syntax_error(f->curr_token, "Invalid asm operand, found '%.*s'", LIT(f->curr_token.string)); + advance_token(f); + return nullptr; +} + +gb_internal Slice parse_asm_operands(AstFile *f) { + Array operands = {}; + operands.allocator = heap_allocator(); + + while (f->curr_token.kind != Token_Semicolon && + f->curr_token.kind != Token_EOF) { + Ast *operand = parse_asm_operand(f, true); + if (operand != nullptr) { + array_add(&operands, operand); + } + if (!allow_token(f, Token_Comma)) { + break; + } + } + + if (allow_token(f, Token_Semicolon)) { + // okay + } + + return slice_from_array(operands); +} + +gb_internal Ast *parse_asm_instruction(AstFile *f) { + if (allow_token(f, Token_Semicolon)) { + return nullptr; + } + switch (f->curr_token.kind) { + default: + if (!token_is_keyword(f->curr_token.kind)) { + break; + } + /*fallthrough*/ + case Token_Ident: + { + Token name = advance_token(f); + auto operands = parse_asm_operands(f); + Ast *instruction = alloc_ast_node(f, Ast_AsmInstruction); + instruction->AsmInstruction.name = name; + instruction->AsmInstruction.operands = operands; + return instruction; + } + case Token_Period: + { + Token token = expect_token(f, Token_Period); + Ast *name = parse_ident(f); + expect_token(f, Token_Colon); + Ast *label_decl = alloc_ast_node(f, Ast_AsmLabelDecl); + label_decl->AsmLabelDecl.token = token; + label_decl->AsmLabelDecl.name = name; + return label_decl; + } + } + syntax_error(f->curr_token, "Expected an asm instruction, got '%.*s'", LIT(f->curr_token.string)); + advance_token(f); + return nullptr; +} + + +gb_internal Ast *parse_asm_template(AstFile *f) { + Token token = expect_token(f, Token_asm); + + bool has_side_effects = false; + bool is_align_stack = false; + + while (f->curr_token.kind == Token_Hash) { + advance_token(f); + if (f->curr_token.kind == Token_Ident) { + Token token = advance_token(f); + String name = token.string; + if (name == "side_effects") { + if (has_side_effects) { + syntax_error(token, "Duplicate directive on inline asm expression: '#side_effects'"); + } + has_side_effects = true; + } else if (name == "align_stack") { + if (is_align_stack) { + syntax_error(token, "Duplicate directive on inline asm expression: '#align_stack'"); + } + is_align_stack = true; + } else { + syntax_error(token, "Invalid directive on inline asm expression: '#%.*s'", LIT(token.string)); + } + } else { + syntax_error(f->curr_token, "Expected an identifier after hash"); + } + } + + + Ast *signature = parse_proc_type(f, token); + + Slice asm_specs = {}; + Slice asm_clobbers = {}; + + if (f->curr_token.kind == Token_OpenBracket) { + Array specs = {}; + specs.allocator = heap_allocator(); + + Array clobbers = {}; + clobbers.allocator = heap_allocator(); + + Token open = expect_token(f, Token_OpenBracket); + while (f->curr_token.kind != Token_CloseBracket && + f->curr_token.kind != Token_EOF) { + Ast *spec = nullptr; + if (f->curr_token.kind == Token_Ident) { + Ast *name = parse_ident(f); + Ast *tied_name = nullptr; + Ast *type = nullptr; + Ast *value = nullptr; + if (allow_token(f, Token_ArrowRight)) { + tied_name = parse_ident(f); + } + if (allow_token(f, Token_Colon)) { + type = parse_type(f); + } + if (allow_token(f, Token_Eq)) { + value = parse_asm_register(f); + } + + if (type == nullptr && value == nullptr) { + syntax_error(f->curr_token, "An asm specification must specify at least either a type or a value"); + } + + spec = alloc_ast_node(f, Ast_AsmSpec); + spec->AsmSpec.name = name; + spec->AsmSpec.tied_name = tied_name; + spec->AsmSpec.type = type; + spec->AsmSpec.value = value; + } else if (f->curr_token.kind == Token_Hash) { + Token hash = expect_token(f, Token_Hash); + Token clobber_token = expect_token(f, Token_Ident); + if (clobber_token.string != "clobber") { + syntax_error(clobber_token, "Expected #clobber, got '%.*s'", LIT(clobber_token.string)); + } else { + Ast *value = parse_asm_operand(f, false); + Ast *clobber = alloc_ast_node(f, Ast_AsmClobber); + clobber->AsmClobber.token = hash; + clobber->AsmClobber.value = value; + array_add(&clobbers, clobber); + } + } else { + syntax_error(f->curr_token, "Expected am asm specification which begins with a identifier, got '%.*s'", LIT(f->curr_token.string)); + advance_token(f); + } + if (spec != nullptr) { + array_add(&specs, spec); + } + if (!allow_token(f, Token_Comma)) { + break; + } + } + Token close = expect_token(f, Token_CloseBracket); + + asm_specs = slice_from_array(specs); + asm_clobbers = slice_from_array(clobbers); + } + + Slice asm_instructions = {}; + + if (!allow_token(f, Token_OpenBrace)) { + syntax_error(f->curr_token, "Expected a body for an asm template"); + advance_token(f); + } else { + Array instructions = {}; + instructions.allocator = heap_allocator(); + + while (f->curr_token.kind != Token_CloseBrace && + f->curr_token.kind != Token_EOF) { + Ast *instruction = parse_asm_instruction(f); + if (instruction != nullptr) { + array_add(&instructions, instruction); + } + } + + Token close = expect_token(f, Token_CloseBrace); + + asm_instructions = slice_from_array(instructions); + } + + Ast *asm_template = alloc_ast_node(f, Ast_AsmTemplate); + asm_template->AsmTemplate.token = token; + asm_template->AsmTemplate.has_side_effects = has_side_effects; + asm_template->AsmTemplate.is_align_stack = is_align_stack; + asm_template->AsmTemplate.signature = signature; + asm_template->AsmTemplate.specs = asm_specs; + asm_template->AsmTemplate.clobbers = asm_clobbers; + asm_template->AsmTemplate.instructions = asm_instructions; + asm_template->AsmTemplate.end = f->prev_token; + return asm_template; +} + + gb_internal Ast *parse_operand(AstFile *f, bool lhs) { Ast *operand = nullptr; // Operand switch (f->curr_token.kind) { @@ -3105,83 +3396,8 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { return ast_bit_set_type(f, token, elem, underlying); } - case Token_asm: { - Token token = expect_token(f, Token_asm); - - Array param_types = {}; - Ast *return_type = nullptr; - if (allow_token(f, Token_OpenParen)) { - param_types = array_make(ast_allocator(f)); - while (f->curr_token.kind != Token_CloseParen && f->curr_token.kind != Token_EOF) { - Ast *t = parse_type(f); - array_add(¶m_types, t); - if (f->curr_token.kind != Token_Comma || - f->curr_token.kind == Token_EOF) { - break; - } - advance_token(f); - } - expect_token(f, Token_CloseParen); - - if (allow_token(f, Token_ArrowRight)) { - return_type = parse_type(f); - } - } - - bool has_side_effects = false; - bool is_align_stack = false; - InlineAsmDialectKind dialect = InlineAsmDialect_Default; - - while (f->curr_token.kind == Token_Hash) { - advance_token(f); - if (f->curr_token.kind == Token_Ident) { - Token token = advance_token(f); - String name = token.string; - if (name == "side_effects") { - if (has_side_effects) { - syntax_error(token, "Duplicate directive on inline asm expression: '#side_effects'"); - } - has_side_effects = true; - } else if (name == "align_stack") { - if (is_align_stack) { - syntax_error(token, "Duplicate directive on inline asm expression: '#align_stack'"); - } - is_align_stack = true; - } else if (name == "att") { - if (dialect == InlineAsmDialect_ATT) { - syntax_error(token, "Duplicate directive on inline asm expression: '#att'"); - } else if (dialect != InlineAsmDialect_Default) { - syntax_error(token, "Conflicting asm dialects"); - } else { - dialect = InlineAsmDialect_ATT; - } - } else if (name == "intel") { - if (dialect == InlineAsmDialect_Intel) { - syntax_error(token, "Duplicate directive on inline asm expression: '#intel'"); - } else if (dialect != InlineAsmDialect_Default) { - syntax_error(token, "Conflicting asm dialects"); - } else { - dialect = InlineAsmDialect_Intel; - } - } else { - syntax_error(token, "Invalid directive on inline asm expression: '#%.*s'", LIT(token.string)); - } - } else { - syntax_error(f->curr_token, "Expected an identifier after hash"); - } - } - - skip_possible_newline_for_literal(f); - Token open = expect_token(f, Token_OpenBrace); - Ast *asm_string = parse_expr(f, false); - expect_token(f, Token_Comma); - Ast *constraints_string = parse_expr(f, false); - allow_token(f, Token_Comma); - Token close = expect_closing_brace_of_field_list(f); - - return ast_inline_asm_expr(f, token, open, close, param_types, return_type, asm_string, constraints_string, has_side_effects, is_align_stack, dialect); - } - + case Token_asm: + return parse_asm_template(f); } return nullptr; diff --git a/src/parser.hpp b/src/parser.hpp index e423cb9b2..af8c3af09 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -468,6 +468,47 @@ struct AstSplitArgs { i64 max_count; \ Ast *tag; \ }) \ + AST_KIND(AsmTemplate, "asm template", struct { \ + Token token; \ + bool has_side_effects; \ + bool is_align_stack; \ + Ast * signature; \ + Slice specs; \ + Slice clobbers; \ + Slice instructions; \ + Token end; \ + }) \ + AST_KIND(AsmRegister, "asm register", struct { \ + Token token; \ + Token name; \ + }) \ + AST_KIND(AsmSpec, "asm specification", struct { \ + Ast *name; \ + Ast *tied_name; \ + Ast *type; \ + Ast *value; \ + bool is_temporary_decl; \ + }) \ + AST_KIND(AsmClobber, "asm clobber", struct { \ + Token token; \ + Ast * value; \ + }) \ + AST_KIND(AsmLabelDecl, "asm label declaration", struct { \ + Token token; \ + Ast * name; \ + }) \ + AST_KIND(AsmInstruction, "asm instruction", struct { \ + Token name; \ + Slice operands; \ + }) \ + AST_KIND(AsmMemoryOperand, "asm memory operand", struct { \ + Token open; \ + Ast * base; \ + Ast * index; \ + Ast * scale; \ + Ast * disp; \ + Token close; \ + }) \ AST_KIND(_ExprBegin, "", bool) \ AST_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \ AST_KIND(TagExpr, "tag expression", struct { Token token, name; Ast *expr; }) \ diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp index 1d2b5090a..0fe27d2cc 100644 --- a/src/parser_pos.cpp +++ b/src/parser_pos.cpp @@ -13,6 +13,21 @@ gb_internal Token ast_token(Ast *node) { } return node->CompoundLit.open; + case Ast_AsmTemplate: + return node->AsmTemplate.token; + case Ast_AsmRegister: + return node->AsmRegister.token; + case Ast_AsmSpec: + return ast_token(node->AsmSpec.name); + case Ast_AsmClobber: + return node->AsmClobber.token; + case Ast_AsmLabelDecl: + return node->AsmLabelDecl.token; + case Ast_AsmInstruction: + return node->AsmInstruction.name; + case Ast_AsmMemoryOperand: + return node->AsmMemoryOperand.open; + case Ast_TagExpr: return node->TagExpr.token; case Ast_BadExpr: return node->BadExpr.begin; case Ast_UnaryExpr: return node->UnaryExpr.op; @@ -156,6 +171,33 @@ Token ast_end_token(Ast *node) { case Ast_CompoundLit: return node->CompoundLit.close; + case Ast_AsmTemplate: + return node->AsmTemplate.end; + case Ast_AsmRegister: + return node->AsmRegister.name; + case Ast_AsmSpec: + if (node->AsmSpec.value) { + return ast_end_token(node->AsmSpec.value); + } + if (node->AsmSpec.type) { + return ast_end_token(node->AsmSpec.type); + } + if (node->AsmSpec.tied_name) { + return ast_end_token(node->AsmSpec.tied_name); + } + return ast_end_token(node->AsmSpec.name); + case Ast_AsmClobber: + return ast_end_token(node->AsmClobber.value); + case Ast_AsmLabelDecl: + return ast_end_token(node->AsmLabelDecl.name); + case Ast_AsmInstruction: + if (node->AsmInstruction.operands.count > 0) { + return ast_end_token(node->AsmInstruction.operands[node->AsmInstruction.operands.count-1]); + } + return node->AsmInstruction.name; + case Ast_AsmMemoryOperand: + return node->AsmMemoryOperand.close; + case Ast_BadExpr: return node->BadExpr.end; case Ast_TagExpr: if (node->TagExpr.expr) {