| author | |
| committer | |
| log | a1486e1e1e52175d3e1b26049ea960b9c00edeb7 |
| tree | 3f5a476d5f50e65a12def9a4c3dbb3b21aee167a |
| parent | 1e963053d0ff67361b587b046a917375e963d5e9 |
It is not yet determined whether the Zig language will land on
text-based string concatenation for inline assembly, as Zig 0.9.1
allows, and as this commit allows, or whether it will introduce a new
assembly syntax more integrated with the rest of the language. Until
this decision is made, this commit relaxes the restriction which was
preventing inline assembly expressions from using comptime expressions
for the assembly source code.4 files changed, 45 insertions(+), 29 deletions(-)
src/AstGen.zig+17-17| ... | @@ -7194,21 +7194,19 @@ fn asmExpr( | ... | @@ -7194,21 +7194,19 @@ fn asmExpr( |
| 7194 | const node_tags = tree.nodes.items(.tag); | 7194 | const node_tags = tree.nodes.items(.tag); |
| 7195 | const token_tags = tree.tokens.items(.tag); | 7195 | const token_tags = tree.tokens.items(.tag); |
| 7196 | 7196 | ||
| 7197 | const asm_source = switch (node_tags[full.ast.template]) { | 7197 | const TagAndTmpl = struct { tag: Zir.Inst.Extended, tmpl: u32 }; |
| 7198 | .string_literal => try astgen.strLitAsString(main_tokens[full.ast.template]), | 7198 | const tag_and_tmpl: TagAndTmpl = switch (node_tags[full.ast.template]) { |
| 7199 | .multiline_string_literal => try astgen.strLitNodeAsString(full.ast.template), | 7199 | .string_literal => .{ |
| 7200 | else => blk: { | 7200 | .tag = .@"asm", |
| 7201 | // stage1 allows this, and until we do another design iteration on inline assembly | 7201 | .tmpl = (try astgen.strLitAsString(main_tokens[full.ast.template])).index, |
| 7202 | // in stage2 to improve support for the various needed use cases, we allow inline | 7202 | }, |
| 7203 | // assembly templates to be an expression. Once stage2 addresses the real world needs | 7203 | .multiline_string_literal => .{ |
| 7204 | // of people using inline assembly (primarily OS developers) then we can re-institute | 7204 | .tag = .@"asm", |
| 7205 | // the rule into AstGen that assembly code must use string literal syntax. | 7205 | .tmpl = (try astgen.strLitNodeAsString(full.ast.template)).index, |
| 7206 | //return astgen.failNode(full.ast.template, "assembly code must use string literal syntax", .{}), | 7206 | }, |
| 7207 | // We still need to trigger all the expr() calls here to avoid errors for unused things. | 7207 | else => .{ |
| 7208 | // So we pass 0 as the asm source and stage2 Sema will notice this and | 7208 | .tag = .asm_expr, |
| 7209 | // report the error. | 7209 | .tmpl = @enumToInt(try comptimeExpr(gz, scope, .none, full.ast.template)), |
| 7210 | _ = try comptimeExpr(gz, scope, .none, full.ast.template); | ||
| 7211 | break :blk IndexSlice{ .index = 0, .len = 0 }; | ||
| 7212 | }, | 7210 | }, |
| 7213 | }; | 7211 | }; |
| 7214 | 7212 | ||
| ... | @@ -7312,8 +7310,9 @@ fn asmExpr( | ... | @@ -7312,8 +7310,9 @@ fn asmExpr( |
| 7312 | } | 7310 | } |
| 7313 | 7311 | ||
| 7314 | const result = try gz.addAsm(.{ | 7312 | const result = try gz.addAsm(.{ |
| 7313 | .tag = tag_and_tmpl.tag, | ||
| 7315 | .node = node, | 7314 | .node = node, |
| 7316 | .asm_source = asm_source.index, | 7315 | .asm_source = tag_and_tmpl.tmpl, |
| 7317 | .is_volatile = full.volatile_token != null, | 7316 | .is_volatile = full.volatile_token != null, |
| 7318 | .output_type_bits = output_type_bits, | 7317 | .output_type_bits = output_type_bits, |
| 7319 | .outputs = outputs, | 7318 | .outputs = outputs, |
| ... | @@ -11314,6 +11313,7 @@ const GenZir = struct { | ... | @@ -11314,6 +11313,7 @@ const GenZir = struct { |
| 11314 | fn addAsm( | 11313 | fn addAsm( |
| 11315 | gz: *GenZir, | 11314 | gz: *GenZir, |
| 11316 | args: struct { | 11315 | args: struct { |
| 11316 | tag: Zir.Inst.Extended, | ||
| 11317 | /// Absolute node index. This function does the conversion to offset from Decl. | 11317 | /// Absolute node index. This function does the conversion to offset from Decl. |
| 11318 | node: Ast.Node.Index, | 11318 | node: Ast.Node.Index, |
| 11319 | asm_source: u32, | 11319 | asm_source: u32, |
| ... | @@ -11360,7 +11360,7 @@ const GenZir = struct { | ... | @@ -11360,7 +11360,7 @@ const GenZir = struct { |
| 11360 | astgen.instructions.appendAssumeCapacity(.{ | 11360 | astgen.instructions.appendAssumeCapacity(.{ |
| 11361 | .tag = .extended, | 11361 | .tag = .extended, |
| 11362 | .data = .{ .extended = .{ | 11362 | .data = .{ .extended = .{ |
| 11363 | .opcode = .@"asm", | 11363 | .opcode = args.tag, |
| 11364 | .small = small, | 11364 | .small = small, |
| 11365 | .operand = payload_index, | 11365 | .operand = payload_index, |
| 11366 | } }, | 11366 | } }, |
src/Sema.zig+8-8| ... | @@ -953,7 +953,8 @@ fn analyzeBodyInner( | ... | @@ -953,7 +953,8 @@ fn analyzeBodyInner( |
| 953 | .frame_address => try sema.zirFrameAddress( block, extended), | 953 | .frame_address => try sema.zirFrameAddress( block, extended), |
| 954 | .alloc => try sema.zirAllocExtended( block, extended), | 954 | .alloc => try sema.zirAllocExtended( block, extended), |
| 955 | .builtin_extern => try sema.zirBuiltinExtern( block, extended), | 955 | .builtin_extern => try sema.zirBuiltinExtern( block, extended), |
| 956 | .@"asm" => try sema.zirAsm( block, extended), | 956 | .@"asm" => try sema.zirAsm( block, extended, false), |
| 957 | .asm_expr => try sema.zirAsm( block, extended, true), | ||
| 957 | .typeof_peer => try sema.zirTypeofPeer( block, extended), | 958 | .typeof_peer => try sema.zirTypeofPeer( block, extended), |
| 958 | .compile_log => try sema.zirCompileLog( block, extended), | 959 | .compile_log => try sema.zirCompileLog( block, extended), |
| 959 | .add_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), | 960 | .add_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), |
| ... | @@ -13846,6 +13847,7 @@ fn zirAsm( | ... | @@ -13846,6 +13847,7 @@ fn zirAsm( |
| 13846 | sema: *Sema, | 13847 | sema: *Sema, |
| 13847 | block: *Block, | 13848 | block: *Block, |
| 13848 | extended: Zir.Inst.Extended.InstData, | 13849 | extended: Zir.Inst.Extended.InstData, |
| 13850 | tmpl_is_expr: bool, | ||
| 13849 | ) CompileError!Air.Inst.Ref { | 13851 | ) CompileError!Air.Inst.Ref { |
| 13850 | const tracy = trace(@src()); | 13852 | const tracy = trace(@src()); |
| 13851 | defer tracy.end(); | 13853 | defer tracy.end(); |
| ... | @@ -13859,13 +13861,11 @@ fn zirAsm( | ... | @@ -13859,13 +13861,11 @@ fn zirAsm( |
| 13859 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | 13861 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; |
| 13860 | const is_global_assembly = sema.func == null; | 13862 | const is_global_assembly = sema.func == null; |
| 13861 | 13863 | ||
| 13862 | if (extra.data.asm_source == 0) { | 13864 | const asm_source: []const u8 = if (tmpl_is_expr) blk: { |
| 13863 | // This can move to become an AstGen error after inline assembly improvements land | 13865 | const tmpl = @intToEnum(Zir.Inst.Ref, extra.data.asm_source); |
| 13864 | // and stage1 code matches stage2 code. | 13866 | const s: []const u8 = try sema.resolveConstString(block, src, tmpl, "assembly code must be comptime-known"); |
| 13865 | return sema.fail(block, src, "assembly code must use string literal syntax", .{}); | 13867 | break :blk s; |
| 13866 | } | 13868 | } else sema.code.nullTerminatedString(extra.data.asm_source); |
| 13867 | |||
| 13868 | const asm_source = sema.code.nullTerminatedString(extra.data.asm_source); | ||
| 13869 | 13869 | ||
| 13870 | if (is_global_assembly) { | 13870 | if (is_global_assembly) { |
| 13871 | if (outputs_len != 0) { | 13871 | if (outputs_len != 0) { |
src/Zir.zig+5| ... | @@ -1883,6 +1883,11 @@ pub const Inst = struct { | ... | @@ -1883,6 +1883,11 @@ pub const Inst = struct { |
| 1883 | /// * 0bX0000000_00000000 - is volatile | 1883 | /// * 0bX0000000_00000000 - is volatile |
| 1884 | /// `operand` is payload index to `Asm`. | 1884 | /// `operand` is payload index to `Asm`. |
| 1885 | @"asm", | 1885 | @"asm", |
| 1886 | /// Same as `asm` except the assembly template is not a string literal but a comptime | ||
| 1887 | /// expression. | ||
| 1888 | /// The `asm_source` field of the Asm is not a null-terminated string | ||
| 1889 | /// but instead a Ref. | ||
| 1890 | asm_expr, | ||
| 1886 | /// Log compile time variables and emit an error message. | 1891 | /// Log compile time variables and emit an error message. |
| 1887 | /// `operand` is payload index to `NodeMultiOp`. | 1892 | /// `operand` is payload index to `NodeMultiOp`. |
| 1888 | /// `small` is `operands_len`. | 1893 | /// `small` is `operands_len`. |
src/print_zir.zig+15-4| ... | @@ -469,7 +469,8 @@ const Writer = struct { | ... | @@ -469,7 +469,8 @@ const Writer = struct { |
| 469 | try stream.print(":{d}:{d}", .{ inst_data.line + 1, inst_data.column + 1 }); | 469 | try stream.print(":{d}:{d}", .{ inst_data.line + 1, inst_data.column + 1 }); |
| 470 | }, | 470 | }, |
| 471 | 471 | ||
| 472 | .@"asm" => try self.writeAsm(stream, extended), | 472 | .@"asm" => try self.writeAsm(stream, extended, false), |
| 473 | .asm_expr => try self.writeAsm(stream, extended, true), | ||
| 473 | .variable => try self.writeVarExtended(stream, extended), | 474 | .variable => try self.writeVarExtended(stream, extended), |
| 474 | .alloc => try self.writeAllocExtended(stream, extended), | 475 | .alloc => try self.writeAllocExtended(stream, extended), |
| 475 | 476 | ||
| ... | @@ -1062,17 +1063,27 @@ const Writer = struct { | ... | @@ -1062,17 +1063,27 @@ const Writer = struct { |
| 1062 | try self.writeSrc(stream, inst_data.src()); | 1063 | try self.writeSrc(stream, inst_data.src()); |
| 1063 | } | 1064 | } |
| 1064 | 1065 | ||
| 1065 | fn writeAsm(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { | 1066 | fn writeAsm( |
| 1067 | self: *Writer, | ||
| 1068 | stream: anytype, | ||
| 1069 | extended: Zir.Inst.Extended.InstData, | ||
| 1070 | tmpl_is_expr: bool, | ||
| 1071 | ) !void { | ||
| 1066 | const extra = self.code.extraData(Zir.Inst.Asm, extended.operand); | 1072 | const extra = self.code.extraData(Zir.Inst.Asm, extended.operand); |
| 1067 | const src = LazySrcLoc.nodeOffset(extra.data.src_node); | 1073 | const src = LazySrcLoc.nodeOffset(extra.data.src_node); |
| 1068 | const outputs_len = @truncate(u5, extended.small); | 1074 | const outputs_len = @truncate(u5, extended.small); |
| 1069 | const inputs_len = @truncate(u5, extended.small >> 5); | 1075 | const inputs_len = @truncate(u5, extended.small >> 5); |
| 1070 | const clobbers_len = @truncate(u5, extended.small >> 10); | 1076 | const clobbers_len = @truncate(u5, extended.small >> 10); |
| 1071 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | 1077 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; |
| 1072 | const asm_source = self.code.nullTerminatedString(extra.data.asm_source); | ||
| 1073 | 1078 | ||
| 1074 | try self.writeFlag(stream, "volatile, ", is_volatile); | 1079 | try self.writeFlag(stream, "volatile, ", is_volatile); |
| 1075 | try stream.print("\"{}\", ", .{std.zig.fmtEscapes(asm_source)}); | 1080 | if (tmpl_is_expr) { |
| 1081 | try self.writeInstRef(stream, @intToEnum(Zir.Inst.Ref, extra.data.asm_source)); | ||
| 1082 | try stream.writeAll(", "); | ||
| 1083 | } else { | ||
| 1084 | const asm_source = self.code.nullTerminatedString(extra.data.asm_source); | ||
| 1085 | try stream.print("\"{}\", ", .{std.zig.fmtEscapes(asm_source)}); | ||
| 1086 | } | ||
| 1076 | try stream.writeAll(", "); | 1087 | try stream.writeAll(", "); |
| 1077 | 1088 | ||
| 1078 | var extra_i: usize = extra.end; | 1089 | var extra_i: usize = extra.end; |