authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-02 12:32:30-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-02 19:11:19-07:00
log9362f382ab7023592cc1d71044217b847b122406
tree3587f4c88b949673a94e995367414d80a5ef68af
parentfea8659b82ea1a785f933c58ba9d65ceb05a4094

stage2: implement function call inlining in the frontend

* remove the -Ddump-zir thing. that's handled through --verbose-ir * rework Fn to have an is_inline flag without requiring any more memory on the heap per function. * implement a rough first version of dumping typed zir (tzir) which is a lot more helpful for debugging than what we had before. We don't have a way to parse it though. * keep track of whether the inline-ness of a function changes because if it does we have to go update callsites. * add compile error for inline and export used together. inline function calls and comptime function calls are implemented the same way. A block instruction is set up to capture the result, and then a scope is set up that has a flag for is_comptime and some state if the scope is being inlined. when analyzing `ret` instructions, zig looks for inlining state in the scope, and if found, treats `ret` as a `break` instruction instead, with the target block being the one set up at the inline callsite. Follow-up items: * Complete out the debug TZIR dumping code. * Don't redundantly generate ZIR for each inline/comptime function call. Instead we should add a new state enum tag to Fn. * comptime and inlining branch quotas. * Add more test cases.

13 files changed, 549 insertions(+), 254 deletions(-)

build.zig-2
......@@ -220,7 +220,6 @@ pub fn build(b: *Builder) !void {
220220 }
221221
222222 const log_scopes = b.option([]const []const u8, "log", "Which log scopes to enable") orelse &[0][]const u8{};
223 const zir_dumps = b.option([]const []const u8, "dump-zir", "Which functions to dump ZIR for before codegen") orelse &[0][]const u8{};
224223
225224 const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git.");
226225 const version = if (opt_version_string) |version| version else v: {
......@@ -277,7 +276,6 @@ pub fn build(b: *Builder) !void {
277276 exe.addBuildOption(std.SemanticVersion, "semver", semver);
278277
279278 exe.addBuildOption([]const []const u8, "log_scopes", log_scopes);
280 exe.addBuildOption([]const []const u8, "zir_dumps", zir_dumps);
281279 exe.addBuildOption(bool, "enable_tracy", tracy != null);
282280 exe.addBuildOption(bool, "is_stage1", is_stage1);
283281 if (tracy) |tracy_path| {
src/Compilation.zig+9-5
......@@ -1459,10 +1459,10 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
14591459 const module = self.bin_file.options.module.?;
14601460 if (decl.typed_value.most_recent.typed_value.val.castTag(.function)) |payload| {
14611461 const func = payload.data;
1462 switch (func.analysis) {
1462 switch (func.bits.state) {
14631463 .queued => module.analyzeFnBody(decl, func) catch |err| switch (err) {
14641464 error.AnalysisFail => {
1465 assert(func.analysis != .in_progress);
1465 assert(func.bits.state != .in_progress);
14661466 continue;
14671467 },
14681468 error.OutOfMemory => return error.OutOfMemory,
......@@ -1471,12 +1471,16 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
14711471 .sema_failure, .dependency_failure => continue,
14721472 .success => {},
14731473 }
1474 // Here we tack on additional allocations to the Decl's arena. The allocations are
1475 // lifetime annotations in the ZIR.
1474 // Here we tack on additional allocations to the Decl's arena. The allocations
1475 // are lifetime annotations in the ZIR.
14761476 var decl_arena = decl.typed_value.most_recent.arena.?.promote(module.gpa);
14771477 defer decl.typed_value.most_recent.arena.?.* = decl_arena.state;
14781478 log.debug("analyze liveness of {s}\n", .{decl.name});
1479 try liveness.analyze(module.gpa, &decl_arena.allocator, func.analysis.success);
1479 try liveness.analyze(module.gpa, &decl_arena.allocator, func.data.body);
1480
1481 if (self.verbose_ir) {
1482 func.dump(module.*);
1483 }
14801484 }
14811485
14821486 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
src/Module.zig+114-36
......@@ -286,23 +286,40 @@ pub const Decl = struct {
286286/// Extern functions do not have this data structure; they are represented by
287287/// the `Decl` only, with a `Value` tag of `extern_fn`.
288288pub const Fn = struct {
289 /// This memory owned by the Decl's TypedValue.Managed arena allocator.
290 analysis: union(enum) {
289 bits: packed struct {
290 /// Get and set this field via `analysis` and `setAnalysis`.
291 state: Analysis.Tag,
292 /// We carry this state into `Fn` instead of leaving it in the AST so that
293 /// analysis of function calls can happen even on functions whose AST has
294 /// been unloaded from memory.
295 is_inline: bool,
296 unused_bits: u4 = 0,
297 },
298 /// Get and set this data via `analysis` and `setAnalysis`.
299 data: union {
300 none: void,
301 zir: *ZIR,
302 body: Body,
303 },
304 owner_decl: *Decl,
305
306 pub const Analysis = union(Tag) {
291307 queued: *ZIR,
292308 in_progress,
293 /// There will be a corresponding ErrorMsg in Module.failed_decls
294309 sema_failure,
295 /// This Fn might be OK but it depends on another Decl which did not successfully complete
296 /// semantic analysis.
297310 dependency_failure,
298311 success: Body,
299 },
300 owner_decl: *Decl,
301312
302 /// This memory is temporary and points to stack memory for the duration
303 /// of Fn analysis.
304 pub const Analysis = struct {
305 inner_block: Scope.Block,
313 pub const Tag = enum(u3) {
314 queued,
315 in_progress,
316 /// There will be a corresponding ErrorMsg in Module.failed_decls
317 sema_failure,
318 /// This Fn might be OK but it depends on another Decl which did not
319 /// successfully complete semantic analysis.
320 dependency_failure,
321 success,
322 };
306323 };
307324
308325 /// Contains un-analyzed ZIR instructions generated from Zig source AST.
......@@ -311,22 +328,37 @@ pub const Fn = struct {
311328 arena: std.heap.ArenaAllocator.State,
312329 };
313330
314 /// For debugging purposes.
315 pub fn dump(self: *Fn, mod: Module) void {
316 std.debug.print("Module.Function(name={s}) ", .{self.owner_decl.name});
317 switch (self.analysis) {
318 .queued => {
319 std.debug.print("queued\n", .{});
331 pub fn analysis(self: Fn) Analysis {
332 return switch (self.bits.state) {
333 .queued => .{ .queued = self.data.zir },
334 .success => .{ .success = self.data.body },
335 .in_progress => .in_progress,
336 .sema_failure => .sema_failure,
337 .dependency_failure => .dependency_failure,
338 };
339 }
340
341 pub fn setAnalysis(self: *Fn, anal: Analysis) void {
342 switch (anal) {
343 .queued => |zir_ptr| {
344 self.bits.state = .queued;
345 self.data = .{ .zir = zir_ptr };
320346 },
321 .in_progress => {
322 std.debug.print("in_progress\n", .{});
347 .success => |body| {
348 self.bits.state = .success;
349 self.data = .{ .body = body };
323350 },
324 else => {
325 std.debug.print("\n", .{});
326 zir.dumpFn(mod, self);
351 .in_progress, .sema_failure, .dependency_failure => {
352 self.bits.state = anal;
353 self.data = .{ .none = {} };
327354 },
328355 }
329356 }
357
358 /// For debugging purposes.
359 pub fn dump(self: *Fn, mod: Module) void {
360 zir.dumpFn(mod, self);
361 }
330362};
331363
332364pub const Var = struct {
......@@ -773,13 +805,33 @@ pub const Scope = struct {
773805 instructions: ArrayListUnmanaged(*Inst),
774806 /// Points to the arena allocator of DeclAnalysis
775807 arena: *Allocator,
776 label: ?Label = null,
808 label: Label = Label.none,
777809 is_comptime: bool,
778810
779 pub const Label = struct {
780 zir_block: *zir.Inst.Block,
781 results: ArrayListUnmanaged(*Inst),
782 block_inst: *Inst.Block,
811 pub const Label = union(enum) {
812 none,
813 /// This `Block` maps a block ZIR instruction to the corresponding
814 /// TZIR instruction for break instruction analysis.
815 breaking: struct {
816 zir_block: *zir.Inst.Block,
817 merges: Merges,
818 },
819 /// This `Block` indicates that an inline function call is happening
820 /// and return instructions should be analyzed as a break instruction
821 /// to this TZIR block instruction.
822 inlining: struct {
823 /// We use this to count from 0 so that arg instructions know
824 /// which parameter index they are, without having to store
825 /// a parameter index with each arg instruction.
826 param_index: usize,
827 casted_args: []*Inst,
828 merges: Merges,
829 },
830
831 pub const Merges = struct {
832 results: ArrayListUnmanaged(*Inst),
833 block_inst: *Inst.Block,
834 };
783835 };
784836
785837 /// For debugging purposes.
......@@ -1189,8 +1241,21 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
11891241 break :blk fn_zir;
11901242 };
11911243
1244 const is_inline = blk: {
1245 if (fn_proto.getExternExportInlineToken()) |maybe_inline_token| {
1246 if (tree.token_ids[maybe_inline_token] == .Keyword_inline) {
1247 break :blk true;
1248 }
1249 }
1250 break :blk false;
1251 };
1252
11921253 new_func.* = .{
1193 .analysis = .{ .queued = fn_zir },
1254 .bits = .{
1255 .state = .queued,
1256 .is_inline = is_inline,
1257 },
1258 .data = .{ .zir = fn_zir },
11941259 .owner_decl = decl,
11951260 };
11961261 fn_payload.* = .{
......@@ -1199,11 +1264,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
11991264 };
12001265
12011266 var prev_type_has_bits = false;
1267 var prev_is_inline = false;
12021268 var type_changed = true;
12031269
12041270 if (decl.typedValueManaged()) |tvm| {
12051271 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
12061272 type_changed = !tvm.typed_value.ty.eql(fn_type);
1273 if (tvm.typed_value.val.castTag(.function)) |payload| {
1274 const prev_func = payload.data;
1275 prev_is_inline = prev_func.bits.is_inline;
1276 }
12071277
12081278 tvm.deinit(self.gpa);
12091279 }
......@@ -1221,18 +1291,26 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12211291 decl.analysis = .complete;
12221292 decl.generation = self.generation;
12231293
1224 if (fn_type.hasCodeGenBits()) {
1294 if (!is_inline and fn_type.hasCodeGenBits()) {
12251295 // We don't fully codegen the decl until later, but we do need to reserve a global
12261296 // offset table index for it. This allows us to codegen decls out of dependency order,
12271297 // increasing how many computations can be done in parallel.
12281298 try self.comp.bin_file.allocateDeclIndexes(decl);
12291299 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
1230 } else if (prev_type_has_bits) {
1300 } else if (!prev_is_inline and prev_type_has_bits) {
12311301 self.comp.bin_file.freeDecl(decl);
12321302 }
12331303
12341304 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
12351305 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1306 if (is_inline) {
1307 return self.failTok(
1308 &block_scope.base,
1309 maybe_export_token,
1310 "export of inline function",
1311 .{},
1312 );
1313 }
12361314 const export_src = tree.token_locs[maybe_export_token].start;
12371315 const name_loc = tree.token_locs[fn_proto.getNameToken().?];
12381316 const name = tree.tokenSliceLoc(name_loc);
......@@ -1240,7 +1318,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12401318 try self.analyzeExport(&block_scope.base, export_src, name, decl);
12411319 }
12421320 }
1243 return type_changed;
1321 return type_changed or is_inline != prev_is_inline;
12441322 },
12451323 .VarDecl => {
12461324 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node);
......@@ -1824,15 +1902,15 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
18241902 };
18251903 defer inner_block.instructions.deinit(self.gpa);
18261904
1827 const fn_zir = func.analysis.queued;
1905 const fn_zir = func.data.zir;
18281906 defer fn_zir.arena.promote(self.gpa).deinit();
1829 func.analysis = .{ .in_progress = {} };
1907 func.setAnalysis(.in_progress);
18301908 log.debug("set {s} to in_progress\n", .{decl.name});
18311909
18321910 try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body);
18331911
18341912 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
1835 func.analysis = .{ .success = .{ .instructions = instructions } };
1913 func.setAnalysis(.{ .success = .{ .instructions = instructions } });
18361914 log.debug("set {s} to success\n", .{decl.name});
18371915}
18381916
......@@ -2329,7 +2407,7 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn
23292407 self.ensureDeclAnalyzed(decl) catch |err| {
23302408 if (scope.cast(Scope.Block)) |block| {
23312409 if (block.func) |func| {
2332 func.analysis = .dependency_failure;
2410 func.setAnalysis(.dependency_failure);
23332411 } else {
23342412 block.decl.analysis = .dependency_failure;
23352413 }
......@@ -3029,7 +3107,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Com
30293107 .block => {
30303108 const block = scope.cast(Scope.Block).?;
30313109 if (block.func) |func| {
3032 func.analysis = .sema_failure;
3110 func.setAnalysis(.sema_failure);
30333111 } else {
30343112 block.decl.analysis = .sema_failure;
30353113 block.decl.generation = self.generation;
src/codegen.zig+5-5
......@@ -532,7 +532,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
532532 self.code.items.len += 4;
533533
534534 try self.dbgSetPrologueEnd();
535 try self.genBody(self.mod_fn.analysis.success);
535 try self.genBody(self.mod_fn.data.body);
536536
537537 const stack_end = self.max_end_stack;
538538 if (stack_end > math.maxInt(i32))
......@@ -576,7 +576,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
576576 });
577577 } else {
578578 try self.dbgSetPrologueEnd();
579 try self.genBody(self.mod_fn.analysis.success);
579 try self.genBody(self.mod_fn.data.body);
580580 try self.dbgSetEpilogueBegin();
581581 }
582582 },
......@@ -593,7 +593,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
593593
594594 try self.dbgSetPrologueEnd();
595595
596 try self.genBody(self.mod_fn.analysis.success);
596 try self.genBody(self.mod_fn.data.body);
597597
598598 // Backpatch stack offset
599599 const stack_end = self.max_end_stack;
......@@ -638,13 +638,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
638638 writeInt(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32());
639639 } else {
640640 try self.dbgSetPrologueEnd();
641 try self.genBody(self.mod_fn.analysis.success);
641 try self.genBody(self.mod_fn.data.body);
642642 try self.dbgSetEpilogueBegin();
643643 }
644644 },
645645 else => {
646646 try self.dbgSetPrologueEnd();
647 try self.genBody(self.mod_fn.analysis.success);
647 try self.genBody(self.mod_fn.data.body);
648648 try self.dbgSetEpilogueBegin();
649649 },
650650 }
src/codegen/c.zig+1-1
......@@ -275,7 +275,7 @@ pub fn generate(file: *C, module: *Module, decl: *Decl) !void {
275275 try writer.writeAll(" {");
276276
277277 const func: *Module.Fn = func_payload.data;
278 const instructions = func.analysis.success.instructions;
278 const instructions = func.data.body.instructions;
279279 if (instructions.len > 0) {
280280 try writer.writeAll("\n");
281281 for (instructions) |inst| {
src/codegen/wasm.zig+1-1
......@@ -63,7 +63,7 @@ pub fn genCode(buf: *ArrayList(u8), decl: *Decl) !void {
6363 // TODO: check for and handle death of instructions
6464 const tv = decl.typed_value.most_recent.typed_value;
6565 const mod_fn = tv.val.castTag(.function).?.data;
66 for (mod_fn.analysis.success.instructions) |inst| try genInst(buf, decl, inst);
66 for (mod_fn.data.body.instructions) |inst| try genInst(buf, decl, inst);
6767
6868 // Write 'end' opcode
6969 try writer.writeByte(0x0B);
src/config.zig.in-1
......@@ -2,7 +2,6 @@ pub const have_llvm = true;
22pub const version: [:0]const u8 = "@ZIG_VERSION@";
33pub const semver = try @import("std").SemanticVersion.parse(version);
44pub const log_scopes: []const []const u8 = &[_][]const u8{};
5pub const zir_dumps: []const []const u8 = &[_][]const u8{};
65pub const enable_tracy = false;
76pub const is_stage1 = true;
87pub const skip_non_native = false;
src/link/Elf.zig-10
......@@ -2178,16 +2178,6 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
21782178 else => false,
21792179 };
21802180 if (is_fn) {
2181 const zir_dumps = if (std.builtin.is_test) &[0][]const u8{} else build_options.zir_dumps;
2182 if (zir_dumps.len != 0) {
2183 for (zir_dumps) |fn_name| {
2184 if (mem.eql(u8, mem.spanZ(decl.name), fn_name)) {
2185 std.debug.print("\n{s}\n", .{decl.name});
2186 typed_value.val.castTag(.function).?.data.dump(module.*);
2187 }
2188 }
2189 }
2190
21912181 // For functions we need to add a prologue to the debug line program.
21922182 try dbg_line_buffer.ensureCapacity(26);
21932183
src/link/MachO/DebugSymbols.zig-10
......@@ -936,16 +936,6 @@ pub fn initDeclDebugBuffers(
936936 const typed_value = decl.typed_value.most_recent.typed_value;
937937 switch (typed_value.ty.zigTypeTag()) {
938938 .Fn => {
939 const zir_dumps = if (std.builtin.is_test) &[0][]const u8{} else build_options.zir_dumps;
940 if (zir_dumps.len != 0) {
941 for (zir_dumps) |fn_name| {
942 if (mem.eql(u8, mem.spanZ(decl.name), fn_name)) {
943 std.debug.print("\n{}\n", .{decl.name});
944 typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*);
945 }
946 }
947 }
948
949939 // For functions we need to add a prologue to the debug line program.
950940 try dbg_line_buffer.ensureCapacity(26);
951941
src/llvm_backend.zig+1-1
......@@ -294,7 +294,7 @@ pub const LLVMIRModule = struct {
294294 const entry_block = llvm_func.appendBasicBlock("Entry");
295295 self.builder.positionBuilderAtEnd(entry_block);
296296
297 const instructions = func.analysis.success.instructions;
297 const instructions = func.data.body.instructions;
298298 for (instructions) |inst| {
299299 switch (inst.tag) {
300300 .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),
src/zir.zig+249-100
......@@ -793,7 +793,9 @@ pub const Inst = struct {
793793 fn_type: *Inst,
794794 body: Module.Body,
795795 },
796 kw_args: struct {},
796 kw_args: struct {
797 is_inline: bool = false,
798 },
797799 };
798800
799801 pub const FnType = struct {
......@@ -1847,83 +1849,258 @@ pub fn emit(allocator: *Allocator, old_module: *IrModule) !Module {
18471849/// For debugging purposes, prints a function representation to stderr.
18481850pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void {
18491851 const allocator = old_module.gpa;
1850 var ctx: EmitZIR = .{
1852 var ctx: DumpTzir = .{
18511853 .allocator = allocator,
1852 .decls = .{},
18531854 .arena = std.heap.ArenaAllocator.init(allocator),
18541855 .old_module = &old_module,
1855 .next_auto_name = 0,
1856 .names = std.StringArrayHashMap(void).init(allocator),
1857 .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),
1858 .indent = 0,
1859 .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator),
1860 .loop_table = std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop).init(allocator),
1861 .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator),
1862 .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator),
1856 .module_fn = module_fn,
1857 .indent = 2,
1858 .inst_table = DumpTzir.InstTable.init(allocator),
1859 .partial_inst_table = DumpTzir.InstTable.init(allocator),
1860 .const_table = DumpTzir.InstTable.init(allocator),
18631861 };
1864 defer ctx.metadata.deinit();
1865 defer ctx.body_metadata.deinit();
1866 defer ctx.block_table.deinit();
1867 defer ctx.loop_table.deinit();
1868 defer ctx.decls.deinit(allocator);
1869 defer ctx.names.deinit();
1870 defer ctx.primitive_table.deinit();
1862 defer ctx.inst_table.deinit();
1863 defer ctx.partial_inst_table.deinit();
1864 defer ctx.const_table.deinit();
18711865 defer ctx.arena.deinit();
18721866
1873 const fn_ty = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
1874 _ = ctx.emitFn(module_fn, 0, fn_ty) catch |err| {
1875 std.debug.print("unable to dump function: {s}\n", .{@errorName(err)});
1876 return;
1877 };
1878 var module = Module{
1879 .decls = ctx.decls.items,
1880 .arena = ctx.arena,
1881 .metadata = ctx.metadata,
1882 .body_metadata = ctx.body_metadata,
1883 };
1884
1885 module.dump();
1867 switch (module_fn.analysis()) {
1868 .queued => std.debug.print("(queued)", .{}),
1869 .in_progress => std.debug.print("(in_progress)", .{}),
1870 .sema_failure => std.debug.print("(sema_failure)", .{}),
1871 .dependency_failure => std.debug.print("(dependency_failure)", .{}),
1872 .success => |body| {
1873 ctx.dump(body, std.io.getStdErr().writer()) catch @panic("failed to dump TZIR");
1874 },
1875 }
18861876}
18871877
1888/// For debugging purposes, prints a function representation to stderr.
1889pub fn dumpBlock(old_module: IrModule, module_block: *IrModule.Scope.Block) void {
1890 const allocator = old_module.gpa;
1891 var ctx: EmitZIR = .{
1892 .allocator = allocator,
1893 .decls = .{},
1894 .arena = std.heap.ArenaAllocator.init(allocator),
1895 .old_module = &old_module,
1896 .next_auto_name = 0,
1897 .names = std.StringArrayHashMap(void).init(allocator),
1898 .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),
1899 .indent = 0,
1900 .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator),
1901 .loop_table = std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop).init(allocator),
1902 .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator),
1903 .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator),
1904 };
1905 defer ctx.metadata.deinit();
1906 defer ctx.body_metadata.deinit();
1907 defer ctx.block_table.deinit();
1908 defer ctx.loop_table.deinit();
1909 defer ctx.decls.deinit(allocator);
1910 defer ctx.names.deinit();
1911 defer ctx.primitive_table.deinit();
1912 defer ctx.arena.deinit();
1878const DumpTzir = struct {
1879 allocator: *Allocator,
1880 arena: std.heap.ArenaAllocator,
1881 old_module: *const IrModule,
1882 module_fn: *IrModule.Fn,
1883 indent: usize,
1884 inst_table: InstTable,
1885 partial_inst_table: InstTable,
1886 const_table: InstTable,
1887 next_index: usize = 0,
1888 next_partial_index: usize = 0,
1889 next_const_index: usize = 0,
1890
1891 const InstTable = std.AutoArrayHashMap(*ir.Inst, usize);
1892
1893 fn dump(dtz: *DumpTzir, body: ir.Body, writer: std.fs.File.Writer) !void {
1894 // First pass to pre-populate the table so that we can show even invalid references.
1895 // Must iterate the same order we iterate the second time.
1896 // We also look for constants and put them in the const_table.
1897 for (body.instructions) |inst| {
1898 try dtz.inst_table.put(inst, dtz.next_index);
1899 dtz.next_index += 1;
1900 switch (inst.tag) {
1901 .alloc,
1902 .retvoid,
1903 .unreach,
1904 .breakpoint,
1905 .dbg_stmt,
1906 => {},
19131907
1914 _ = ctx.emitBlock(module_block, 0) catch |err| {
1915 std.debug.print("unable to dump function: {}\n", .{err});
1916 return;
1917 };
1918 var module = Module{
1919 .decls = ctx.decls.items,
1920 .arena = ctx.arena,
1921 .metadata = ctx.metadata,
1922 .body_metadata = ctx.body_metadata,
1923 };
1908 .ref,
1909 .ret,
1910 .bitcast,
1911 .not,
1912 .isnonnull,
1913 .isnull,
1914 .iserr,
1915 .ptrtoint,
1916 .floatcast,
1917 .intcast,
1918 .load,
1919 .unwrap_optional,
1920 .wrap_optional,
1921 => {
1922 const un_op = inst.cast(ir.Inst.UnOp).?;
1923 try dtz.findConst(un_op.operand);
1924 },
19241925
1925 module.dump();
1926}
1926 .add,
1927 .sub,
1928 .cmp_lt,
1929 .cmp_lte,
1930 .cmp_eq,
1931 .cmp_gte,
1932 .cmp_gt,
1933 .cmp_neq,
1934 .store,
1935 .booland,
1936 .boolor,
1937 .bitand,
1938 .bitor,
1939 .xor,
1940 => {
1941 const bin_op = inst.cast(ir.Inst.BinOp).?;
1942 try dtz.findConst(bin_op.lhs);
1943 try dtz.findConst(bin_op.rhs);
1944 },
1945
1946 .arg => {},
1947
1948 // TODO fill out this debug printing
1949 .assembly,
1950 .block,
1951 .br,
1952 .brvoid,
1953 .call,
1954 .condbr,
1955 .constant,
1956 .loop,
1957 .varptr,
1958 .switchbr,
1959 => {},
1960 }
1961 }
1962
1963 std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name});
1964
1965 for (dtz.const_table.items()) |entry| {
1966 const constant = entry.key.castTag(.constant).?;
1967 try writer.print(" @{d}: {} = {};\n", .{
1968 entry.value, constant.base.ty, constant.val,
1969 });
1970 }
1971
1972 return dtz.dumpBody(body, writer);
1973 }
1974
1975 fn dumpBody(dtz: *DumpTzir, body: ir.Body, writer: std.fs.File.Writer) !void {
1976 for (body.instructions) |inst| {
1977 const my_index = dtz.next_partial_index;
1978 try dtz.partial_inst_table.put(inst, my_index);
1979 dtz.next_partial_index += 1;
1980
1981 try writer.writeByteNTimes(' ', dtz.indent);
1982 try writer.print("%{d}: {} = {s}(", .{
1983 my_index, inst.ty, @tagName(inst.tag),
1984 });
1985 switch (inst.tag) {
1986 .alloc,
1987 .retvoid,
1988 .unreach,
1989 .breakpoint,
1990 .dbg_stmt,
1991 => try writer.writeAll(")\n"),
1992
1993 .ref,
1994 .ret,
1995 .bitcast,
1996 .not,
1997 .isnonnull,
1998 .isnull,
1999 .iserr,
2000 .ptrtoint,
2001 .floatcast,
2002 .intcast,
2003 .load,
2004 .unwrap_optional,
2005 .wrap_optional,
2006 => {
2007 const un_op = inst.cast(ir.Inst.UnOp).?;
2008 if (dtz.partial_inst_table.get(un_op.operand)) |operand_index| {
2009 try writer.print("%{d})\n", .{operand_index});
2010 } else if (dtz.const_table.get(un_op.operand)) |operand_index| {
2011 try writer.print("@{d})\n", .{operand_index});
2012 } else if (dtz.inst_table.get(un_op.operand)) |operand_index| {
2013 try writer.print("%{d}) // Instruction does not dominate all uses!\n", .{
2014 operand_index,
2015 });
2016 } else {
2017 try writer.writeAll("!BADREF!)\n");
2018 }
2019 },
2020
2021 .add,
2022 .sub,
2023 .cmp_lt,
2024 .cmp_lte,
2025 .cmp_eq,
2026 .cmp_gte,
2027 .cmp_gt,
2028 .cmp_neq,
2029 .store,
2030 .booland,
2031 .boolor,
2032 .bitand,
2033 .bitor,
2034 .xor,
2035 => {
2036 var lhs_kinky: ?usize = null;
2037 var rhs_kinky: ?usize = null;
2038
2039 const bin_op = inst.cast(ir.Inst.BinOp).?;
2040 if (dtz.partial_inst_table.get(bin_op.lhs)) |operand_index| {
2041 try writer.print("%{d}, ", .{operand_index});
2042 } else if (dtz.const_table.get(bin_op.lhs)) |operand_index| {
2043 try writer.print("@{d}, ", .{operand_index});
2044 } else if (dtz.inst_table.get(bin_op.lhs)) |operand_index| {
2045 lhs_kinky = operand_index;
2046 try writer.print("%{d}, ", .{operand_index});
2047 } else {
2048 try writer.writeAll("!BADREF!, ");
2049 }
2050 if (dtz.partial_inst_table.get(bin_op.rhs)) |operand_index| {
2051 try writer.print("%{d}", .{operand_index});
2052 } else if (dtz.const_table.get(bin_op.rhs)) |operand_index| {
2053 try writer.print("@{d}", .{operand_index});
2054 } else if (dtz.inst_table.get(bin_op.rhs)) |operand_index| {
2055 rhs_kinky = operand_index;
2056 try writer.print("%{d}", .{operand_index});
2057 } else {
2058 try writer.writeAll("!BADREF!");
2059 }
2060 if (lhs_kinky != null or rhs_kinky != null) {
2061 try writer.writeAll(") // Instruction does not dominate all uses!");
2062 if (lhs_kinky) |lhs| {
2063 try writer.print(" %{d}", .{lhs});
2064 }
2065 if (rhs_kinky) |rhs| {
2066 try writer.print(" %{d}", .{rhs});
2067 }
2068 try writer.writeAll("\n");
2069 } else {
2070 try writer.writeAll(")\n");
2071 }
2072 },
2073
2074 .arg => {
2075 const arg = inst.castTag(.arg).?;
2076 try writer.print("{s})\n", .{arg.name});
2077 },
2078
2079 // TODO fill out this debug printing
2080 .assembly,
2081 .block,
2082 .br,
2083 .brvoid,
2084 .call,
2085 .condbr,
2086 .constant,
2087 .loop,
2088 .varptr,
2089 .switchbr,
2090 => {
2091 try writer.writeAll("!TODO!)\n");
2092 },
2093 }
2094 }
2095 }
2096
2097 fn findConst(dtz: *DumpTzir, operand: *ir.Inst) !void {
2098 if (operand.tag == .constant) {
2099 try dtz.const_table.put(operand, dtz.next_const_index);
2100 dtz.next_const_index += 1;
2101 }
2102 }
2103};
19272104
19282105const EmitZIR = struct {
19292106 allocator: *Allocator,
......@@ -2105,36 +2282,6 @@ const EmitZIR = struct {
21052282 return &declref_inst.base;
21062283 }
21072284
2108 fn emitBlock(self: *EmitZIR, module_block: *IrModule.Scope.Block, src: usize) Allocator.Error!*Decl {
2109 var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator);
2110 defer inst_table.deinit();
2111
2112 var instructions = std.ArrayList(*Inst).init(self.allocator);
2113 defer instructions.deinit();
2114
2115 const body: ir.Body = .{ .instructions = module_block.instructions.items };
2116 try self.emitBody(body, &inst_table, &instructions);
2117
2118 const fn_type = try self.emitType(src, Type.initTag(.void));
2119
2120 const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len);
2121 mem.copy(*Inst, arena_instrs, instructions.items);
2122
2123 const fn_inst = try self.arena.allocator.create(Inst.Fn);
2124 fn_inst.* = .{
2125 .base = .{
2126 .src = src,
2127 .tag = Inst.Fn.base_tag,
2128 },
2129 .positionals = .{
2130 .fn_type = fn_type.inst,
2131 .body = .{ .instructions = arena_instrs },
2132 },
2133 .kw_args = .{},
2134 };
2135 return self.emitUnnamedDecl(&fn_inst.base);
2136 }
2137
21382285 fn emitFn(self: *EmitZIR, module_fn: *IrModule.Fn, src: usize, ty: Type) Allocator.Error!*Decl {
21392286 var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator);
21402287 defer inst_table.deinit();
......@@ -2142,7 +2289,7 @@ const EmitZIR = struct {
21422289 var instructions = std.ArrayList(*Inst).init(self.allocator);
21432290 defer instructions.deinit();
21442291
2145 switch (module_fn.analysis) {
2292 switch (module_fn.analysis()) {
21462293 .queued => unreachable,
21472294 .in_progress => unreachable,
21482295 .success => |body| {
......@@ -2224,7 +2371,9 @@ const EmitZIR = struct {
22242371 .fn_type = fn_type.inst,
22252372 .body = .{ .instructions = arena_instrs },
22262373 },
2227 .kw_args = .{},
2374 .kw_args = .{
2375 .is_inline = module_fn.bits.is_inline,
2376 },
22282377 };
22292378 return self.emitUnnamedDecl(&fn_inst.base);
22302379 }
src/zir_sema.zig+163-76
......@@ -577,7 +577,15 @@ fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) In
577577}
578578
579579fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst {
580 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
580 const b = try mod.requireFunctionBlock(scope, inst.base.src);
581 switch (b.label) {
582 .none, .breaking => {},
583 .inlining => |*inlining| {
584 const param_index = inlining.param_index;
585 inlining.param_index += 1;
586 return inlining.casted_args[param_index];
587 },
588 }
581589 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
582590 const param_index = b.instructions.items.len;
583591 const param_count = fn_ty.fnParamLen();
......@@ -636,7 +644,7 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c
636644 .decl = parent_block.decl,
637645 .instructions = .{},
638646 .arena = parent_block.arena,
639 .label = null,
647 .label = .none,
640648 .is_comptime = parent_block.is_comptime or is_comptime,
641649 };
642650 defer child_block.instructions.deinit(mod.gpa);
......@@ -674,41 +682,56 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt
674682 .decl = parent_block.decl,
675683 .instructions = .{},
676684 .arena = parent_block.arena,
677 // TODO @as here is working around a stage1 miscompilation bug :(
678 .label = @as(?Scope.Block.Label, Scope.Block.Label{
679 .zir_block = inst,
680 .results = .{},
681 .block_inst = block_inst,
682 }),
685 .label = Scope.Block.Label{
686 .breaking = .{
687 .zir_block = inst,
688 .merges = .{
689 .results = .{},
690 .block_inst = block_inst,
691 },
692 },
693 },
683694 .is_comptime = is_comptime or parent_block.is_comptime,
684695 };
685 const label = &child_block.label.?;
696 const merges = &child_block.label.breaking.merges;
686697
687698 defer child_block.instructions.deinit(mod.gpa);
688 defer label.results.deinit(mod.gpa);
699 defer merges.results.deinit(mod.gpa);
689700
690701 try analyzeBody(mod, &child_block.base, inst.positionals.body);
691702
703 return analyzeBlockBody(mod, scope, &child_block, merges);
704}
705
706fn analyzeBlockBody(
707 mod: *Module,
708 scope: *Scope,
709 child_block: *Scope.Block,
710 merges: *Scope.Block.Label.Merges,
711) InnerError!*Inst {
712 const parent_block = scope.cast(Scope.Block).?;
713
692714 // Blocks must terminate with noreturn instruction.
693715 assert(child_block.instructions.items.len != 0);
694716 assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());
695717
696 if (label.results.items.len == 0) {
697 // No need for a block instruction. We can put the new instructions directly into the parent block.
718 if (merges.results.items.len == 0) {
719 // No need for a block instruction. We can put the new instructions
720 // directly into the parent block.
698721 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items);
699722 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
700723 return copied_instructions[copied_instructions.len - 1];
701724 }
702 if (label.results.items.len == 1) {
725 if (merges.results.items.len == 1) {
703726 const last_inst_index = child_block.instructions.items.len - 1;
704727 const last_inst = child_block.instructions.items[last_inst_index];
705728 if (last_inst.breakBlock()) |br_block| {
706 if (br_block == block_inst) {
729 if (br_block == merges.block_inst) {
707730 // No need for a block instruction. We can put the new instructions directly into the parent block.
708731 // Here we omit the break instruction.
709732 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]);
710733 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
711 return label.results.items[0];
734 return merges.results.items[0];
712735 }
713736 }
714737 }
......@@ -717,10 +740,10 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt
717740
718741 // Need to set the type and emit the Block instruction. This allows machine code generation
719742 // to emit a jump instruction to after the block when it encounters the break.
720 try parent_block.instructions.append(mod.gpa, &block_inst.base);
721 block_inst.base.ty = try mod.resolvePeerTypes(scope, label.results.items);
722 block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
723 return &block_inst.base;
743 try parent_block.instructions.append(mod.gpa, &merges.block_inst.base);
744 merges.block_inst.base.ty = try mod.resolvePeerTypes(scope, merges.results.items);
745 merges.block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
746 return &merges.block_inst.base;
724747}
725748
726749fn analyzeInstBreakpoint(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
......@@ -829,14 +852,32 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
829852 const ret_type = func.ty.fnReturnType();
830853
831854 const b = try mod.requireFunctionBlock(scope, inst.base.src);
832 if (b.is_comptime) {
833 const fn_val = try mod.resolveConstValue(scope, func);
834 const module_fn = switch (fn_val.tag()) {
835 .function => fn_val.castTag(.function).?.data,
836 .extern_fn => return mod.fail(scope, inst.base.src, "comptime call of extern function", .{}),
855 const is_comptime_call = b.is_comptime or inst.kw_args.modifier == .compile_time;
856 const is_inline_call = is_comptime_call or inst.kw_args.modifier == .always_inline or blk: {
857 // This logic will get simplified by
858 // https://github.com/ziglang/zig/issues/6429
859 if (try mod.resolveDefinedValue(scope, func)) |func_val| {
860 const module_fn = switch (func_val.tag()) {
861 .function => func_val.castTag(.function).?.data,
862 else => break :blk false,
863 };
864 break :blk module_fn.bits.is_inline;
865 }
866 break :blk false;
867 };
868 if (is_inline_call) {
869 const func_val = try mod.resolveConstValue(scope, func);
870 const module_fn = switch (func_val.tag()) {
871 .function => func_val.castTag(.function).?.data,
872 .extern_fn => return mod.fail(scope, inst.base.src, "{s} call of extern function", .{
873 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
874 }),
837875 else => unreachable,
838876 };
839877 const callee_decl = module_fn.owner_decl;
878 // TODO: De-duplicate this with the code in Module.zig that generates
879 // ZIR for the same function and re-use the same ZIR for runtime function
880 // generation and for inline/comptime calls.
840881 const callee_file_scope = callee_decl.getFileScope();
841882 const tree = mod.getAstTree(callee_file_scope) catch |err| switch (err) {
842883 error.OutOfMemory => return error.OutOfMemory,
......@@ -859,23 +900,31 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
859900 };
860901 defer gen_scope.instructions.deinit(mod.gpa);
861902
862 // Add a const instruction for each parameter.
903 // We need an instruction for each parameter, and they must be first in the body.
904 try gen_scope.instructions.resize(mod.gpa, fn_proto.params_len);
863905 var params_scope = &gen_scope.base;
864906 for (fn_proto.params()) |param, i| {
865907 const name_token = param.name_token.?;
866908 const src = tree.token_locs[name_token].start;
867909 const param_name = try mod.identifierTokenString(scope, name_token);
868 const arg_val = try mod.resolveConstValue(scope, casted_args[i]);
869 const arg = try astgen.addZIRInstConst(mod, params_scope, src, .{
870 .ty = casted_args[i].ty,
871 .val = arg_val,
872 });
910 const arg = try call_arena.allocator.create(zir.Inst.Arg);
911 arg.* = .{
912 .base = .{
913 .tag = .arg,
914 .src = src,
915 },
916 .positionals = .{
917 .name = param_name,
918 },
919 .kw_args = .{},
920 };
921 gen_scope.instructions.items[i] = &arg.base;
873922 const sub_scope = try call_arena.allocator.create(Scope.LocalVal);
874923 sub_scope.* = .{
875924 .parent = params_scope,
876925 .gen_zir = &gen_scope,
877926 .name = param_name,
878 .inst = arg,
927 .inst = &arg.base,
879928 };
880929 params_scope = &sub_scope.base;
881930 }
......@@ -896,42 +945,52 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
896945 zir.dumpZir(mod.gpa, "fn_body_callee", callee_decl.name, gen_scope.instructions.items) catch {};
897946 }
898947
899 // Analyze the ZIR.
900 var inner_block: Scope.Block = .{
948 // Analyze the ZIR. The same ZIR gets analyzed into a runtime function
949 // or an inlined call depending on what union tag the `label` field is
950 // set to in the `Scope.Block`.
951 // This block instruction will be used to capture the return value from the
952 // inlined function.
953 const block_inst = try scope.arena().create(Inst.Block);
954 block_inst.* = .{
955 .base = .{
956 .tag = Inst.Block.base_tag,
957 .ty = ret_type,
958 .src = inst.base.src,
959 },
960 .body = undefined,
961 };
962 var child_block: Scope.Block = .{
901963 .parent = null,
902964 .func = module_fn,
903 .decl = callee_decl,
965 // Note that we pass the caller's Decl, not the callee. This causes
966 // compile errors to be attached (correctly) to the caller's Decl.
967 .decl = scope.decl().?,
904968 .instructions = .{},
905 .arena = &call_arena.allocator,
906 .is_comptime = true,
969 .arena = scope.arena(),
970 .label = Scope.Block.Label{
971 .inlining = .{
972 .param_index = 0,
973 .casted_args = casted_args,
974 .merges = .{
975 .results = .{},
976 .block_inst = block_inst,
977 },
978 },
979 },
980 .is_comptime = is_comptime_call,
907981 };
908 defer inner_block.instructions.deinit(mod.gpa);
982 const merges = &child_block.label.inlining.merges;
983
984 defer child_block.instructions.deinit(mod.gpa);
985 defer merges.results.deinit(mod.gpa);
909986
910 // TODO make sure compile errors that happen from this analyzeBody are reported correctly
911 // and attach to the caller Decl not the callee.
912 try analyzeBody(mod, &inner_block.base, .{
987 // This will have return instructions analyzed as break instructions to
988 // the block_inst above.
989 try analyzeBody(mod, &child_block.base, .{
913990 .instructions = gen_scope.instructions.items,
914991 });
915992
916 if (mod.comp.verbose_ir) {
917 inner_block.dump(mod.*);
918 }
919
920 assert(inner_block.instructions.items.len == 1);
921 const only_inst = inner_block.instructions.items[0];
922 switch (only_inst.tag) {
923 .ret => {
924 const ret_inst = only_inst.castTag(.ret).?;
925 const operand = ret_inst.operand;
926 const callee_arena = scope.arena();
927 return mod.constInst(scope, inst.base.src, .{
928 .ty = try operand.ty.copy(callee_arena),
929 .val = try operand.value().?.copy(callee_arena),
930 });
931 },
932 .retvoid => return mod.constVoid(scope, inst.base.src),
933 else => unreachable,
934 }
993 return analyzeBlockBody(mod, scope, &child_block, merges);
935994 }
936995
937996 return mod.addCall(b, inst.base.src, ret_type, func, casted_args);
......@@ -954,7 +1013,11 @@ fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!
9541013 };
9551014 const new_func = try scope.arena().create(Module.Fn);
9561015 new_func.* = .{
957 .analysis = .{ .queued = fn_zir },
1016 .bits = .{
1017 .state = .queued,
1018 .is_inline = fn_inst.kw_args.is_inline,
1019 },
1020 .data = .{ .zir = fn_zir },
9581021 .owner_decl = scope.decl().?,
9591022 };
9601023 return mod.constInst(scope, fn_inst.base.src, .{
......@@ -2020,21 +2083,41 @@ fn analyzeInstUnreachable(
20202083fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
20212084 const operand = try resolveInst(mod, scope, inst.positionals.operand);
20222085 const b = try mod.requireFunctionBlock(scope, inst.base.src);
2023 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand);
2086
2087 switch (b.label) {
2088 .inlining => |*inlining| {
2089 // We are inlining a function call; rewrite the `ret` as a `break`.
2090 try inlining.merges.results.append(mod.gpa, operand);
2091 return mod.addBr(b, inst.base.src, inlining.merges.block_inst, operand);
2092 },
2093 .none, .breaking => {
2094 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand);
2095 },
2096 }
20242097}
20252098
20262099fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
20272100 const b = try mod.requireFunctionBlock(scope, inst.base.src);
2028 if (b.func) |func| {
2029 // Need to emit a compile error if returning void is not allowed.
2030 const void_inst = try mod.constVoid(scope, inst.base.src);
2031 const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty;
2032 const casted_void = try mod.coerce(scope, fn_ty.fnReturnType(), void_inst);
2033 if (casted_void.ty.zigTypeTag() != .Void) {
2034 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, casted_void);
2035 }
2101 switch (b.label) {
2102 .inlining => |*inlining| {
2103 // We are inlining a function call; rewrite the `retvoid` as a `breakvoid`.
2104 const void_inst = try mod.constVoid(scope, inst.base.src);
2105 try inlining.merges.results.append(mod.gpa, void_inst);
2106 return mod.addBr(b, inst.base.src, inlining.merges.block_inst, void_inst);
2107 },
2108 .none, .breaking => {
2109 if (b.func) |func| {
2110 // Need to emit a compile error if returning void is not allowed.
2111 const void_inst = try mod.constVoid(scope, inst.base.src);
2112 const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty;
2113 const casted_void = try mod.coerce(scope, fn_ty.fnReturnType(), void_inst);
2114 if (casted_void.ty.zigTypeTag() != .Void) {
2115 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, casted_void);
2116 }
2117 }
2118 return mod.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);
2119 },
20362120 }
2037 return mod.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);
20382121}
20392122
20402123fn floatOpAllowed(tag: zir.Inst.Tag) bool {
......@@ -2054,12 +2137,16 @@ fn analyzeBreak(
20542137) InnerError!*Inst {
20552138 var opt_block = scope.cast(Scope.Block);
20562139 while (opt_block) |block| {
2057 if (block.label) |*label| {
2058 if (label.zir_block == zir_block) {
2059 try label.results.append(mod.gpa, operand);
2060 const b = try mod.requireRuntimeBlock(scope, src);
2061 return mod.addBr(b, src, label.block_inst, operand);
2062 }
2140 switch (block.label) {
2141 .none => {},
2142 .breaking => |*label| {
2143 if (label.zir_block == zir_block) {
2144 try label.merges.results.append(mod.gpa, operand);
2145 const b = try mod.requireFunctionBlock(scope, src);
2146 return mod.addBr(b, src, label.merges.block_inst, operand);
2147 }
2148 },
2149 .inlining => unreachable, // Invalid `break` ZIR inside inline function call.
20632150 }
20642151 opt_block = block.parent;
20652152 } else unreachable;
test/stage2/zir.zig+6-6
......@@ -30,7 +30,7 @@ pub fn addCases(ctx: *TestContext) !void {
3030 \\@unnamed$7 = fntype([], @void, cc=C)
3131 \\@entry = fn(@unnamed$7, {
3232 \\ %0 = returnvoid() ; deaths=0b1000000000000000
33 \\})
33 \\}, is_inline=0)
3434 \\
3535 );
3636 ctx.transformZIR("elemptr, add, cmp, condbr, return, breakpoint", linux_x64,
......@@ -78,7 +78,7 @@ pub fn addCases(ctx: *TestContext) !void {
7878 \\@unnamed$6 = fntype([], @void, cc=C)
7979 \\@entry = fn(@unnamed$6, {
8080 \\ %0 = returnvoid() ; deaths=0b1000000000000000
81 \\})
81 \\}, is_inline=0)
8282 \\@entry__anon_1 = str("2\x08\x01\n")
8383 \\@9 = declref("9__anon_0")
8484 \\@9__anon_0 = str("entry")
......@@ -123,17 +123,17 @@ pub fn addCases(ctx: *TestContext) !void {
123123 \\@entry = fn(@unnamed$7, {
124124 \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001
125125 \\ %1 = returnvoid() ; deaths=0b1000000000000000
126 \\})
126 \\}, is_inline=0)
127127 \\@unnamed$9 = fntype([], @void, cc=C)
128128 \\@a = fn(@unnamed$9, {
129129 \\ %0 = call(@b, [], modifier=auto) ; deaths=0b1000000000000001
130130 \\ %1 = returnvoid() ; deaths=0b1000000000000000
131 \\})
131 \\}, is_inline=0)
132132 \\@unnamed$11 = fntype([], @void, cc=C)
133133 \\@b = fn(@unnamed$11, {
134134 \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001
135135 \\ %1 = returnvoid() ; deaths=0b1000000000000000
136 \\})
136 \\}, is_inline=0)
137137 \\
138138 );
139139 // Now we introduce a compile error
......@@ -203,7 +203,7 @@ pub fn addCases(ctx: *TestContext) !void {
203203 \\@unnamed$7 = fntype([], @void, cc=C)
204204 \\@entry = fn(@unnamed$7, {
205205 \\ %0 = returnvoid() ; deaths=0b1000000000000000
206 \\})
206 \\}, is_inline=0)
207207 \\
208208 );
209209 }