authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-10-16 09:29:03-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-10-17 16:31:23-04:00
loga1486e1e1e52175d3e1b26049ea960b9c00edeb7
tree3f5a476d5f50e65a12def9a4c3dbb3b21aee167a
parent1e963053d0ff67361b587b046a917375e963d5e9

stage2: allow comptime expressions for inline asm

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);
71967196
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 assembly7201 .tmpl = (try astgen.strLitAsString(main_tokens[full.ast.template])).index,
7202 // in stage2 to improve support for the various needed use cases, we allow inline7202 },
7203 // assembly templates to be an expression. Once stage2 addresses the real world needs7203 .multiline_string_literal => .{
7204 // of people using inline assembly (primarily OS developers) then we can re-institute7204 .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 and7208 .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 };
72147212
...@@ -7312,8 +7310,9 @@ fn asmExpr(...@@ -7312,8 +7310,9 @@ fn asmExpr(
7312 }7310 }
73137311
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;
1386113863
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 land13865 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);
1386913869
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 volatile1883 /// * 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 },
471471
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),
475476
...@@ -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 }
10641065
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);
10731078
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(", ");
10771088
1078 var extra_i: usize = extra.end;1089 var extra_i: usize = extra.end;