diff --git a/src/AstGen.zig b/src/AstGen.zig index 7bb2ef765c..49c5b80967 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -7194,21 +7194,19 @@ fn asmExpr( const node_tags = tree.nodes.items(.tag); const token_tags = tree.tokens.items(.tag); - const asm_source = switch (node_tags[full.ast.template]) { - .string_literal => try astgen.strLitAsString(main_tokens[full.ast.template]), - .multiline_string_literal => try astgen.strLitNodeAsString(full.ast.template), - else => blk: { - // stage1 allows this, and until we do another design iteration on inline assembly - // in stage2 to improve support for the various needed use cases, we allow inline - // assembly templates to be an expression. Once stage2 addresses the real world needs - // of people using inline assembly (primarily OS developers) then we can re-institute - // the rule into AstGen that assembly code must use string literal syntax. - //return astgen.failNode(full.ast.template, "assembly code must use string literal syntax", .{}), - // We still need to trigger all the expr() calls here to avoid errors for unused things. - // So we pass 0 as the asm source and stage2 Sema will notice this and - // report the error. - _ = try comptimeExpr(gz, scope, .none, full.ast.template); - break :blk IndexSlice{ .index = 0, .len = 0 }; + const TagAndTmpl = struct { tag: Zir.Inst.Extended, tmpl: u32 }; + const tag_and_tmpl: TagAndTmpl = switch (node_tags[full.ast.template]) { + .string_literal => .{ + .tag = .@"asm", + .tmpl = (try astgen.strLitAsString(main_tokens[full.ast.template])).index, + }, + .multiline_string_literal => .{ + .tag = .@"asm", + .tmpl = (try astgen.strLitNodeAsString(full.ast.template)).index, + }, + else => .{ + .tag = .asm_expr, + .tmpl = @enumToInt(try comptimeExpr(gz, scope, .none, full.ast.template)), }, }; @@ -7312,8 +7310,9 @@ fn asmExpr( } const result = try gz.addAsm(.{ + .tag = tag_and_tmpl.tag, .node = node, - .asm_source = asm_source.index, + .asm_source = tag_and_tmpl.tmpl, .is_volatile = full.volatile_token != null, .output_type_bits = output_type_bits, .outputs = outputs, @@ -11314,6 +11313,7 @@ const GenZir = struct { fn addAsm( gz: *GenZir, args: struct { + tag: Zir.Inst.Extended, /// Absolute node index. This function does the conversion to offset from Decl. node: Ast.Node.Index, asm_source: u32, @@ -11360,7 +11360,7 @@ const GenZir = struct { astgen.instructions.appendAssumeCapacity(.{ .tag = .extended, .data = .{ .extended = .{ - .opcode = .@"asm", + .opcode = args.tag, .small = small, .operand = payload_index, } }, diff --git a/src/Sema.zig b/src/Sema.zig index 08f3b59661..4807e68f48 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -953,7 +953,8 @@ fn analyzeBodyInner( .frame_address => try sema.zirFrameAddress( block, extended), .alloc => try sema.zirAllocExtended( block, extended), .builtin_extern => try sema.zirBuiltinExtern( block, extended), - .@"asm" => try sema.zirAsm( block, extended), + .@"asm" => try sema.zirAsm( block, extended, false), + .asm_expr => try sema.zirAsm( block, extended, true), .typeof_peer => try sema.zirTypeofPeer( block, extended), .compile_log => try sema.zirCompileLog( block, extended), .add_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), @@ -13846,6 +13847,7 @@ fn zirAsm( sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, + tmpl_is_expr: bool, ) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -13859,13 +13861,11 @@ fn zirAsm( const is_volatile = @truncate(u1, extended.small >> 15) != 0; const is_global_assembly = sema.func == null; - if (extra.data.asm_source == 0) { - // This can move to become an AstGen error after inline assembly improvements land - // and stage1 code matches stage2 code. - return sema.fail(block, src, "assembly code must use string literal syntax", .{}); - } - - const asm_source = sema.code.nullTerminatedString(extra.data.asm_source); + const asm_source: []const u8 = if (tmpl_is_expr) blk: { + const tmpl = @intToEnum(Zir.Inst.Ref, extra.data.asm_source); + const s: []const u8 = try sema.resolveConstString(block, src, tmpl, "assembly code must be comptime-known"); + break :blk s; + } else sema.code.nullTerminatedString(extra.data.asm_source); if (is_global_assembly) { if (outputs_len != 0) { diff --git a/src/Zir.zig b/src/Zir.zig index 351330b7c4..01b52a8a7c 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -1883,6 +1883,11 @@ pub const Inst = struct { /// * 0bX0000000_00000000 - is volatile /// `operand` is payload index to `Asm`. @"asm", + /// Same as `asm` except the assembly template is not a string literal but a comptime + /// expression. + /// The `asm_source` field of the Asm is not a null-terminated string + /// but instead a Ref. + asm_expr, /// Log compile time variables and emit an error message. /// `operand` is payload index to `NodeMultiOp`. /// `small` is `operands_len`. diff --git a/src/print_zir.zig b/src/print_zir.zig index f2a79d53a4..14183d458c 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -469,7 +469,8 @@ const Writer = struct { try stream.print(":{d}:{d}", .{ inst_data.line + 1, inst_data.column + 1 }); }, - .@"asm" => try self.writeAsm(stream, extended), + .@"asm" => try self.writeAsm(stream, extended, false), + .asm_expr => try self.writeAsm(stream, extended, true), .variable => try self.writeVarExtended(stream, extended), .alloc => try self.writeAllocExtended(stream, extended), @@ -1062,17 +1063,27 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } - fn writeAsm(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { + fn writeAsm( + self: *Writer, + stream: anytype, + extended: Zir.Inst.Extended.InstData, + tmpl_is_expr: bool, + ) !void { const extra = self.code.extraData(Zir.Inst.Asm, extended.operand); const src = LazySrcLoc.nodeOffset(extra.data.src_node); const outputs_len = @truncate(u5, extended.small); const inputs_len = @truncate(u5, extended.small >> 5); const clobbers_len = @truncate(u5, extended.small >> 10); const is_volatile = @truncate(u1, extended.small >> 15) != 0; - const asm_source = self.code.nullTerminatedString(extra.data.asm_source); try self.writeFlag(stream, "volatile, ", is_volatile); - try stream.print("\"{}\", ", .{std.zig.fmtEscapes(asm_source)}); + if (tmpl_is_expr) { + try self.writeInstRef(stream, @intToEnum(Zir.Inst.Ref, extra.data.asm_source)); + try stream.writeAll(", "); + } else { + const asm_source = self.code.nullTerminatedString(extra.data.asm_source); + try stream.print("\"{}\", ", .{std.zig.fmtEscapes(asm_source)}); + } try stream.writeAll(", "); var extra_i: usize = extra.end;