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 {...@@ -220,7 +220,6 @@ pub fn build(b: *Builder) !void {
220 }220 }
221221
222 const log_scopes = b.option([]const []const u8, "log", "Which log scopes to enable") orelse &[0][]const u8{};222 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
225 const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git.");224 const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git.");
226 const version = if (opt_version_string) |version| version else v: {225 const version = if (opt_version_string) |version| version else v: {
...@@ -277,7 +276,6 @@ pub fn build(b: *Builder) !void {...@@ -277,7 +276,6 @@ pub fn build(b: *Builder) !void {
277 exe.addBuildOption(std.SemanticVersion, "semver", semver);276 exe.addBuildOption(std.SemanticVersion, "semver", semver);
278277
279 exe.addBuildOption([]const []const u8, "log_scopes", log_scopes);278 exe.addBuildOption([]const []const u8, "log_scopes", log_scopes);
280 exe.addBuildOption([]const []const u8, "zir_dumps", zir_dumps);
281 exe.addBuildOption(bool, "enable_tracy", tracy != null);279 exe.addBuildOption(bool, "enable_tracy", tracy != null);
282 exe.addBuildOption(bool, "is_stage1", is_stage1);280 exe.addBuildOption(bool, "is_stage1", is_stage1);
283 if (tracy) |tracy_path| {281 if (tracy) |tracy_path| {
src/Compilation.zig+9-5
...@@ -1459,10 +1459,10 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -1459,10 +1459,10 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
1459 const module = self.bin_file.options.module.?;1459 const module = self.bin_file.options.module.?;
1460 if (decl.typed_value.most_recent.typed_value.val.castTag(.function)) |payload| {1460 if (decl.typed_value.most_recent.typed_value.val.castTag(.function)) |payload| {
1461 const func = payload.data;1461 const func = payload.data;
1462 switch (func.analysis) {1462 switch (func.bits.state) {
1463 .queued => module.analyzeFnBody(decl, func) catch |err| switch (err) {1463 .queued => module.analyzeFnBody(decl, func) catch |err| switch (err) {
1464 error.AnalysisFail => {1464 error.AnalysisFail => {
1465 assert(func.analysis != .in_progress);1465 assert(func.bits.state != .in_progress);
1466 continue;1466 continue;
1467 },1467 },
1468 error.OutOfMemory => return error.OutOfMemory,1468 error.OutOfMemory => return error.OutOfMemory,
...@@ -1471,12 +1471,16 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -1471,12 +1471,16 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
1471 .sema_failure, .dependency_failure => continue,1471 .sema_failure, .dependency_failure => continue,
1472 .success => {},1472 .success => {},
1473 }1473 }
1474 // Here we tack on additional allocations to the Decl's arena. The allocations are1474 // Here we tack on additional allocations to the Decl's arena. The allocations
1475 // lifetime annotations in the ZIR.1475 // are lifetime annotations in the ZIR.
1476 var decl_arena = decl.typed_value.most_recent.arena.?.promote(module.gpa);1476 var decl_arena = decl.typed_value.most_recent.arena.?.promote(module.gpa);
1477 defer decl.typed_value.most_recent.arena.?.* = decl_arena.state;1477 defer decl.typed_value.most_recent.arena.?.* = decl_arena.state;
1478 log.debug("analyze liveness of {s}\n", .{decl.name});1478 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 }
1480 }1484 }
14811485
1482 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());1486 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
src/Module.zig+114-36
...@@ -286,23 +286,40 @@ pub const Decl = struct {...@@ -286,23 +286,40 @@ pub const Decl = struct {
286/// Extern functions do not have this data structure; they are represented by286/// Extern functions do not have this data structure; they are represented by
287/// the `Decl` only, with a `Value` tag of `extern_fn`.287/// the `Decl` only, with a `Value` tag of `extern_fn`.
288pub const Fn = struct {288pub const Fn = struct {
289 /// This memory owned by the Decl's TypedValue.Managed arena allocator.289 bits: packed struct {
290 analysis: union(enum) {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) {
291 queued: *ZIR,307 queued: *ZIR,
292 in_progress,308 in_progress,
293 /// There will be a corresponding ErrorMsg in Module.failed_decls
294 sema_failure,309 sema_failure,
295 /// This Fn might be OK but it depends on another Decl which did not successfully complete
296 /// semantic analysis.
297 dependency_failure,310 dependency_failure,
298 success: Body,311 success: Body,
299 },
300 owner_decl: *Decl,
301312
302 /// This memory is temporary and points to stack memory for the duration313 pub const Tag = enum(u3) {
303 /// of Fn analysis.314 queued,
304 pub const Analysis = struct {315 in_progress,
305 inner_block: Scope.Block,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 };
306 };323 };
307324
308 /// Contains un-analyzed ZIR instructions generated from Zig source AST.325 /// Contains un-analyzed ZIR instructions generated from Zig source AST.
...@@ -311,22 +328,37 @@ pub const Fn = struct {...@@ -311,22 +328,37 @@ pub const Fn = struct {
311 arena: std.heap.ArenaAllocator.State,328 arena: std.heap.ArenaAllocator.State,
312 };329 };
313330
314 /// For debugging purposes.331 pub fn analysis(self: Fn) Analysis {
315 pub fn dump(self: *Fn, mod: Module) void {332 return switch (self.bits.state) {
316 std.debug.print("Module.Function(name={s}) ", .{self.owner_decl.name});333 .queued => .{ .queued = self.data.zir },
317 switch (self.analysis) {334 .success => .{ .success = self.data.body },
318 .queued => {335 .in_progress => .in_progress,
319 std.debug.print("queued\n", .{});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 };
320 },346 },
321 .in_progress => {347 .success => |body| {
322 std.debug.print("in_progress\n", .{});348 self.bits.state = .success;
349 self.data = .{ .body = body };
323 },350 },
324 else => {351 .in_progress, .sema_failure, .dependency_failure => {
325 std.debug.print("\n", .{});352 self.bits.state = anal;
326 zir.dumpFn(mod, self);353 self.data = .{ .none = {} };
327 },354 },
328 }355 }
329 }356 }
357
358 /// For debugging purposes.
359 pub fn dump(self: *Fn, mod: Module) void {
360 zir.dumpFn(mod, self);
361 }
330};362};
331363
332pub const Var = struct {364pub const Var = struct {
...@@ -773,13 +805,33 @@ pub const Scope = struct {...@@ -773,13 +805,33 @@ pub const Scope = struct {
773 instructions: ArrayListUnmanaged(*Inst),805 instructions: ArrayListUnmanaged(*Inst),
774 /// Points to the arena allocator of DeclAnalysis806 /// Points to the arena allocator of DeclAnalysis
775 arena: *Allocator,807 arena: *Allocator,
776 label: ?Label = null,808 label: Label = Label.none,
777 is_comptime: bool,809 is_comptime: bool,
778810
779 pub const Label = struct {811 pub const Label = union(enum) {
780 zir_block: *zir.Inst.Block,812 none,
781 results: ArrayListUnmanaged(*Inst),813 /// This `Block` maps a block ZIR instruction to the corresponding
782 block_inst: *Inst.Block,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 };
783 };835 };
784836
785 /// For debugging purposes.837 /// For debugging purposes.
...@@ -1189,8 +1241,21 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1189,8 +1241,21 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1189 break :blk fn_zir;1241 break :blk fn_zir;
1190 };1242 };
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
1192 new_func.* = .{1253 new_func.* = .{
1193 .analysis = .{ .queued = fn_zir },1254 .bits = .{
1255 .state = .queued,
1256 .is_inline = is_inline,
1257 },
1258 .data = .{ .zir = fn_zir },
1194 .owner_decl = decl,1259 .owner_decl = decl,
1195 };1260 };
1196 fn_payload.* = .{1261 fn_payload.* = .{
...@@ -1199,11 +1264,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1199,11 +1264,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1199 };1264 };
12001265
1201 var prev_type_has_bits = false;1266 var prev_type_has_bits = false;
1267 var prev_is_inline = false;
1202 var type_changed = true;1268 var type_changed = true;
12031269
1204 if (decl.typedValueManaged()) |tvm| {1270 if (decl.typedValueManaged()) |tvm| {
1205 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();1271 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
1206 type_changed = !tvm.typed_value.ty.eql(fn_type);1272 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
1208 tvm.deinit(self.gpa);1278 tvm.deinit(self.gpa);
1209 }1279 }
...@@ -1221,18 +1291,26 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1221,18 +1291,26 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1221 decl.analysis = .complete;1291 decl.analysis = .complete;
1222 decl.generation = self.generation;1292 decl.generation = self.generation;
12231293
1224 if (fn_type.hasCodeGenBits()) {1294 if (!is_inline and fn_type.hasCodeGenBits()) {
1225 // We don't fully codegen the decl until later, but we do need to reserve a global1295 // We don't fully codegen the decl until later, but we do need to reserve a global
1226 // offset table index for it. This allows us to codegen decls out of dependency order,1296 // offset table index for it. This allows us to codegen decls out of dependency order,
1227 // increasing how many computations can be done in parallel.1297 // increasing how many computations can be done in parallel.
1228 try self.comp.bin_file.allocateDeclIndexes(decl);1298 try self.comp.bin_file.allocateDeclIndexes(decl);
1229 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });1299 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) {
1231 self.comp.bin_file.freeDecl(decl);1301 self.comp.bin_file.freeDecl(decl);
1232 }1302 }
12331303
1234 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {1304 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
1235 if (tree.token_ids[maybe_export_token] == .Keyword_export) {1305 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 }
1236 const export_src = tree.token_locs[maybe_export_token].start;1314 const export_src = tree.token_locs[maybe_export_token].start;
1237 const name_loc = tree.token_locs[fn_proto.getNameToken().?];1315 const name_loc = tree.token_locs[fn_proto.getNameToken().?];
1238 const name = tree.tokenSliceLoc(name_loc);1316 const name = tree.tokenSliceLoc(name_loc);
...@@ -1240,7 +1318,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1240,7 +1318,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1240 try self.analyzeExport(&block_scope.base, export_src, name, decl);1318 try self.analyzeExport(&block_scope.base, export_src, name, decl);
1241 }1319 }
1242 }1320 }
1243 return type_changed;1321 return type_changed or is_inline != prev_is_inline;
1244 },1322 },
1245 .VarDecl => {1323 .VarDecl => {
1246 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node);1324 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node);
...@@ -1824,15 +1902,15 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {...@@ -1824,15 +1902,15 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
1824 };1902 };
1825 defer inner_block.instructions.deinit(self.gpa);1903 defer inner_block.instructions.deinit(self.gpa);
18261904
1827 const fn_zir = func.analysis.queued;1905 const fn_zir = func.data.zir;
1828 defer fn_zir.arena.promote(self.gpa).deinit();1906 defer fn_zir.arena.promote(self.gpa).deinit();
1829 func.analysis = .{ .in_progress = {} };1907 func.setAnalysis(.in_progress);
1830 log.debug("set {s} to in_progress\n", .{decl.name});1908 log.debug("set {s} to in_progress\n", .{decl.name});
18311909
1832 try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body);1910 try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body);
18331911
1834 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);1912 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
1835 func.analysis = .{ .success = .{ .instructions = instructions } };1913 func.setAnalysis(.{ .success = .{ .instructions = instructions } });
1836 log.debug("set {s} to success\n", .{decl.name});1914 log.debug("set {s} to success\n", .{decl.name});
1837}1915}
18381916
...@@ -2329,7 +2407,7 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn...@@ -2329,7 +2407,7 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn
2329 self.ensureDeclAnalyzed(decl) catch |err| {2407 self.ensureDeclAnalyzed(decl) catch |err| {
2330 if (scope.cast(Scope.Block)) |block| {2408 if (scope.cast(Scope.Block)) |block| {
2331 if (block.func) |func| {2409 if (block.func) |func| {
2332 func.analysis = .dependency_failure;2410 func.setAnalysis(.dependency_failure);
2333 } else {2411 } else {
2334 block.decl.analysis = .dependency_failure;2412 block.decl.analysis = .dependency_failure;
2335 }2413 }
...@@ -3029,7 +3107,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Com...@@ -3029,7 +3107,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Com
3029 .block => {3107 .block => {
3030 const block = scope.cast(Scope.Block).?;3108 const block = scope.cast(Scope.Block).?;
3031 if (block.func) |func| {3109 if (block.func) |func| {
3032 func.analysis = .sema_failure;3110 func.setAnalysis(.sema_failure);
3033 } else {3111 } else {
3034 block.decl.analysis = .sema_failure;3112 block.decl.analysis = .sema_failure;
3035 block.decl.generation = self.generation;3113 block.decl.generation = self.generation;
src/codegen.zig+5-5
...@@ -532,7 +532,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -532,7 +532,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
532 self.code.items.len += 4;532 self.code.items.len += 4;
533533
534 try self.dbgSetPrologueEnd();534 try self.dbgSetPrologueEnd();
535 try self.genBody(self.mod_fn.analysis.success);535 try self.genBody(self.mod_fn.data.body);
536536
537 const stack_end = self.max_end_stack;537 const stack_end = self.max_end_stack;
538 if (stack_end > math.maxInt(i32))538 if (stack_end > math.maxInt(i32))
...@@ -576,7 +576,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -576,7 +576,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
576 });576 });
577 } else {577 } else {
578 try self.dbgSetPrologueEnd();578 try self.dbgSetPrologueEnd();
579 try self.genBody(self.mod_fn.analysis.success);579 try self.genBody(self.mod_fn.data.body);
580 try self.dbgSetEpilogueBegin();580 try self.dbgSetEpilogueBegin();
581 }581 }
582 },582 },
...@@ -593,7 +593,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -593,7 +593,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
593593
594 try self.dbgSetPrologueEnd();594 try self.dbgSetPrologueEnd();
595595
596 try self.genBody(self.mod_fn.analysis.success);596 try self.genBody(self.mod_fn.data.body);
597597
598 // Backpatch stack offset598 // Backpatch stack offset
599 const stack_end = self.max_end_stack;599 const stack_end = self.max_end_stack;
...@@ -638,13 +638,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -638,13 +638,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
638 writeInt(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32());638 writeInt(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32());
639 } else {639 } else {
640 try self.dbgSetPrologueEnd();640 try self.dbgSetPrologueEnd();
641 try self.genBody(self.mod_fn.analysis.success);641 try self.genBody(self.mod_fn.data.body);
642 try self.dbgSetEpilogueBegin();642 try self.dbgSetEpilogueBegin();
643 }643 }
644 },644 },
645 else => {645 else => {
646 try self.dbgSetPrologueEnd();646 try self.dbgSetPrologueEnd();
647 try self.genBody(self.mod_fn.analysis.success);647 try self.genBody(self.mod_fn.data.body);
648 try self.dbgSetEpilogueBegin();648 try self.dbgSetEpilogueBegin();
649 },649 },
650 }650 }
src/codegen/c.zig+1-1
...@@ -275,7 +275,7 @@ pub fn generate(file: *C, module: *Module, decl: *Decl) !void {...@@ -275,7 +275,7 @@ pub fn generate(file: *C, module: *Module, decl: *Decl) !void {
275 try writer.writeAll(" {");275 try writer.writeAll(" {");
276276
277 const func: *Module.Fn = func_payload.data;277 const func: *Module.Fn = func_payload.data;
278 const instructions = func.analysis.success.instructions;278 const instructions = func.data.body.instructions;
279 if (instructions.len > 0) {279 if (instructions.len > 0) {
280 try writer.writeAll("\n");280 try writer.writeAll("\n");
281 for (instructions) |inst| {281 for (instructions) |inst| {
src/codegen/wasm.zig+1-1
...@@ -63,7 +63,7 @@ pub fn genCode(buf: *ArrayList(u8), decl: *Decl) !void {...@@ -63,7 +63,7 @@ pub fn genCode(buf: *ArrayList(u8), decl: *Decl) !void {
63 // TODO: check for and handle death of instructions63 // TODO: check for and handle death of instructions
64 const tv = decl.typed_value.most_recent.typed_value;64 const tv = decl.typed_value.most_recent.typed_value;
65 const mod_fn = tv.val.castTag(.function).?.data;65 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
68 // Write 'end' opcode68 // Write 'end' opcode
69 try writer.writeByte(0x0B);69 try writer.writeByte(0x0B);
src/config.zig.in-1
...@@ -2,7 +2,6 @@ pub const have_llvm = true;...@@ -2,7 +2,6 @@ pub const have_llvm = true;
2pub const version: [:0]const u8 = "@ZIG_VERSION@";2pub const version: [:0]const u8 = "@ZIG_VERSION@";
3pub const semver = try @import("std").SemanticVersion.parse(version);3pub const semver = try @import("std").SemanticVersion.parse(version);
4pub const log_scopes: []const []const u8 = &[_][]const u8{};4pub const log_scopes: []const []const u8 = &[_][]const u8{};
5pub const zir_dumps: []const []const u8 = &[_][]const u8{};
6pub const enable_tracy = false;5pub const enable_tracy = false;
7pub const is_stage1 = true;6pub const is_stage1 = true;
8pub const skip_non_native = false;7pub 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 {...@@ -2178,16 +2178,6 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
2178 else => false,2178 else => false,
2179 };2179 };
2180 if (is_fn) {2180 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
2191 // For functions we need to add a prologue to the debug line program.2181 // For functions we need to add a prologue to the debug line program.
2192 try dbg_line_buffer.ensureCapacity(26);2182 try dbg_line_buffer.ensureCapacity(26);
21932183
src/link/MachO/DebugSymbols.zig-10
...@@ -936,16 +936,6 @@ pub fn initDeclDebugBuffers(...@@ -936,16 +936,6 @@ pub fn initDeclDebugBuffers(
936 const typed_value = decl.typed_value.most_recent.typed_value;936 const typed_value = decl.typed_value.most_recent.typed_value;
937 switch (typed_value.ty.zigTypeTag()) {937 switch (typed_value.ty.zigTypeTag()) {
938 .Fn => {938 .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
949 // For functions we need to add a prologue to the debug line program.939 // For functions we need to add a prologue to the debug line program.
950 try dbg_line_buffer.ensureCapacity(26);940 try dbg_line_buffer.ensureCapacity(26);
951941
src/llvm_backend.zig+1-1
...@@ -294,7 +294,7 @@ pub const LLVMIRModule = struct {...@@ -294,7 +294,7 @@ pub const LLVMIRModule = struct {
294 const entry_block = llvm_func.appendBasicBlock("Entry");294 const entry_block = llvm_func.appendBasicBlock("Entry");
295 self.builder.positionBuilderAtEnd(entry_block);295 self.builder.positionBuilderAtEnd(entry_block);
296296
297 const instructions = func.analysis.success.instructions;297 const instructions = func.data.body.instructions;
298 for (instructions) |inst| {298 for (instructions) |inst| {
299 switch (inst.tag) {299 switch (inst.tag) {
300 .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),300 .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),
src/zir.zig+249-100
...@@ -793,7 +793,9 @@ pub const Inst = struct {...@@ -793,7 +793,9 @@ pub const Inst = struct {
793 fn_type: *Inst,793 fn_type: *Inst,
794 body: Module.Body,794 body: Module.Body,
795 },795 },
796 kw_args: struct {},796 kw_args: struct {
797 is_inline: bool = false,
798 },
797 };799 };
798800
799 pub const FnType = struct {801 pub const FnType = struct {
...@@ -1847,83 +1849,258 @@ pub fn emit(allocator: *Allocator, old_module: *IrModule) !Module {...@@ -1847,83 +1849,258 @@ pub fn emit(allocator: *Allocator, old_module: *IrModule) !Module {
1847/// For debugging purposes, prints a function representation to stderr.1849/// For debugging purposes, prints a function representation to stderr.
1848pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void {1850pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void {
1849 const allocator = old_module.gpa;1851 const allocator = old_module.gpa;
1850 var ctx: EmitZIR = .{1852 var ctx: DumpTzir = .{
1851 .allocator = allocator,1853 .allocator = allocator,
1852 .decls = .{},
1853 .arena = std.heap.ArenaAllocator.init(allocator),1854 .arena = std.heap.ArenaAllocator.init(allocator),
1854 .old_module = &old_module,1855 .old_module = &old_module,
1855 .next_auto_name = 0,1856 .module_fn = module_fn,
1856 .names = std.StringArrayHashMap(void).init(allocator),1857 .indent = 2,
1857 .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),1858 .inst_table = DumpTzir.InstTable.init(allocator),
1858 .indent = 0,1859 .partial_inst_table = DumpTzir.InstTable.init(allocator),
1859 .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator),1860 .const_table = DumpTzir.InstTable.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),
1863 };1861 };
1864 defer ctx.metadata.deinit();1862 defer ctx.inst_table.deinit();
1865 defer ctx.body_metadata.deinit();1863 defer ctx.partial_inst_table.deinit();
1866 defer ctx.block_table.deinit();1864 defer ctx.const_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();
1871 defer ctx.arena.deinit();1865 defer ctx.arena.deinit();
18721866
1873 const fn_ty = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;1867 switch (module_fn.analysis()) {
1874 _ = ctx.emitFn(module_fn, 0, fn_ty) catch |err| {1868 .queued => std.debug.print("(queued)", .{}),
1875 std.debug.print("unable to dump function: {s}\n", .{@errorName(err)});1869 .in_progress => std.debug.print("(in_progress)", .{}),
1876 return;1870 .sema_failure => std.debug.print("(sema_failure)", .{}),
1877 };1871 .dependency_failure => std.debug.print("(dependency_failure)", .{}),
1878 var module = Module{1872 .success => |body| {
1879 .decls = ctx.decls.items,1873 ctx.dump(body, std.io.getStdErr().writer()) catch @panic("failed to dump TZIR");
1880 .arena = ctx.arena,1874 },
1881 .metadata = ctx.metadata,1875 }
1882 .body_metadata = ctx.body_metadata,
1883 };
1884
1885 module.dump();
1886}1876}
18871877
1888/// For debugging purposes, prints a function representation to stderr.1878const DumpTzir = struct {
1889pub fn dumpBlock(old_module: IrModule, module_block: *IrModule.Scope.Block) void {1879 allocator: *Allocator,
1890 const allocator = old_module.gpa;1880 arena: std.heap.ArenaAllocator,
1891 var ctx: EmitZIR = .{1881 old_module: *const IrModule,
1892 .allocator = allocator,1882 module_fn: *IrModule.Fn,
1893 .decls = .{},1883 indent: usize,
1894 .arena = std.heap.ArenaAllocator.init(allocator),1884 inst_table: InstTable,
1895 .old_module = &old_module,1885 partial_inst_table: InstTable,
1896 .next_auto_name = 0,1886 const_table: InstTable,
1897 .names = std.StringArrayHashMap(void).init(allocator),1887 next_index: usize = 0,
1898 .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),1888 next_partial_index: usize = 0,
1899 .indent = 0,1889 next_const_index: usize = 0,
1900 .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator),1890
1901 .loop_table = std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop).init(allocator),1891 const InstTable = std.AutoArrayHashMap(*ir.Inst, usize);
1902 .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator),1892
1903 .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator),1893 fn dump(dtz: *DumpTzir, body: ir.Body, writer: std.fs.File.Writer) !void {
1904 };1894 // First pass to pre-populate the table so that we can show even invalid references.
1905 defer ctx.metadata.deinit();1895 // Must iterate the same order we iterate the second time.
1906 defer ctx.body_metadata.deinit();1896 // We also look for constants and put them in the const_table.
1907 defer ctx.block_table.deinit();1897 for (body.instructions) |inst| {
1908 defer ctx.loop_table.deinit();1898 try dtz.inst_table.put(inst, dtz.next_index);
1909 defer ctx.decls.deinit(allocator);1899 dtz.next_index += 1;
1910 defer ctx.names.deinit();1900 switch (inst.tag) {
1911 defer ctx.primitive_table.deinit();1901 .alloc,
1912 defer ctx.arena.deinit();1902 .retvoid,
1903 .unreach,
1904 .breakpoint,
1905 .dbg_stmt,
1906 => {},
19131907
1914 _ = ctx.emitBlock(module_block, 0) catch |err| {1908 .ref,
1915 std.debug.print("unable to dump function: {}\n", .{err});1909 .ret,
1916 return;1910 .bitcast,
1917 };1911 .not,
1918 var module = Module{1912 .isnonnull,
1919 .decls = ctx.decls.items,1913 .isnull,
1920 .arena = ctx.arena,1914 .iserr,
1921 .metadata = ctx.metadata,1915 .ptrtoint,
1922 .body_metadata = ctx.body_metadata,1916 .floatcast,
1923 };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 .add,
1926}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
1928const EmitZIR = struct {2105const EmitZIR = struct {
1929 allocator: *Allocator,2106 allocator: *Allocator,
...@@ -2105,36 +2282,6 @@ const EmitZIR = struct {...@@ -2105,36 +2282,6 @@ const EmitZIR = struct {
2105 return &declref_inst.base;2282 return &declref_inst.base;
2106 }2283 }
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
2138 fn emitFn(self: *EmitZIR, module_fn: *IrModule.Fn, src: usize, ty: Type) Allocator.Error!*Decl {2285 fn emitFn(self: *EmitZIR, module_fn: *IrModule.Fn, src: usize, ty: Type) Allocator.Error!*Decl {
2139 var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator);2286 var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator);
2140 defer inst_table.deinit();2287 defer inst_table.deinit();
...@@ -2142,7 +2289,7 @@ const EmitZIR = struct {...@@ -2142,7 +2289,7 @@ const EmitZIR = struct {
2142 var instructions = std.ArrayList(*Inst).init(self.allocator);2289 var instructions = std.ArrayList(*Inst).init(self.allocator);
2143 defer instructions.deinit();2290 defer instructions.deinit();
21442291
2145 switch (module_fn.analysis) {2292 switch (module_fn.analysis()) {
2146 .queued => unreachable,2293 .queued => unreachable,
2147 .in_progress => unreachable,2294 .in_progress => unreachable,
2148 .success => |body| {2295 .success => |body| {
...@@ -2224,7 +2371,9 @@ const EmitZIR = struct {...@@ -2224,7 +2371,9 @@ const EmitZIR = struct {
2224 .fn_type = fn_type.inst,2371 .fn_type = fn_type.inst,
2225 .body = .{ .instructions = arena_instrs },2372 .body = .{ .instructions = arena_instrs },
2226 },2373 },
2227 .kw_args = .{},2374 .kw_args = .{
2375 .is_inline = module_fn.bits.is_inline,
2376 },
2228 };2377 };
2229 return self.emitUnnamedDecl(&fn_inst.base);2378 return self.emitUnnamedDecl(&fn_inst.base);
2230 }2379 }
src/zir_sema.zig+163-76
...@@ -577,7 +577,15 @@ fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) In...@@ -577,7 +577,15 @@ fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) In
577}577}
578578
579fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst {579fn 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 }
581 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;589 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
582 const param_index = b.instructions.items.len;590 const param_index = b.instructions.items.len;
583 const param_count = fn_ty.fnParamLen();591 const param_count = fn_ty.fnParamLen();
...@@ -636,7 +644,7 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c...@@ -636,7 +644,7 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c
636 .decl = parent_block.decl,644 .decl = parent_block.decl,
637 .instructions = .{},645 .instructions = .{},
638 .arena = parent_block.arena,646 .arena = parent_block.arena,
639 .label = null,647 .label = .none,
640 .is_comptime = parent_block.is_comptime or is_comptime,648 .is_comptime = parent_block.is_comptime or is_comptime,
641 };649 };
642 defer child_block.instructions.deinit(mod.gpa);650 defer child_block.instructions.deinit(mod.gpa);
...@@ -674,41 +682,56 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt...@@ -674,41 +682,56 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt
674 .decl = parent_block.decl,682 .decl = parent_block.decl,
675 .instructions = .{},683 .instructions = .{},
676 .arena = parent_block.arena,684 .arena = parent_block.arena,
677 // TODO @as here is working around a stage1 miscompilation bug :(685 .label = Scope.Block.Label{
678 .label = @as(?Scope.Block.Label, Scope.Block.Label{686 .breaking = .{
679 .zir_block = inst,687 .zir_block = inst,
680 .results = .{},688 .merges = .{
681 .block_inst = block_inst,689 .results = .{},
682 }),690 .block_inst = block_inst,
691 },
692 },
693 },
683 .is_comptime = is_comptime or parent_block.is_comptime,694 .is_comptime = is_comptime or parent_block.is_comptime,
684 };695 };
685 const label = &child_block.label.?;696 const merges = &child_block.label.breaking.merges;
686697
687 defer child_block.instructions.deinit(mod.gpa);698 defer child_block.instructions.deinit(mod.gpa);
688 defer label.results.deinit(mod.gpa);699 defer merges.results.deinit(mod.gpa);
689700
690 try analyzeBody(mod, &child_block.base, inst.positionals.body);701 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
692 // Blocks must terminate with noreturn instruction.714 // Blocks must terminate with noreturn instruction.
693 assert(child_block.instructions.items.len != 0);715 assert(child_block.instructions.items.len != 0);
694 assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());716 assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());
695717
696 if (label.results.items.len == 0) {718 if (merges.results.items.len == 0) {
697 // No need for a block instruction. We can put the new instructions directly into the parent block.719 // No need for a block instruction. We can put the new instructions
720 // directly into the parent block.
698 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items);721 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items);
699 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);722 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
700 return copied_instructions[copied_instructions.len - 1];723 return copied_instructions[copied_instructions.len - 1];
701 }724 }
702 if (label.results.items.len == 1) {725 if (merges.results.items.len == 1) {
703 const last_inst_index = child_block.instructions.items.len - 1;726 const last_inst_index = child_block.instructions.items.len - 1;
704 const last_inst = child_block.instructions.items[last_inst_index];727 const last_inst = child_block.instructions.items[last_inst_index];
705 if (last_inst.breakBlock()) |br_block| {728 if (last_inst.breakBlock()) |br_block| {
706 if (br_block == block_inst) {729 if (br_block == merges.block_inst) {
707 // No need for a block instruction. We can put the new instructions directly into the parent block.730 // No need for a block instruction. We can put the new instructions directly into the parent block.
708 // Here we omit the break instruction.731 // Here we omit the break instruction.
709 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]);732 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]);
710 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);733 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
711 return label.results.items[0];734 return merges.results.items[0];
712 }735 }
713 }736 }
714 }737 }
...@@ -717,10 +740,10 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt...@@ -717,10 +740,10 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt
717740
718 // Need to set the type and emit the Block instruction. This allows machine code generation741 // Need to set the type and emit the Block instruction. This allows machine code generation
719 // to emit a jump instruction to after the block when it encounters the break.742 // 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);743 try parent_block.instructions.append(mod.gpa, &merges.block_inst.base);
721 block_inst.base.ty = try mod.resolvePeerTypes(scope, label.results.items);744 merges.block_inst.base.ty = try mod.resolvePeerTypes(scope, merges.results.items);
722 block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };745 merges.block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
723 return &block_inst.base;746 return &merges.block_inst.base;
724}747}
725748
726fn analyzeInstBreakpoint(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {749fn 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...@@ -829,14 +852,32 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
829 const ret_type = func.ty.fnReturnType();852 const ret_type = func.ty.fnReturnType();
830853
831 const b = try mod.requireFunctionBlock(scope, inst.base.src);854 const b = try mod.requireFunctionBlock(scope, inst.base.src);
832 if (b.is_comptime) {855 const is_comptime_call = b.is_comptime or inst.kw_args.modifier == .compile_time;
833 const fn_val = try mod.resolveConstValue(scope, func);856 const is_inline_call = is_comptime_call or inst.kw_args.modifier == .always_inline or blk: {
834 const module_fn = switch (fn_val.tag()) {857 // This logic will get simplified by
835 .function => fn_val.castTag(.function).?.data,858 // https://github.com/ziglang/zig/issues/6429
836 .extern_fn => return mod.fail(scope, inst.base.src, "comptime call of extern function", .{}),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 }),
837 else => unreachable,875 else => unreachable,
838 };876 };
839 const callee_decl = module_fn.owner_decl;877 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.
840 const callee_file_scope = callee_decl.getFileScope();881 const callee_file_scope = callee_decl.getFileScope();
841 const tree = mod.getAstTree(callee_file_scope) catch |err| switch (err) {882 const tree = mod.getAstTree(callee_file_scope) catch |err| switch (err) {
842 error.OutOfMemory => return error.OutOfMemory,883 error.OutOfMemory => return error.OutOfMemory,
...@@ -859,23 +900,31 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError...@@ -859,23 +900,31 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
859 };900 };
860 defer gen_scope.instructions.deinit(mod.gpa);901 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);
863 var params_scope = &gen_scope.base;905 var params_scope = &gen_scope.base;
864 for (fn_proto.params()) |param, i| {906 for (fn_proto.params()) |param, i| {
865 const name_token = param.name_token.?;907 const name_token = param.name_token.?;
866 const src = tree.token_locs[name_token].start;908 const src = tree.token_locs[name_token].start;
867 const param_name = try mod.identifierTokenString(scope, name_token);909 const param_name = try mod.identifierTokenString(scope, name_token);
868 const arg_val = try mod.resolveConstValue(scope, casted_args[i]);910 const arg = try call_arena.allocator.create(zir.Inst.Arg);
869 const arg = try astgen.addZIRInstConst(mod, params_scope, src, .{911 arg.* = .{
870 .ty = casted_args[i].ty,912 .base = .{
871 .val = arg_val,913 .tag = .arg,
872 });914 .src = src,
915 },
916 .positionals = .{
917 .name = param_name,
918 },
919 .kw_args = .{},
920 };
921 gen_scope.instructions.items[i] = &arg.base;
873 const sub_scope = try call_arena.allocator.create(Scope.LocalVal);922 const sub_scope = try call_arena.allocator.create(Scope.LocalVal);
874 sub_scope.* = .{923 sub_scope.* = .{
875 .parent = params_scope,924 .parent = params_scope,
876 .gen_zir = &gen_scope,925 .gen_zir = &gen_scope,
877 .name = param_name,926 .name = param_name,
878 .inst = arg,927 .inst = &arg.base,
879 };928 };
880 params_scope = &sub_scope.base;929 params_scope = &sub_scope.base;
881 }930 }
...@@ -896,42 +945,52 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError...@@ -896,42 +945,52 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
896 zir.dumpZir(mod.gpa, "fn_body_callee", callee_decl.name, gen_scope.instructions.items) catch {};945 zir.dumpZir(mod.gpa, "fn_body_callee", callee_decl.name, gen_scope.instructions.items) catch {};
897 }946 }
898947
899 // Analyze the ZIR.948 // Analyze the ZIR. The same ZIR gets analyzed into a runtime function
900 var inner_block: Scope.Block = .{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 = .{
901 .parent = null,963 .parent = null,
902 .func = module_fn,964 .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().?,
904 .instructions = .{},968 .instructions = .{},
905 .arena = &call_arena.allocator,969 .arena = scope.arena(),
906 .is_comptime = true,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,
907 };981 };
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 correctly987 // This will have return instructions analyzed as break instructions to
911 // and attach to the caller Decl not the callee.988 // the block_inst above.
912 try analyzeBody(mod, &inner_block.base, .{989 try analyzeBody(mod, &child_block.base, .{
913 .instructions = gen_scope.instructions.items,990 .instructions = gen_scope.instructions.items,
914 });991 });
915992
916 if (mod.comp.verbose_ir) {993 return analyzeBlockBody(mod, scope, &child_block, merges);
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 }
935 }994 }
936995
937 return mod.addCall(b, inst.base.src, ret_type, func, casted_args);996 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!...@@ -954,7 +1013,11 @@ fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!
954 };1013 };
955 const new_func = try scope.arena().create(Module.Fn);1014 const new_func = try scope.arena().create(Module.Fn);
956 new_func.* = .{1015 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 },
958 .owner_decl = scope.decl().?,1021 .owner_decl = scope.decl().?,
959 };1022 };
960 return mod.constInst(scope, fn_inst.base.src, .{1023 return mod.constInst(scope, fn_inst.base.src, .{
...@@ -2020,21 +2083,41 @@ fn analyzeInstUnreachable(...@@ -2020,21 +2083,41 @@ fn analyzeInstUnreachable(
2020fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {2083fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
2021 const operand = try resolveInst(mod, scope, inst.positionals.operand);2084 const operand = try resolveInst(mod, scope, inst.positionals.operand);
2022 const b = try mod.requireFunctionBlock(scope, inst.base.src);2085 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 }
2024}2097}
20252098
2026fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {2099fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
2027 const b = try mod.requireFunctionBlock(scope, inst.base.src);2100 const b = try mod.requireFunctionBlock(scope, inst.base.src);
2028 if (b.func) |func| {2101 switch (b.label) {
2029 // Need to emit a compile error if returning void is not allowed.2102 .inlining => |*inlining| {
2030 const void_inst = try mod.constVoid(scope, inst.base.src);2103 // We are inlining a function call; rewrite the `retvoid` as a `breakvoid`.
2031 const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty;2104 const void_inst = try mod.constVoid(scope, inst.base.src);
2032 const casted_void = try mod.coerce(scope, fn_ty.fnReturnType(), void_inst);2105 try inlining.merges.results.append(mod.gpa, void_inst);
2033 if (casted_void.ty.zigTypeTag() != .Void) {2106 return mod.addBr(b, inst.base.src, inlining.merges.block_inst, void_inst);
2034 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, casted_void);2107 },
2035 }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 },
2036 }2120 }
2037 return mod.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);
2038}2121}
20392122
2040fn floatOpAllowed(tag: zir.Inst.Tag) bool {2123fn floatOpAllowed(tag: zir.Inst.Tag) bool {
...@@ -2054,12 +2137,16 @@ fn analyzeBreak(...@@ -2054,12 +2137,16 @@ fn analyzeBreak(
2054) InnerError!*Inst {2137) InnerError!*Inst {
2055 var opt_block = scope.cast(Scope.Block);2138 var opt_block = scope.cast(Scope.Block);
2056 while (opt_block) |block| {2139 while (opt_block) |block| {
2057 if (block.label) |*label| {2140 switch (block.label) {
2058 if (label.zir_block == zir_block) {2141 .none => {},
2059 try label.results.append(mod.gpa, operand);2142 .breaking => |*label| {
2060 const b = try mod.requireRuntimeBlock(scope, src);2143 if (label.zir_block == zir_block) {
2061 return mod.addBr(b, src, label.block_inst, operand);2144 try label.merges.results.append(mod.gpa, operand);
2062 }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.
2063 }2150 }
2064 opt_block = block.parent;2151 opt_block = block.parent;
2065 } else unreachable;2152 } else unreachable;
test/stage2/zir.zig+6-6
...@@ -30,7 +30,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -30,7 +30,7 @@ pub fn addCases(ctx: *TestContext) !void {
30 \\@unnamed$7 = fntype([], @void, cc=C)30 \\@unnamed$7 = fntype([], @void, cc=C)
31 \\@entry = fn(@unnamed$7, {31 \\@entry = fn(@unnamed$7, {
32 \\ %0 = returnvoid() ; deaths=0b100000000000000032 \\ %0 = returnvoid() ; deaths=0b1000000000000000
33 \\})33 \\}, is_inline=0)
34 \\34 \\
35 );35 );
36 ctx.transformZIR("elemptr, add, cmp, condbr, return, breakpoint", linux_x64,36 ctx.transformZIR("elemptr, add, cmp, condbr, return, breakpoint", linux_x64,
...@@ -78,7 +78,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -78,7 +78,7 @@ pub fn addCases(ctx: *TestContext) !void {
78 \\@unnamed$6 = fntype([], @void, cc=C)78 \\@unnamed$6 = fntype([], @void, cc=C)
79 \\@entry = fn(@unnamed$6, {79 \\@entry = fn(@unnamed$6, {
80 \\ %0 = returnvoid() ; deaths=0b100000000000000080 \\ %0 = returnvoid() ; deaths=0b1000000000000000
81 \\})81 \\}, is_inline=0)
82 \\@entry__anon_1 = str("2\x08\x01\n")82 \\@entry__anon_1 = str("2\x08\x01\n")
83 \\@9 = declref("9__anon_0")83 \\@9 = declref("9__anon_0")
84 \\@9__anon_0 = str("entry")84 \\@9__anon_0 = str("entry")
...@@ -123,17 +123,17 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -123,17 +123,17 @@ pub fn addCases(ctx: *TestContext) !void {
123 \\@entry = fn(@unnamed$7, {123 \\@entry = fn(@unnamed$7, {
124 \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001124 \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001
125 \\ %1 = returnvoid() ; deaths=0b1000000000000000125 \\ %1 = returnvoid() ; deaths=0b1000000000000000
126 \\})126 \\}, is_inline=0)
127 \\@unnamed$9 = fntype([], @void, cc=C)127 \\@unnamed$9 = fntype([], @void, cc=C)
128 \\@a = fn(@unnamed$9, {128 \\@a = fn(@unnamed$9, {
129 \\ %0 = call(@b, [], modifier=auto) ; deaths=0b1000000000000001129 \\ %0 = call(@b, [], modifier=auto) ; deaths=0b1000000000000001
130 \\ %1 = returnvoid() ; deaths=0b1000000000000000130 \\ %1 = returnvoid() ; deaths=0b1000000000000000
131 \\})131 \\}, is_inline=0)
132 \\@unnamed$11 = fntype([], @void, cc=C)132 \\@unnamed$11 = fntype([], @void, cc=C)
133 \\@b = fn(@unnamed$11, {133 \\@b = fn(@unnamed$11, {
134 \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001134 \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001
135 \\ %1 = returnvoid() ; deaths=0b1000000000000000135 \\ %1 = returnvoid() ; deaths=0b1000000000000000
136 \\})136 \\}, is_inline=0)
137 \\137 \\
138 );138 );
139 // Now we introduce a compile error139 // Now we introduce a compile error
...@@ -203,7 +203,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -203,7 +203,7 @@ pub fn addCases(ctx: *TestContext) !void {
203 \\@unnamed$7 = fntype([], @void, cc=C)203 \\@unnamed$7 = fntype([], @void, cc=C)
204 \\@entry = fn(@unnamed$7, {204 \\@entry = fn(@unnamed$7, {
205 \\ %0 = returnvoid() ; deaths=0b1000000000000000205 \\ %0 = returnvoid() ; deaths=0b1000000000000000
206 \\})206 \\}, is_inline=0)
207 \\207 \\
208 );208 );
209 }209 }