authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-03-20 19:00:23-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-03-20 19:00:23-04:00
log15c316b0d8152c0a1ad5b4b26efdf3fdc8cfb4c0
tree0bd91c0ba2a63ed3495e94385fbedaa1e3a6fde0
parent3c7555cb679492f3f1c0ce320cbdf4a3769e56db
signaturelock-open Commit is signed but in an unrecognized format.

add docs for assembly and fix global assembly parsing

Previously, global assembly was parsed expecting it to have the template syntax. However global assembly has no inputs, outputs, or clobbers, and thus does not have template syntax. This is now fixed. This commit also adds a compile error for using volatile on global assembly, since it is meaningless. closes #1515

10 files changed, 415 insertions(+), 165 deletions(-)

doc/docgen.zig+21-7
...@@ -274,7 +274,7 @@ const Code = struct {...@@ -274,7 +274,7 @@ const Code = struct {
274 is_inline: bool,274 is_inline: bool,
275 mode: builtin.Mode,275 mode: builtin.Mode,
276 link_objects: []const []const u8,276 link_objects: []const []const u8,
277 target_windows: bool,277 target_str: ?[]const u8,
278 link_libc: bool,278 link_libc: bool,
279279
280 const Id = union(enum) {280 const Id = union(enum) {
...@@ -491,7 +491,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {...@@ -491,7 +491,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
491 var mode = builtin.Mode.Debug;491 var mode = builtin.Mode.Debug;
492 var link_objects = std.ArrayList([]const u8).init(allocator);492 var link_objects = std.ArrayList([]const u8).init(allocator);
493 defer link_objects.deinit();493 defer link_objects.deinit();
494 var target_windows = false;494 var target_str: ?[]const u8 = null;
495 var link_libc = false;495 var link_libc = false;
496496
497 const source_token = while (true) {497 const source_token = while (true) {
...@@ -506,7 +506,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {...@@ -506,7 +506,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
506 const obj_tok = try eatToken(tokenizer, Token.Id.TagContent);506 const obj_tok = try eatToken(tokenizer, Token.Id.TagContent);
507 try link_objects.append(tokenizer.buffer[obj_tok.start..obj_tok.end]);507 try link_objects.append(tokenizer.buffer[obj_tok.start..obj_tok.end]);
508 } else if (mem.eql(u8, end_tag_name, "target_windows")) {508 } else if (mem.eql(u8, end_tag_name, "target_windows")) {
509 target_windows = true;509 target_str = "x86_64-windows";
510 } else if (mem.eql(u8, end_tag_name, "target_linux_x86_64")) {
511 target_str = "x86_64-linux";
510 } else if (mem.eql(u8, end_tag_name, "link_libc")) {512 } else if (mem.eql(u8, end_tag_name, "link_libc")) {
511 link_libc = true;513 link_libc = true;
512 } else if (mem.eql(u8, end_tag_name, "code_end")) {514 } else if (mem.eql(u8, end_tag_name, "code_end")) {
...@@ -526,7 +528,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {...@@ -526,7 +528,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
526 .is_inline = is_inline,528 .is_inline = is_inline,
527 .mode = mode,529 .mode = mode,
528 .link_objects = link_objects.toOwnedSlice(),530 .link_objects = link_objects.toOwnedSlice(),
529 .target_windows = target_windows,531 .target_str = target_str,
530 .link_libc = link_libc,532 .link_libc = link_libc,
531 },533 },
532 });534 });
...@@ -998,7 +1000,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -998,7 +1000,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
998 try io.writeFile(tmp_source_file_name, trimmed_raw_source);1000 try io.writeFile(tmp_source_file_name, trimmed_raw_source);
9991001
1000 switch (code.id) {1002 switch (code.id) {
1001 Code.Id.Exe => |expected_outcome| {1003 Code.Id.Exe => |expected_outcome| code_block: {
1002 const name_plus_bin_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, exe_ext);1004 const name_plus_bin_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, exe_ext);
1003 const tmp_bin_file_name = try os.path.join(1005 const tmp_bin_file_name = try os.path.join(
1004 allocator,1006 allocator,
...@@ -1046,8 +1048,20 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -1046,8 +1048,20 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
1046 try build_args.append("c");1048 try build_args.append("c");
1047 try out.print(" --library c");1049 try out.print(" --library c");
1048 }1050 }
1051 if (code.target_str) |triple| {
1052 try build_args.appendSlice([][]const u8{ "-target", triple });
1053 }
1049 _ = exec(allocator, &env_map, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile");1054 _ = exec(allocator, &env_map, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile");
10501055
1056 if (code.target_str) |triple| {
1057 if (mem.startsWith(u8, triple, "x86_64-linux") and
1058 (builtin.os != builtin.Os.linux or builtin.arch != builtin.Arch.x86_64))
1059 {
1060 // skip execution
1061 break :code_block;
1062 }
1063 }
1064
1051 const run_args = [][]const u8{tmp_bin_file_name};1065 const run_args = [][]const u8{tmp_bin_file_name};
10521066
1053 const result = if (expected_outcome == ExpectedOutcome.Fail) blk: {1067 const result = if (expected_outcome == ExpectedOutcome.Fail) blk: {
...@@ -1105,8 +1119,8 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -1105,8 +1119,8 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
1105 try out.print(" --release-small");1119 try out.print(" --release-small");
1106 },1120 },
1107 }1121 }
1108 if (code.target_windows) {1122 if (code.target_str) |triple| {
1109 try test_args.appendSlice([][]const u8{ "-target", "x86_64-windows" });1123 try test_args.appendSlice([][]const u8{ "-target", triple });
1110 }1124 }
1111 const result = exec(allocator, &env_map, test_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "test failed");1125 const result = exec(allocator, &env_map, test_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "test failed");
1112 const escaped_stderr = try escapeHtml(allocator, result.stderr);1126 const escaped_stderr = try escapeHtml(allocator, result.stderr);
doc/langref.html.in+189-4
...@@ -5396,11 +5396,196 @@ pub fn main() void {...@@ -5396,11 +5396,196 @@ pub fn main() void {
5396 {#see_also|inline while|inline for#}5396 {#see_also|inline while|inline for#}
5397 {#header_close#}5397 {#header_close#}
5398 {#header_open|Assembly#}5398 {#header_open|Assembly#}
5399 <p>TODO: example of inline assembly</p>5399 <p>
5400 <p>TODO: example of module level assembly</p>5400 For some use cases, it may be necessary to directly control the machine code generated
5401 <p>TODO: example of using inline assembly return value</p>5401 by Zig programs, rather than relying on Zig's code generation. For these cases, one
5402 <p>TODO: example of using inline assembly assigning values to variables</p>5402 can use inline assembly. Here is an example of implementing Hello, World on x86_64 Linux
5403 using inline assembly:
5404 </p>
5405 {#code_begin|exe#}
5406 {#target_linux_x86_64#}
5407pub fn main() noreturn {
5408 const msg = "hello world\n";
5409 _ = syscall3(SYS_write, STDOUT_FILENO, @ptrToInt(&msg), msg.len);
5410 _ = syscall1(SYS_exit, 0);
5411 unreachable;
5412}
5413
5414pub const SYS_write = 1;
5415pub const SYS_exit = 60;
5416
5417pub const STDOUT_FILENO = 1;
5418
5419pub fn syscall1(number: usize, arg1: usize) usize {
5420 return asm volatile ("syscall"
5421 : [ret] "={rax}" (-> usize)
5422 : [number] "{rax}" (number),
5423 [arg1] "{rdi}" (arg1)
5424 : "rcx", "r11"
5425 );
5426}
5427
5428pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize {
5429 return asm volatile ("syscall"
5430 : [ret] "={rax}" (-> usize)
5431 : [number] "{rax}" (number),
5432 [arg1] "{rdi}" (arg1),
5433 [arg2] "{rsi}" (arg2),
5434 [arg3] "{rdx}" (arg3)
5435 : "rcx", "r11"
5436 );
5437}
5438 {#code_end#}
5439 <p>
5440 Dissecting the syntax:
5441 </p>
5442 <pre>{#syntax#}// Inline assembly is an expression which returns a value.
5443// the `asm` keyword begins the expression.
5444_ = asm
5445// `volatile` is an optional modifier that tells Zig this
5446// inline assembly expression has side-effects. Without
5447// `volatile`, Zig is allowed to delete the inline assembly
5448// code if the result is unused.
5449volatile (
5450// Next is a comptime string which is the assembly code.
5451// Inside this string one may use `%[ret]`, `%[number]`,
5452// or `%[arg1]` where a register is expected, to specify
5453// the register that Zig uses for the argument or return value,
5454// if the register constraint strings are used. However in
5455// the below code, this is not used. A literal `%` can be
5456// obtained by escaping it with a double percent: `%%`.
5457// Often multiline string syntax comes in handy here.
5458 \\syscall
5459// Next is the output. It is possible in the future Zig will
5460// support multiple outputs, depending on how
5461// https://github.com/ziglang/zig/issues/215 is resolved.
5462// It is allowed for there to be no outputs, in which case
5463// this colon would be directly followed by the colon for the inputs.
5464 :
5465// This specifies the name to be used in `%[ret]` syntax in
5466// the above assembly string. This example does not use it,
5467// but the syntax is mandatory.
5468 [ret]
5469// Next is the output constraint string. This feature is still
5470// considered unstable in Zig, and so LLVM/GCC documentation
5471// must be used to understand the semantics.
5472// http://releases.llvm.org/8.0.0/docs/LangRef.html#inline-asm-constraint-string
5473// https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
5474// In this example, the constraint string means "the result value of
5475// this inline assembly instruction is whatever is in $rax".
5476 "={rax}"
5477// Next is either a value binding, or `->` and then a type. The
5478// type is the result type of the inline assembly expression.
5479// If it is a value binding, then `%[ret]` syntax would be used
5480// to refer to the register bound to the value.
5481 (-> usize)
5482// Next is the list of inputs.
5483// The constraint for these inputs means, "when the assembly code is
5484// executed, $rax shall have the value of `number` and $rdi shall have
5485// the value of `arg1`". Any number of input parameters is allowed,
5486// including none.
5487 : [number] "{rax}" (number),
5488 [arg1] "{rdi}" (arg1)
5489// Next is the list of clobbers. These declare a set of registers whose
5490// values will not be preserved by the execution of this assembly code.
5491// These do not include output or input registers. The special clobber
5492// value of "memory" means that the assembly writes to arbitrary undeclared
5493// memory locations - not only the memory pointed to by a declared indirect
5494// output. In this example we list $rcx and $r11 because it is known the
5495// kernel syscall does not preserve these registers.
5496 : "rcx", "r11"
5497);{#endsyntax#}</pre>
5498 <p>
5499 For i386 and x86_64 targets, the syntax is AT&amp;T syntax, rather than the more
5500 popular Intel syntax. This is due to technical constraints; assembly parsing is
5501 provided by LLVM and its support for Intel syntax is buggy and not well tested.
5502 </p>
5503 <p>
5504 Some day Zig may have its own assembler. This would allow it to integrate more seamlessly
5505 into the language, as well as be compatible with the popular NASM syntax. This documentation
5506 section will be updated before 1.0.0 is released, with a conclusive statement about the status
5507 of AT&amp;T vs Intel/NASM syntax.
5508 </p>
5509 {#header_open|Output Constraints#}
5510 <p>
5511 Output constraints are still considered to be unstable in Zig, and
5512 so
5513 <a href="http://releases.llvm.org/8.0.0/docs/LangRef.html#inline-asm-constraint-string">LLVM documentation</a>
5514 and
5515 <a href="https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html">GCC documentation</a>
5516 must be used to understand the semantics.
5517 </p>
5518 <p>
5519 Note that some breaking changes to output constraints are planned with
5520 <a href="https://github.com/ziglang/zig/issues/215">issue #215</a>.
5521 </p>
5522 {#header_close#}
5523
5524 {#header_open|Input Constraints#}
5525 <p>
5526 Input constraints are still considered to be unstable in Zig, and
5527 so
5528 <a href="http://releases.llvm.org/8.0.0/docs/LangRef.html#inline-asm-constraint-string">LLVM documentation</a>
5529 and
5530 <a href="https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html">GCC documentation</a>
5531 must be used to understand the semantics.
5532 </p>
5533 <p>
5534 Note that some breaking changes to input constraints are planned with
5535 <a href="https://github.com/ziglang/zig/issues/215">issue #215</a>.
5536 </p>
5537 {#header_close#}
5538
5539 {#header_open|Clobbers#}
5540 <p>
5541 Clobbers are the set of registers whose values will not be preserved by the execution of
5542 the assembly code. These do not include output or input registers. The special clobber
5543 value of {#syntax#}"memory"{#endsyntax#} means that the assembly causes writes to
5544 arbitrary undeclared memory locations - not only the memory pointed to by a declared
5545 indirect output.
5546 </p>
5547 <p>
5548 Failure to declare the full set of clobbers for a given inline assembly
5549 expression is unchecked {#link|Undefined Behavior#}.
5550 </p>
5551 {#header_close#}
5552
5553 {#header_open|Global Assembly#}
5554 <p>
5555 When an assembly expression occurs in a top level {#link|comptime#} block, this is
5556 <strong>global assembly</strong>.
5557 </p>
5558 <p>
5559 This kind of assembly has different rules than inline assembly. First, {#syntax#}volatile{#endsyntax#}
5560 is not valid because all global assembly is unconditionally included.
5561 Second, there are no inputs, outputs, or clobbers. All global assembly is concatenated
5562 verbatim into one long string and assembled together. There are no template substitution rules regarding
5563 <code>%</code> as there are in inline assembly expressions.
5564 </p>
5565 {#code_begin|test|global-asm#}
5566 {#target_linux_x86_64#}
5567const std = @import("std");
5568const assert = std.debug.assert;
5569
5570comptime {
5571 asm (
5572 \\.global my_func;
5573 \\.type my_func, @function;
5574 \\my_func:
5575 \\ lea (%rdi,%rsi,1),%eax
5576 \\ retq
5577 );
5578}
5579
5580extern fn my_func(a: i32, b: i32) i32;
5581
5582test "global assembly" {
5583 assert(my_func(12, 34) == 46);
5584}
5585 {#code_end#}
5586 {#header_close#}
5403 {#header_close#}5587 {#header_close#}
5588
5404 {#header_open|Atomics#}5589 {#header_open|Atomics#}
5405 <p>TODO: @fence()</p>5590 <p>TODO: @fence()</p>
5406 <p>TODO: @atomic rmw</p>5591 <p>TODO: @atomic rmw</p>
src/all_types.hpp+12-4
...@@ -800,9 +800,8 @@ struct AsmToken {...@@ -800,9 +800,8 @@ struct AsmToken {
800};800};
801801
802struct AstNodeAsmExpr {802struct AstNodeAsmExpr {
803 bool is_volatile;803 Token *volatile_token;
804 Buf *asm_template;804 Token *asm_template;
805 ZigList<AsmToken> token_list;
806 ZigList<AsmOutput*> output_list;805 ZigList<AsmOutput*> output_list;
807 ZigList<AsmInput*> input_list;806 ZigList<AsmInput*> input_list;
808 ZigList<Buf*> clobber_list;807 ZigList<Buf*> clobber_list;
...@@ -2169,6 +2168,7 @@ enum IrInstructionId {...@@ -2169,6 +2168,7 @@ enum IrInstructionId {
2169 IrInstructionIdArrayType,2168 IrInstructionIdArrayType,
2170 IrInstructionIdPromiseType,2169 IrInstructionIdPromiseType,
2171 IrInstructionIdSliceType,2170 IrInstructionIdSliceType,
2171 IrInstructionIdGlobalAsm,
2172 IrInstructionIdAsm,2172 IrInstructionIdAsm,
2173 IrInstructionIdSizeOf,2173 IrInstructionIdSizeOf,
2174 IrInstructionIdTestNonNull,2174 IrInstructionIdTestNonNull,
...@@ -2677,10 +2677,18 @@ struct IrInstructionSliceType {...@@ -2677,10 +2677,18 @@ struct IrInstructionSliceType {
2677 bool allow_zero;2677 bool allow_zero;
2678};2678};
26792679
2680struct IrInstructionGlobalAsm {
2681 IrInstruction base;
2682
2683 Buf *asm_code;
2684};
2685
2680struct IrInstructionAsm {2686struct IrInstructionAsm {
2681 IrInstruction base;2687 IrInstruction base;
26822688
2683 // Most information on inline assembly comes from the source node.2689 Buf *asm_template;
2690 AsmToken *token_list;
2691 size_t token_list_len;
2684 IrInstruction **input_list;2692 IrInstruction **input_list;
2685 IrInstruction **output_types;2693 IrInstruction **output_types;
2686 ZigVar **output_vars;2694 ZigVar **output_vars;
src/ast_render.cpp+2-2
...@@ -862,8 +862,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -862,8 +862,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
862 case NodeTypeAsmExpr:862 case NodeTypeAsmExpr:
863 {863 {
864 AstNodeAsmExpr *asm_expr = &node->data.asm_expr;864 AstNodeAsmExpr *asm_expr = &node->data.asm_expr;
865 const char *volatile_str = asm_expr->is_volatile ? " volatile" : "";865 const char *volatile_str = (asm_expr->volatile_token != nullptr) ? " volatile" : "";
866 fprintf(ar->f, "asm%s (\"%s\"\n", volatile_str, buf_ptr(asm_expr->asm_template));866 fprintf(ar->f, "asm%s (\"%s\"\n", volatile_str, buf_ptr(&asm_expr->asm_template->data.str_lit.str));
867 print_indent(ar);867 print_indent(ar);
868 fprintf(ar->f, ": ");868 fprintf(ar->f, ": ");
869 for (size_t i = 0; i < asm_expr->output_list.length; i += 1) {869 for (size_t i = 0; i < asm_expr->output_list.length; i += 1) {
src/codegen.cpp+8-7
...@@ -3793,8 +3793,8 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab...@@ -3793,8 +3793,8 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab
3793 return bitcasted_union_field_ptr;3793 return bitcasted_union_field_ptr;
3794}3794}
37953795
3796static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok) {3796static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok, Buf *src_template) {
3797 const char *ptr = buf_ptr(node->data.asm_expr.asm_template) + tok->start + 2;3797 const char *ptr = buf_ptr(src_template) + tok->start + 2;
3798 size_t len = tok->end - tok->start - 2;3798 size_t len = tok->end - tok->start - 2;
3799 size_t result = 0;3799 size_t result = 0;
3800 for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1, result += 1) {3800 for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1, result += 1) {
...@@ -3817,13 +3817,13 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru...@@ -3817,13 +3817,13 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru
3817 assert(asm_node->type == NodeTypeAsmExpr);3817 assert(asm_node->type == NodeTypeAsmExpr);
3818 AstNodeAsmExpr *asm_expr = &asm_node->data.asm_expr;3818 AstNodeAsmExpr *asm_expr = &asm_node->data.asm_expr;
38193819
3820 Buf *src_template = asm_expr->asm_template;3820 Buf *src_template = instruction->asm_template;
38213821
3822 Buf llvm_template = BUF_INIT;3822 Buf llvm_template = BUF_INIT;
3823 buf_resize(&llvm_template, 0);3823 buf_resize(&llvm_template, 0);
38243824
3825 for (size_t token_i = 0; token_i < asm_expr->token_list.length; token_i += 1) {3825 for (size_t token_i = 0; token_i < instruction->token_list_len; token_i += 1) {
3826 AsmToken *asm_token = &asm_expr->token_list.at(token_i);3826 AsmToken *asm_token = &instruction->token_list[token_i];
3827 switch (asm_token->id) {3827 switch (asm_token->id) {
3828 case AsmTokenIdTemplate:3828 case AsmTokenIdTemplate:
3829 for (size_t offset = asm_token->start; offset < asm_token->end; offset += 1) {3829 for (size_t offset = asm_token->start; offset < asm_token->end; offset += 1) {
...@@ -3840,7 +3840,7 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru...@@ -3840,7 +3840,7 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru
3840 break;3840 break;
3841 case AsmTokenIdVar:3841 case AsmTokenIdVar:
3842 {3842 {
3843 size_t index = find_asm_index(g, asm_node, asm_token);3843 size_t index = find_asm_index(g, asm_node, asm_token, src_template);
3844 assert(index < SIZE_MAX);3844 assert(index < SIZE_MAX);
3845 buf_appendf(&llvm_template, "$%" ZIG_PRI_usize "", index);3845 buf_appendf(&llvm_template, "$%" ZIG_PRI_usize "", index);
3846 break;3846 break;
...@@ -3937,7 +3937,7 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru...@@ -3937,7 +3937,7 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru
3937 }3937 }
3938 LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, (unsigned)input_and_output_count, false);3938 LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, (unsigned)input_and_output_count, false);
39393939
3940 bool is_volatile = asm_expr->is_volatile || (asm_expr->output_list.length == 0);3940 bool is_volatile = instruction->has_side_effects || (asm_expr->output_list.length == 0);
3941 LLVMValueRef asm_fn = LLVMGetInlineAsm(function_type, buf_ptr(&llvm_template), buf_len(&llvm_template),3941 LLVMValueRef asm_fn = LLVMGetInlineAsm(function_type, buf_ptr(&llvm_template), buf_len(&llvm_template),
3942 buf_ptr(&constraint_buf), buf_len(&constraint_buf), is_volatile, false, LLVMInlineAsmDialectATT);3942 buf_ptr(&constraint_buf), buf_len(&constraint_buf), is_volatile, false, LLVMInlineAsmDialectATT);
39433943
...@@ -5480,6 +5480,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -5480,6 +5480,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
5480 case IrInstructionIdCmpxchgSrc:5480 case IrInstructionIdCmpxchgSrc:
5481 case IrInstructionIdLoadPtr:5481 case IrInstructionIdLoadPtr:
5482 case IrInstructionIdBitCast:5482 case IrInstructionIdBitCast:
5483 case IrInstructionIdGlobalAsm:
5483 zig_unreachable();5484 zig_unreachable();
54845485
5485 case IrInstructionIdDeclVarGen:5486 case IrInstructionIdDeclVarGen:
src/ir.cpp+160-28
...@@ -513,6 +513,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceType *) {...@@ -513,6 +513,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceType *) {
513 return IrInstructionIdSliceType;513 return IrInstructionIdSliceType;
514}514}
515515
516static constexpr IrInstructionId ir_instruction_id(IrInstructionGlobalAsm *) {
517 return IrInstructionIdGlobalAsm;
518}
519
516static constexpr IrInstructionId ir_instruction_id(IrInstructionAsm *) {520static constexpr IrInstructionId ir_instruction_id(IrInstructionAsm *) {
517 return IrInstructionIdAsm;521 return IrInstructionIdAsm;
518}522}
...@@ -1628,10 +1632,21 @@ static IrInstruction *ir_build_slice_type(IrBuilder *irb, Scope *scope, AstNode...@@ -1628,10 +1632,21 @@ static IrInstruction *ir_build_slice_type(IrBuilder *irb, Scope *scope, AstNode
1628 return &instruction->base;1632 return &instruction->base;
1629}1633}
16301634
1631static IrInstruction *ir_build_asm(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction **input_list,1635static IrInstruction *ir_build_global_asm(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *asm_code) {
1632 IrInstruction **output_types, ZigVar **output_vars, size_t return_count, bool has_side_effects)1636 IrInstructionGlobalAsm *instruction = ir_build_instruction<IrInstructionGlobalAsm>(irb, scope, source_node);
1637 instruction->asm_code = asm_code;
1638 return &instruction->base;
1639}
1640
1641static IrInstruction *ir_build_asm(IrBuilder *irb, Scope *scope, AstNode *source_node,
1642 Buf *asm_template, AsmToken *token_list, size_t token_list_len,
1643 IrInstruction **input_list, IrInstruction **output_types, ZigVar **output_vars, size_t return_count,
1644 bool has_side_effects)
1633{1645{
1634 IrInstructionAsm *instruction = ir_build_instruction<IrInstructionAsm>(irb, scope, source_node);1646 IrInstructionAsm *instruction = ir_build_instruction<IrInstructionAsm>(irb, scope, source_node);
1647 instruction->asm_template = asm_template;
1648 instruction->token_list = token_list;
1649 instruction->token_list_len = token_list_len;
1635 instruction->input_list = input_list;1650 instruction->input_list = input_list;
1636 instruction->output_types = output_types;1651 instruction->output_types = output_types;
1637 instruction->output_vars = output_vars;1652 instruction->output_vars = output_vars;
...@@ -5861,21 +5876,142 @@ static IrInstruction *ir_gen_undefined_literal(IrBuilder *irb, Scope *scope, Ast...@@ -5861,21 +5876,142 @@ static IrInstruction *ir_gen_undefined_literal(IrBuilder *irb, Scope *scope, Ast
5861 return ir_build_const_undefined(irb, scope, node);5876 return ir_build_const_undefined(irb, scope, node);
5862}5877}
58635878
5879static Error parse_asm_template(IrBuilder *irb, AstNode *source_node, Buf *asm_template,
5880 ZigList<AsmToken> *tok_list)
5881{
5882 // TODO Connect the errors in this function back up to the actual source location
5883 // rather than just the token. https://github.com/ziglang/zig/issues/2080
5884 enum State {
5885 StateStart,
5886 StatePercent,
5887 StateTemplate,
5888 StateVar,
5889 };
5890
5891 assert(tok_list->length == 0);
5892
5893 AsmToken *cur_tok = nullptr;
5894
5895 enum State state = StateStart;
5896
5897 for (size_t i = 0; i < buf_len(asm_template); i += 1) {
5898 uint8_t c = *((uint8_t*)buf_ptr(asm_template) + i);
5899 switch (state) {
5900 case StateStart:
5901 if (c == '%') {
5902 tok_list->add_one();
5903 cur_tok = &tok_list->last();
5904 cur_tok->id = AsmTokenIdPercent;
5905 cur_tok->start = i;
5906 state = StatePercent;
5907 } else {
5908 tok_list->add_one();
5909 cur_tok = &tok_list->last();
5910 cur_tok->id = AsmTokenIdTemplate;
5911 cur_tok->start = i;
5912 state = StateTemplate;
5913 }
5914 break;
5915 case StatePercent:
5916 if (c == '%') {
5917 cur_tok->end = i;
5918 state = StateStart;
5919 } else if (c == '[') {
5920 cur_tok->id = AsmTokenIdVar;
5921 state = StateVar;
5922 } else if (c == '=') {
5923 cur_tok->id = AsmTokenIdUniqueId;
5924 cur_tok->end = i;
5925 state = StateStart;
5926 } else {
5927 add_node_error(irb->codegen, source_node,
5928 buf_create_from_str("expected a '%' or '['"));
5929 return ErrorSemanticAnalyzeFail;
5930 }
5931 break;
5932 case StateTemplate:
5933 if (c == '%') {
5934 cur_tok->end = i;
5935 i -= 1;
5936 cur_tok = nullptr;
5937 state = StateStart;
5938 }
5939 break;
5940 case StateVar:
5941 if (c == ']') {
5942 cur_tok->end = i;
5943 state = StateStart;
5944 } else if ((c >= 'a' && c <= 'z') ||
5945 (c >= '0' && c <= '9') ||
5946 (c == '_'))
5947 {
5948 // do nothing
5949 } else {
5950 add_node_error(irb->codegen, source_node,
5951 buf_sprintf("invalid substitution character: '%c'", c));
5952 return ErrorSemanticAnalyzeFail;
5953 }
5954 break;
5955 }
5956 }
5957
5958 switch (state) {
5959 case StateStart:
5960 break;
5961 case StatePercent:
5962 case StateVar:
5963 add_node_error(irb->codegen, source_node, buf_sprintf("unexpected end of assembly template"));
5964 return ErrorSemanticAnalyzeFail;
5965 case StateTemplate:
5966 cur_tok->end = buf_len(asm_template);
5967 break;
5968 }
5969 return ErrorNone;
5970}
5971
5864static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *node) {5972static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
5973 Error err;
5865 assert(node->type == NodeTypeAsmExpr);5974 assert(node->type == NodeTypeAsmExpr);
5975 AstNodeAsmExpr *asm_expr = &node->data.asm_expr;
5976 bool is_volatile = asm_expr->volatile_token != nullptr;
5977 bool in_fn_scope = (scope_fn_entry(scope) != nullptr);
5978
5979 Buf *template_buf = &asm_expr->asm_template->data.str_lit.str;
58665980
5867 IrInstruction **input_list = allocate<IrInstruction *>(node->data.asm_expr.input_list.length);5981 if (!in_fn_scope) {
5868 IrInstruction **output_types = allocate<IrInstruction *>(node->data.asm_expr.output_list.length);5982 if (is_volatile) {
5869 ZigVar **output_vars = allocate<ZigVar *>(node->data.asm_expr.output_list.length);5983 add_token_error(irb->codegen, node->owner, asm_expr->volatile_token,
5984 buf_sprintf("volatile is meaningless on global assembly"));
5985 return irb->codegen->invalid_instruction;
5986 }
5987
5988 if (asm_expr->output_list.length != 0 || asm_expr->input_list.length != 0 ||
5989 asm_expr->clobber_list.length != 0)
5990 {
5991 add_node_error(irb->codegen, node,
5992 buf_sprintf("global assembly cannot have inputs, outputs, or clobbers"));
5993 return irb->codegen->invalid_instruction;
5994 }
5995
5996 return ir_build_global_asm(irb, scope, node, template_buf);
5997 }
5998
5999 ZigList<AsmToken> tok_list = {};
6000 if ((err = parse_asm_template(irb, node, template_buf, &tok_list))) {
6001 return irb->codegen->invalid_instruction;
6002 }
6003
6004 IrInstruction **input_list = allocate<IrInstruction *>(asm_expr->input_list.length);
6005 IrInstruction **output_types = allocate<IrInstruction *>(asm_expr->output_list.length);
6006 ZigVar **output_vars = allocate<ZigVar *>(asm_expr->output_list.length);
5870 size_t return_count = 0;6007 size_t return_count = 0;
5871 bool is_volatile = node->data.asm_expr.is_volatile;6008 if (!is_volatile && asm_expr->output_list.length == 0) {
5872 if (!is_volatile && node->data.asm_expr.output_list.length == 0) {
5873 add_node_error(irb->codegen, node,6009 add_node_error(irb->codegen, node,
5874 buf_sprintf("assembly expression with no output must be marked volatile"));6010 buf_sprintf("assembly expression with no output must be marked volatile"));
5875 return irb->codegen->invalid_instruction;6011 return irb->codegen->invalid_instruction;
5876 }6012 }
5877 for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1) {6013 for (size_t i = 0; i < asm_expr->output_list.length; i += 1) {
5878 AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);6014 AsmOutput *asm_output = asm_expr->output_list.at(i);
5879 if (asm_output->return_type) {6015 if (asm_output->return_type) {
5880 return_count += 1;6016 return_count += 1;
58816017
...@@ -5911,8 +6047,8 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod...@@ -5911,8 +6047,8 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod
5911 return irb->codegen->invalid_instruction;6047 return irb->codegen->invalid_instruction;
5912 }6048 }
5913 }6049 }
5914 for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1) {6050 for (size_t i = 0; i < asm_expr->input_list.length; i += 1) {
5915 AsmInput *asm_input = node->data.asm_expr.input_list.at(i);6051 AsmInput *asm_input = asm_expr->input_list.at(i);
5916 IrInstruction *input_value = ir_gen_node(irb, asm_input->expr, scope);6052 IrInstruction *input_value = ir_gen_node(irb, asm_input->expr, scope);
5917 if (input_value == irb->codegen->invalid_instruction)6053 if (input_value == irb->codegen->invalid_instruction)
5918 return irb->codegen->invalid_instruction;6054 return irb->codegen->invalid_instruction;
...@@ -5920,7 +6056,8 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod...@@ -5920,7 +6056,8 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod
5920 input_list[i] = input_value;6056 input_list[i] = input_value;
5921 }6057 }
59226058
5923 return ir_build_asm(irb, scope, node, input_list, output_types, output_vars, return_count, is_volatile);6059 return ir_build_asm(irb, scope, node, template_buf, tok_list.items, tok_list.length,
6060 input_list, output_types, output_vars, return_count, is_volatile);
5924}6061}
59256062
5926static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstNode *node) {6063static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
...@@ -16309,27 +16446,18 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira,...@@ -16309,27 +16446,18 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira,
16309 zig_unreachable();16446 zig_unreachable();
16310}16447}
1631116448
16449static IrInstruction *ir_analyze_instruction_global_asm(IrAnalyze *ira, IrInstructionGlobalAsm *instruction) {
16450 buf_append_char(&ira->codegen->global_asm, '\n');
16451 buf_append_buf(&ira->codegen->global_asm, instruction->asm_code);
16452
16453 return ir_const_void(ira, &instruction->base);
16454}
16455
16312static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAsm *asm_instruction) {16456static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAsm *asm_instruction) {
16313 assert(asm_instruction->base.source_node->type == NodeTypeAsmExpr);16457 assert(asm_instruction->base.source_node->type == NodeTypeAsmExpr);
1631416458
16315 AstNodeAsmExpr *asm_expr = &asm_instruction->base.source_node->data.asm_expr;16459 AstNodeAsmExpr *asm_expr = &asm_instruction->base.source_node->data.asm_expr;
1631616460
16317 bool global_scope = (scope_fn_entry(asm_instruction->base.scope) == nullptr);
16318 if (global_scope) {
16319 if (asm_expr->output_list.length != 0 || asm_expr->input_list.length != 0 ||
16320 asm_expr->clobber_list.length != 0)
16321 {
16322 ir_add_error(ira, &asm_instruction->base,
16323 buf_sprintf("global assembly cannot have inputs, outputs, or clobbers"));
16324 return ira->codegen->invalid_instruction;
16325 }
16326
16327 buf_append_char(&ira->codegen->global_asm, '\n');
16328 buf_append_buf(&ira->codegen->global_asm, asm_expr->asm_template);
16329
16330 return ir_const_void(ira, &asm_instruction->base);
16331 }
16332
16333 if (!ir_emit_global_runtime_side_effect(ira, &asm_instruction->base))16461 if (!ir_emit_global_runtime_side_effect(ira, &asm_instruction->base))
16334 return ira->codegen->invalid_instruction;16462 return ira->codegen->invalid_instruction;
1633516463
...@@ -16367,6 +16495,7 @@ static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAs...@@ -16367,6 +16495,7 @@ static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAs
1636716495
16368 IrInstruction *result = ir_build_asm(&ira->new_irb,16496 IrInstruction *result = ir_build_asm(&ira->new_irb,
16369 asm_instruction->base.scope, asm_instruction->base.source_node,16497 asm_instruction->base.scope, asm_instruction->base.source_node,
16498 asm_instruction->asm_template, asm_instruction->token_list, asm_instruction->token_list_len,
16370 input_list, output_types, asm_instruction->output_vars, asm_instruction->return_count,16499 input_list, output_types, asm_instruction->output_vars, asm_instruction->return_count,
16371 asm_instruction->has_side_effects);16500 asm_instruction->has_side_effects);
16372 result->value.type = return_type;16501 result->value.type = return_type;
...@@ -22584,6 +22713,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio...@@ -22584,6 +22713,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
22584 return ir_analyze_instruction_set_float_mode(ira, (IrInstructionSetFloatMode *)instruction);22713 return ir_analyze_instruction_set_float_mode(ira, (IrInstructionSetFloatMode *)instruction);
22585 case IrInstructionIdSliceType:22714 case IrInstructionIdSliceType:
22586 return ir_analyze_instruction_slice_type(ira, (IrInstructionSliceType *)instruction);22715 return ir_analyze_instruction_slice_type(ira, (IrInstructionSliceType *)instruction);
22716 case IrInstructionIdGlobalAsm:
22717 return ir_analyze_instruction_global_asm(ira, (IrInstructionGlobalAsm *)instruction);
22587 case IrInstructionIdAsm:22718 case IrInstructionIdAsm:
22588 return ir_analyze_instruction_asm(ira, (IrInstructionAsm *)instruction);22719 return ir_analyze_instruction_asm(ira, (IrInstructionAsm *)instruction);
22589 case IrInstructionIdArrayType:22720 case IrInstructionIdArrayType:
...@@ -22938,6 +23069,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -22938,6 +23069,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
22938 case IrInstructionIdCmpxchgSrc:23069 case IrInstructionIdCmpxchgSrc:
22939 case IrInstructionIdAssertZero:23070 case IrInstructionIdAssertZero:
22940 case IrInstructionIdResizeSlice:23071 case IrInstructionIdResizeSlice:
23072 case IrInstructionIdGlobalAsm:
22941 return true;23073 return true;
2294223074
22943 case IrInstructionIdPhi:23075 case IrInstructionIdPhi:
src/ir_print.cpp+8-1
...@@ -436,11 +436,15 @@ static void ir_print_slice_type(IrPrint *irp, IrInstructionSliceType *instructio...@@ -436,11 +436,15 @@ static void ir_print_slice_type(IrPrint *irp, IrInstructionSliceType *instructio
436 ir_print_other_instruction(irp, instruction->child_type);436 ir_print_other_instruction(irp, instruction->child_type);
437}437}
438438
439static void ir_print_global_asm(IrPrint *irp, IrInstructionGlobalAsm *instruction) {
440 fprintf(irp->f, "asm(\"%s\")", buf_ptr(instruction->asm_code));
441}
442
439static void ir_print_asm(IrPrint *irp, IrInstructionAsm *instruction) {443static void ir_print_asm(IrPrint *irp, IrInstructionAsm *instruction) {
440 assert(instruction->base.source_node->type == NodeTypeAsmExpr);444 assert(instruction->base.source_node->type == NodeTypeAsmExpr);
441 AstNodeAsmExpr *asm_expr = &instruction->base.source_node->data.asm_expr;445 AstNodeAsmExpr *asm_expr = &instruction->base.source_node->data.asm_expr;
442 const char *volatile_kw = instruction->has_side_effects ? " volatile" : "";446 const char *volatile_kw = instruction->has_side_effects ? " volatile" : "";
443 fprintf(irp->f, "asm%s (\"%s\") : ", volatile_kw, buf_ptr(asm_expr->asm_template));447 fprintf(irp->f, "asm%s (\"%s\") : ", volatile_kw, buf_ptr(instruction->asm_template));
444448
445 for (size_t i = 0; i < asm_expr->output_list.length; i += 1) {449 for (size_t i = 0; i < asm_expr->output_list.length; i += 1) {
446 AsmOutput *asm_output = asm_expr->output_list.at(i);450 AsmOutput *asm_output = asm_expr->output_list.at(i);
...@@ -1519,6 +1523,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1519,6 +1523,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1519 case IrInstructionIdSliceType:1523 case IrInstructionIdSliceType:
1520 ir_print_slice_type(irp, (IrInstructionSliceType *)instruction);1524 ir_print_slice_type(irp, (IrInstructionSliceType *)instruction);
1521 break;1525 break;
1526 case IrInstructionIdGlobalAsm:
1527 ir_print_global_asm(irp, (IrInstructionGlobalAsm *)instruction);
1528 break;
1522 case IrInstructionIdAsm:1529 case IrInstructionIdAsm:
1523 ir_print_asm(irp, (IrInstructionAsm *)instruction);1530 ir_print_asm(irp, (IrInstructionAsm *)instruction);
1524 break;1531 break;
src/parser.cpp+5-111
...@@ -85,7 +85,7 @@ static AstNode *ast_parse_asm_output(ParseContext *pc);...@@ -85,7 +85,7 @@ static AstNode *ast_parse_asm_output(ParseContext *pc);
85static AsmOutput *ast_parse_asm_output_item(ParseContext *pc);85static AsmOutput *ast_parse_asm_output_item(ParseContext *pc);
86static AstNode *ast_parse_asm_input(ParseContext *pc);86static AstNode *ast_parse_asm_input(ParseContext *pc);
87static AsmInput *ast_parse_asm_input_item(ParseContext *pc);87static AsmInput *ast_parse_asm_input_item(ParseContext *pc);
88static AstNode *ast_parse_asm_cloppers(ParseContext *pc);88static AstNode *ast_parse_asm_clobbers(ParseContext *pc);
89static Token *ast_parse_break_label(ParseContext *pc);89static Token *ast_parse_break_label(ParseContext *pc);
90static Token *ast_parse_block_label(ParseContext *pc);90static Token *ast_parse_block_label(ParseContext *pc);
91static AstNode *ast_parse_field_init(ParseContext *pc);91static AstNode *ast_parse_field_init(ParseContext *pc);
...@@ -140,24 +140,6 @@ static void ast_error(ParseContext *pc, Token *token, const char *format, ...) {...@@ -140,24 +140,6 @@ static void ast_error(ParseContext *pc, Token *token, const char *format, ...) {
140 exit(EXIT_FAILURE);140 exit(EXIT_FAILURE);
141}141}
142142
143ATTRIBUTE_PRINTF(4, 5)
144ATTRIBUTE_NORETURN
145static void ast_asm_error(ParseContext *pc, AstNode *node, size_t offset, const char *format, ...) {
146 assert(node->type == NodeTypeAsmExpr);
147 va_list ap;
148 va_start(ap, format);
149 Buf *msg = buf_vprintf(format, ap);
150 va_end(ap);
151
152 ErrorMsg *err = err_msg_create_with_line(pc->owner->data.structure.root_struct->path,
153 node->line, node->column,
154 pc->owner->data.structure.root_struct->source_code,
155 pc->owner->data.structure.root_struct->line_offsets, msg);
156
157 print_err_msg(err, pc->err_color);
158 exit(EXIT_FAILURE);
159}
160
161static Buf ast_token_str(Buf *input, Token *token) {143static Buf ast_token_str(Buf *input, Token *token) {
162 Buf str = BUF_INIT;144 Buf str = BUF_INIT;
163 buf_init_from_mem(&str, buf_ptr(input) + token->start_pos, token->end_pos - token->start_pos);145 buf_init_from_mem(&str, buf_ptr(input) + token->start_pos, token->end_pos - token->start_pos);
...@@ -486,93 +468,6 @@ AstNode *ast_parse_bin_op_simple(ParseContext *pc) {...@@ -486,93 +468,6 @@ AstNode *ast_parse_bin_op_simple(ParseContext *pc) {
486 return res;468 return res;
487}469}
488470
489static void ast_parse_asm_template(ParseContext *pc, AstNode *node) {
490 Buf *asm_template = node->data.asm_expr.asm_template;
491
492 enum State {
493 StateStart,
494 StatePercent,
495 StateTemplate,
496 StateVar,
497 };
498
499 ZigList<AsmToken> *tok_list = &node->data.asm_expr.token_list;
500 assert(tok_list->length == 0);
501
502 AsmToken *cur_tok = nullptr;
503
504 enum State state = StateStart;
505
506 for (size_t i = 0; i < buf_len(asm_template); i += 1) {
507 uint8_t c = *((uint8_t*)buf_ptr(asm_template) + i);
508 switch (state) {
509 case StateStart:
510 if (c == '%') {
511 tok_list->add_one();
512 cur_tok = &tok_list->last();
513 cur_tok->id = AsmTokenIdPercent;
514 cur_tok->start = i;
515 state = StatePercent;
516 } else {
517 tok_list->add_one();
518 cur_tok = &tok_list->last();
519 cur_tok->id = AsmTokenIdTemplate;
520 cur_tok->start = i;
521 state = StateTemplate;
522 }
523 break;
524 case StatePercent:
525 if (c == '%') {
526 cur_tok->end = i;
527 state = StateStart;
528 } else if (c == '[') {
529 cur_tok->id = AsmTokenIdVar;
530 state = StateVar;
531 } else if (c == '=') {
532 cur_tok->id = AsmTokenIdUniqueId;
533 cur_tok->end = i;
534 state = StateStart;
535 } else {
536 ast_asm_error(pc, node, i, "expected a '%%' or '['");
537 }
538 break;
539 case StateTemplate:
540 if (c == '%') {
541 cur_tok->end = i;
542 i -= 1;
543 cur_tok = nullptr;
544 state = StateStart;
545 }
546 break;
547 case StateVar:
548 if (c == ']') {
549 cur_tok->end = i;
550 state = StateStart;
551 } else if ((c >= 'a' && c <= 'z') ||
552 (c >= '0' && c <= '9') ||
553 (c == '_'))
554 {
555 // do nothing
556 } else {
557 ast_asm_error(pc, node, i, "invalid substitution character: '%c'", c);
558 }
559 break;
560 }
561 }
562
563 switch (state) {
564 case StateStart:
565 break;
566 case StatePercent:
567 case StateVar:
568 ast_asm_error(pc, node, buf_len(asm_template), "unexpected end of assembly template");
569 break;
570 case StateTemplate:
571 cur_tok->end = buf_len(asm_template);
572 break;
573 }
574}
575
576AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ZigType *owner, ErrColor err_color) {471AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ZigType *owner, ErrColor err_color) {
577 ParseContext pc = {};472 ParseContext pc = {};
578 pc.err_color = err_color;473 pc.err_color = err_color;
...@@ -1931,9 +1826,8 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc) {...@@ -1931,9 +1826,8 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc) {
19311826
1932 res->line = asm_token->start_line;1827 res->line = asm_token->start_line;
1933 res->column = asm_token->start_column;1828 res->column = asm_token->start_column;
1934 res->data.asm_expr.is_volatile = volatile_token != nullptr;1829 res->data.asm_expr.volatile_token = volatile_token;
1935 res->data.asm_expr.asm_template = token_buf(asm_template);1830 res->data.asm_expr.asm_template = asm_template;
1936 ast_parse_asm_template(pc, res);
1937 return res;1831 return res;
1938}1832}
19391833
...@@ -1985,7 +1879,7 @@ static AstNode *ast_parse_asm_input(ParseContext *pc) {...@@ -1985,7 +1879,7 @@ static AstNode *ast_parse_asm_input(ParseContext *pc) {
1985 return nullptr;1879 return nullptr;
19861880
1987 ZigList<AsmInput *> input_list = ast_parse_list(pc, TokenIdComma, ast_parse_asm_input_item);1881 ZigList<AsmInput *> input_list = ast_parse_list(pc, TokenIdComma, ast_parse_asm_input_item);
1988 AstNode *res = ast_parse_asm_cloppers(pc);1882 AstNode *res = ast_parse_asm_clobbers(pc);
1989 if (res == nullptr)1883 if (res == nullptr)
1990 res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr);1884 res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr);
19911885
...@@ -2013,7 +1907,7 @@ static AsmInput *ast_parse_asm_input_item(ParseContext *pc) {...@@ -2013,7 +1907,7 @@ static AsmInput *ast_parse_asm_input_item(ParseContext *pc) {
2013}1907}
20141908
2015// AsmClobbers <- COLON StringList1909// AsmClobbers <- COLON StringList
2016static AstNode *ast_parse_asm_cloppers(ParseContext *pc) {1910static AstNode *ast_parse_asm_clobbers(ParseContext *pc) {
2017 if (eat_token_if(pc, TokenIdColon) == nullptr)1911 if (eat_token_if(pc, TokenIdColon) == nullptr)
2018 return nullptr;1912 return nullptr;
20191913
test/compile_errors.zig+9
...@@ -2,6 +2,15 @@ const tests = @import("tests.zig");...@@ -2,6 +2,15 @@ const tests = @import("tests.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub fn addCases(cases: *tests.CompileErrorContext) void {4pub fn addCases(cases: *tests.CompileErrorContext) void {
5 cases.add(
6 "volatile on global assembly",
7 \\comptime {
8 \\ asm volatile ("");
9 \\}
10 ,
11 "tmp.zig:2:9: error: volatile is meaningless on global assembly",
12 );
13
5 cases.add(14 cases.add(
6 "invalid multiple dereferences",15 "invalid multiple dereferences",
7 \\export fn a() void {16 \\export fn a() void {
test/stage1/behavior/asm.zig+1-1
...@@ -3,7 +3,7 @@ const expect = @import("std").testing.expect;...@@ -3,7 +3,7 @@ const expect = @import("std").testing.expect;
33
4comptime {4comptime {
5 if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) {5 if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) {
6 asm volatile (6 asm (
7 \\.globl this_is_my_alias;7 \\.globl this_is_my_alias;
8 \\.type this_is_my_alias, @function;8 \\.type this_is_my_alias, @function;
9 \\.set this_is_my_alias, derp;9 \\.set this_is_my_alias, derp;