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(
71947194 const node_tags = tree.nodes.items(.tag);
71957195 const token_tags = tree.tokens.items(.tag);
71967196
7197 const asm_source = switch (node_tags[full.ast.template]) {
7198 .string_literal => try astgen.strLitAsString(main_tokens[full.ast.template]),
7199 .multiline_string_literal => try astgen.strLitNodeAsString(full.ast.template),
7200 else => blk: {
7201 // stage1 allows this, and until we do another design iteration on inline assembly
7202 // in stage2 to improve support for the various needed use cases, we allow inline
7203 // assembly templates to be an expression. Once stage2 addresses the real world needs
7204 // of people using inline assembly (primarily OS developers) then we can re-institute
7205 // the rule into AstGen that assembly code must use string literal syntax.
7206 //return astgen.failNode(full.ast.template, "assembly code must use string literal syntax", .{}),
7207 // We still need to trigger all the expr() calls here to avoid errors for unused things.
7208 // So we pass 0 as the asm source and stage2 Sema will notice this and
7209 // report the error.
7210 _ = try comptimeExpr(gz, scope, .none, full.ast.template);
7211 break :blk IndexSlice{ .index = 0, .len = 0 };
7197 const TagAndTmpl = struct { tag: Zir.Inst.Extended, tmpl: u32 };
7198 const tag_and_tmpl: TagAndTmpl = switch (node_tags[full.ast.template]) {
7199 .string_literal => .{
7200 .tag = .@"asm",
7201 .tmpl = (try astgen.strLitAsString(main_tokens[full.ast.template])).index,
7202 },
7203 .multiline_string_literal => .{
7204 .tag = .@"asm",
7205 .tmpl = (try astgen.strLitNodeAsString(full.ast.template)).index,
7206 },
7207 else => .{
7208 .tag = .asm_expr,
7209 .tmpl = @enumToInt(try comptimeExpr(gz, scope, .none, full.ast.template)),
72127210 },
72137211 };
72147212
......@@ -7312,8 +7310,9 @@ fn asmExpr(
73127310 }
73137311
73147312 const result = try gz.addAsm(.{
7313 .tag = tag_and_tmpl.tag,
73157314 .node = node,
7316 .asm_source = asm_source.index,
7315 .asm_source = tag_and_tmpl.tmpl,
73177316 .is_volatile = full.volatile_token != null,
73187317 .output_type_bits = output_type_bits,
73197318 .outputs = outputs,
......@@ -11314,6 +11313,7 @@ const GenZir = struct {
1131411313 fn addAsm(
1131511314 gz: *GenZir,
1131611315 args: struct {
11316 tag: Zir.Inst.Extended,
1131711317 /// Absolute node index. This function does the conversion to offset from Decl.
1131811318 node: Ast.Node.Index,
1131911319 asm_source: u32,
......@@ -11360,7 +11360,7 @@ const GenZir = struct {
1136011360 astgen.instructions.appendAssumeCapacity(.{
1136111361 .tag = .extended,
1136211362 .data = .{ .extended = .{
11363 .opcode = .@"asm",
11363 .opcode = args.tag,
1136411364 .small = small,
1136511365 .operand = payload_index,
1136611366 } },
src/Sema.zig+8-8
......@@ -953,7 +953,8 @@ fn analyzeBodyInner(
953953 .frame_address => try sema.zirFrameAddress( block, extended),
954954 .alloc => try sema.zirAllocExtended( block, extended),
955955 .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),
957958 .typeof_peer => try sema.zirTypeofPeer( block, extended),
958959 .compile_log => try sema.zirCompileLog( block, extended),
959960 .add_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
......@@ -13846,6 +13847,7 @@ fn zirAsm(
1384613847 sema: *Sema,
1384713848 block: *Block,
1384813849 extended: Zir.Inst.Extended.InstData,
13850 tmpl_is_expr: bool,
1384913851) CompileError!Air.Inst.Ref {
1385013852 const tracy = trace(@src());
1385113853 defer tracy.end();
......@@ -13859,13 +13861,11 @@ fn zirAsm(
1385913861 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
1386013862 const is_global_assembly = sema.func == null;
1386113863
13862 if (extra.data.asm_source == 0) {
13863 // This can move to become an AstGen error after inline assembly improvements land
13864 // and stage1 code matches stage2 code.
13865 return sema.fail(block, src, "assembly code must use string literal syntax", .{});
13866 }
13867
13868 const asm_source = sema.code.nullTerminatedString(extra.data.asm_source);
13864 const asm_source: []const u8 = if (tmpl_is_expr) blk: {
13865 const tmpl = @intToEnum(Zir.Inst.Ref, extra.data.asm_source);
13866 const s: []const u8 = try sema.resolveConstString(block, src, tmpl, "assembly code must be comptime-known");
13867 break :blk s;
13868 } else sema.code.nullTerminatedString(extra.data.asm_source);
1386913869
1387013870 if (is_global_assembly) {
1387113871 if (outputs_len != 0) {
src/Zir.zig+5
......@@ -1883,6 +1883,11 @@ pub const Inst = struct {
18831883 /// * 0bX0000000_00000000 - is volatile
18841884 /// `operand` is payload index to `Asm`.
18851885 @"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,
18861891 /// Log compile time variables and emit an error message.
18871892 /// `operand` is payload index to `NodeMultiOp`.
18881893 /// `small` is `operands_len`.
src/print_zir.zig+15-4
......@@ -469,7 +469,8 @@ const Writer = struct {
469469 try stream.print(":{d}:{d}", .{ inst_data.line + 1, inst_data.column + 1 });
470470 },
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),
473474 .variable => try self.writeVarExtended(stream, extended),
474475 .alloc => try self.writeAllocExtended(stream, extended),
475476
......@@ -1062,17 +1063,27 @@ const Writer = struct {
10621063 try self.writeSrc(stream, inst_data.src());
10631064 }
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 {
10661072 const extra = self.code.extraData(Zir.Inst.Asm, extended.operand);
10671073 const src = LazySrcLoc.nodeOffset(extra.data.src_node);
10681074 const outputs_len = @truncate(u5, extended.small);
10691075 const inputs_len = @truncate(u5, extended.small >> 5);
10701076 const clobbers_len = @truncate(u5, extended.small >> 10);
10711077 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
1072 const asm_source = self.code.nullTerminatedString(extra.data.asm_source);
10731078
10741079 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 }
10761087 try stream.writeAll(", ");
10771088
10781089 var extra_i: usize = extra.end;