authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-02 22:42:07-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-02 22:42:07-07:00
log654832253a7857e78aab85e28ed09fb16b632dd2
tree7769abc2acc91382900bfb2042db3ce84a3fab4e
parent50a530196ca4e91b387f9937475dd8891edb3f4f

stage2: support recursive inline/comptime functions

zir.Inst no longer has an `analyzed_inst` field. This is previously how we mapped ZIR to their TZIR counterparts, however with the way inline and comptime function calls work, we can potentially have the same ZIR structure being analyzed by multiple different analyses, such as during a recursive inline function call. This would cause the `analyzed_inst` field to become clobbered. So instead, we use a table to map the instructions to their semantically analyzed counterparts. This will help with multi-threaded compilation as well. Scope.Block.Inlining is split into 2 different layers of "sharedness". The first layer is shared by the whole inline/comptime function call stack. It contains the callsite where something is being inlined and the branch count/quota. The second layer is different per function call but shared by all the blocks within the function being inlined. Add support for debug dumping br and brvoid TZIR instructions. Remove the "unreachable code" error. It was happening even for this case: ```zig if (comptime_condition) return; bar(); // error: unreachable code ``` We will need smarter logic for when it is legal to emit this compile error. Remove the ZIR test cases. These are redundant with other higher level Zig source tests we have, and maintaining support for ZIRModule as a first-class top level abstraction is getting in the way of clean compiler design for the main use case. We will have ZIR/TZIR based test cases someday to help with testing optimization passes and ZIR to TZIR analysis, but as is, these test cases are not accomplishing that, and they are getting in the way.

5 files changed, 244 insertions(+), 399 deletions(-)

src/Module.zig+55-8
...@@ -752,17 +752,22 @@ pub const Scope = struct {...@@ -752,17 +752,22 @@ pub const Scope = struct {
752 /// during semantic analysis of the block.752 /// during semantic analysis of the block.
753 pub const Block = struct {753 pub const Block = struct {
754 pub const base_tag: Tag = .block;754 pub const base_tag: Tag = .block;
755
755 base: Scope = Scope{ .tag = base_tag },756 base: Scope = Scope{ .tag = base_tag },
756 parent: ?*Block,757 parent: ?*Block,
758 /// Maps ZIR to TZIR. Shared to sub-blocks.
759 inst_table: *InstTable,
757 func: ?*Fn,760 func: ?*Fn,
758 decl: *Decl,761 decl: *Decl,
759 instructions: ArrayListUnmanaged(*Inst),762 instructions: ArrayListUnmanaged(*Inst),
760 /// Points to the arena allocator of DeclAnalysis763 /// Points to the arena allocator of DeclAnalysis
761 arena: *Allocator,764 arena: *Allocator,
762 label: ?Label = null,765 label: ?Label = null,
763 inlining: ?Inlining,766 inlining: ?*Inlining,
764 is_comptime: bool,767 is_comptime: bool,
765768
769 pub const InstTable = std.AutoHashMap(*zir.Inst, *Inst);
770
766 /// This `Block` maps a block ZIR instruction to the corresponding771 /// This `Block` maps a block ZIR instruction to the corresponding
767 /// TZIR instruction for break instruction analysis.772 /// TZIR instruction for break instruction analysis.
768 pub const Label = struct {773 pub const Label = struct {
...@@ -773,14 +778,23 @@ pub const Scope = struct {...@@ -773,14 +778,23 @@ pub const Scope = struct {
773 /// This `Block` indicates that an inline function call is happening778 /// This `Block` indicates that an inline function call is happening
774 /// and return instructions should be analyzed as a break instruction779 /// and return instructions should be analyzed as a break instruction
775 /// to this TZIR block instruction.780 /// to this TZIR block instruction.
781 /// It is shared among all the blocks in an inline or comptime called
782 /// function.
776 pub const Inlining = struct {783 pub const Inlining = struct {
777 caller: ?*Fn,784 /// Shared state among the entire inline/comptime call stack.
785 shared: *Shared,
778 /// We use this to count from 0 so that arg instructions know786 /// We use this to count from 0 so that arg instructions know
779 /// which parameter index they are, without having to store787 /// which parameter index they are, without having to store
780 /// a parameter index with each arg instruction.788 /// a parameter index with each arg instruction.
781 param_index: usize,789 param_index: usize,
782 casted_args: []*Inst,790 casted_args: []*Inst,
783 merges: Merges,791 merges: Merges,
792
793 pub const Shared = struct {
794 caller: ?*Fn,
795 branch_count: u64,
796 branch_quota: u64,
797 };
784 };798 };
785799
786 pub const Merges = struct {800 pub const Merges = struct {
...@@ -1087,8 +1101,12 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1087,8 +1101,12 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1087 errdefer decl_arena.deinit();1101 errdefer decl_arena.deinit();
1088 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);1102 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
10891103
1104 var inst_table = Scope.Block.InstTable.init(self.gpa);
1105 defer inst_table.deinit();
1106
1090 var block_scope: Scope.Block = .{1107 var block_scope: Scope.Block = .{
1091 .parent = null,1108 .parent = null,
1109 .inst_table = &inst_table,
1092 .func = null,1110 .func = null,
1093 .decl = decl,1111 .decl = decl,
1094 .instructions = .{},1112 .instructions = .{},
...@@ -1276,8 +1294,12 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1276,8 +1294,12 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1276 errdefer decl_arena.deinit();1294 errdefer decl_arena.deinit();
1277 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);1295 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
12781296
1297 var decl_inst_table = Scope.Block.InstTable.init(self.gpa);
1298 defer decl_inst_table.deinit();
1299
1279 var block_scope: Scope.Block = .{1300 var block_scope: Scope.Block = .{
1280 .parent = null,1301 .parent = null,
1302 .inst_table = &decl_inst_table,
1281 .func = null,1303 .func = null,
1282 .decl = decl,1304 .decl = decl,
1283 .instructions = .{},1305 .instructions = .{},
...@@ -1342,8 +1364,12 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1342,8 +1364,12 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1342 zir.dumpZir(self.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {};1364 zir.dumpZir(self.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {};
1343 }1365 }
13441366
1367 var var_inst_table = Scope.Block.InstTable.init(self.gpa);
1368 defer var_inst_table.deinit();
1369
1345 var inner_block: Scope.Block = .{1370 var inner_block: Scope.Block = .{
1346 .parent = null,1371 .parent = null,
1372 .inst_table = &var_inst_table,
1347 .func = null,1373 .func = null,
1348 .decl = decl,1374 .decl = decl,
1349 .instructions = .{},1375 .instructions = .{},
...@@ -1352,10 +1378,12 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1352,10 +1378,12 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1352 .is_comptime = true,1378 .is_comptime = true,
1353 };1379 };
1354 defer inner_block.instructions.deinit(self.gpa);1380 defer inner_block.instructions.deinit(self.gpa);
1355 try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items });1381 try zir_sema.analyzeBody(self, &inner_block, .{
1382 .instructions = gen_scope.instructions.items,
1383 });
13561384
1357 // The result location guarantees the type coercion.1385 // The result location guarantees the type coercion.
1358 const analyzed_init_inst = init_inst.analyzed_inst.?;1386 const analyzed_init_inst = var_inst_table.get(init_inst).?;
1359 // The is_comptime in the Scope.Block guarantees the result is comptime-known.1387 // The is_comptime in the Scope.Block guarantees the result is comptime-known.
1360 const val = analyzed_init_inst.value().?;1388 const val = analyzed_init_inst.value().?;
13611389
...@@ -1463,8 +1491,12 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1463,8 +1491,12 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1463 zir.dumpZir(self.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {};1491 zir.dumpZir(self.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {};
1464 }1492 }
14651493
1494 var inst_table = Scope.Block.InstTable.init(self.gpa);
1495 defer inst_table.deinit();
1496
1466 var block_scope: Scope.Block = .{1497 var block_scope: Scope.Block = .{
1467 .parent = null,1498 .parent = null,
1499 .inst_table = &inst_table,
1468 .func = null,1500 .func = null,
1469 .decl = decl,1501 .decl = decl,
1470 .instructions = .{},1502 .instructions = .{},
...@@ -1474,7 +1506,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1474,7 +1506,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1474 };1506 };
1475 defer block_scope.instructions.deinit(self.gpa);1507 defer block_scope.instructions.deinit(self.gpa);
14761508
1477 _ = try zir_sema.analyzeBody(self, &block_scope.base, .{1509 _ = try zir_sema.analyzeBody(self, &block_scope, .{
1478 .instructions = gen_scope.instructions.items,1510 .instructions = gen_scope.instructions.items,
1479 });1511 });
14801512
...@@ -1841,8 +1873,11 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {...@@ -1841,8 +1873,11 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
1841 // Use the Decl's arena for function memory.1873 // Use the Decl's arena for function memory.
1842 var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);1874 var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
1843 defer decl.typed_value.most_recent.arena.?.* = arena.state;1875 defer decl.typed_value.most_recent.arena.?.* = arena.state;
1876 var inst_table = Scope.Block.InstTable.init(self.gpa);
1877 defer inst_table.deinit();
1844 var inner_block: Scope.Block = .{1878 var inner_block: Scope.Block = .{
1845 .parent = null,1879 .parent = null,
1880 .inst_table = &inst_table,
1846 .func = func,1881 .func = func,
1847 .decl = decl,1882 .decl = decl,
1848 .instructions = .{},1883 .instructions = .{},
...@@ -1855,7 +1890,7 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {...@@ -1855,7 +1890,7 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
1855 func.state = .in_progress;1890 func.state = .in_progress;
1856 log.debug("set {s} to in_progress\n", .{decl.name});1891 log.debug("set {s} to in_progress\n", .{decl.name});
18571892
1858 try zir_sema.analyzeBody(self, &inner_block.base, func.zir);1893 try zir_sema.analyzeBody(self, &inner_block, func.zir);
18591894
1860 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);1895 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
1861 func.state = .success;1896 func.state = .success;
...@@ -3055,8 +3090,8 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Com...@@ -3055,8 +3090,8 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Com
3055 },3090 },
3056 .block => {3091 .block => {
3057 const block = scope.cast(Scope.Block).?;3092 const block = scope.cast(Scope.Block).?;
3058 if (block.inlining) |*inlining| {3093 if (block.inlining) |inlining| {
3059 if (inlining.caller) |func| {3094 if (inlining.shared.caller) |func| {
3060 func.state = .sema_failure;3095 func.state = .sema_failure;
3061 } else {3096 } else {
3062 block.decl.analysis = .sema_failure;3097 block.decl.analysis = .sema_failure;
...@@ -3424,6 +3459,7 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic...@@ -3424,6 +3459,7 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic
34243459
3425 var fail_block: Scope.Block = .{3460 var fail_block: Scope.Block = .{
3426 .parent = parent_block,3461 .parent = parent_block,
3462 .inst_table = parent_block.inst_table,
3427 .func = parent_block.func,3463 .func = parent_block.func,
3428 .decl = parent_block.decl,3464 .decl = parent_block.decl,
3429 .instructions = .{},3465 .instructions = .{},
...@@ -3492,3 +3528,14 @@ pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex)...@@ -3492,3 +3528,14 @@ pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex)
3492 }3528 }
3493 return ident_name;3529 return ident_name;
3494}3530}
3531
3532pub fn emitBackwardBranch(mod: *Module, block: *Scope.Block, src: usize) !void {
3533 const shared = block.inlining.?.shared;
3534 shared.branch_count += 1;
3535 if (shared.branch_count > shared.branch_quota) {
3536 // TODO show the "called from here" stack
3537 return mod.fail(&block.base, src, "evaluation exceeded {d} backwards branches", .{
3538 shared.branch_quota,
3539 });
3540 }
3541}
src/zir.zig+71-6
...@@ -25,12 +25,13 @@ pub const Decl = struct {...@@ -25,12 +25,13 @@ pub const Decl = struct {
2525
26/// These are instructions that correspond to the ZIR text format. See `ir.Inst` for26/// These are instructions that correspond to the ZIR text format. See `ir.Inst` for
27/// in-memory, analyzed instructions with types and values.27/// in-memory, analyzed instructions with types and values.
28/// We use a table to map these instruction to their respective semantically analyzed
29/// instructions because it is possible to have multiple analyses on the same ZIR
30/// happening at the same time.
28pub const Inst = struct {31pub const Inst = struct {
29 tag: Tag,32 tag: Tag,
30 /// Byte offset into the source.33 /// Byte offset into the source.
31 src: usize,34 src: usize,
32 /// Pre-allocated field for mapping ZIR text instructions to post-analysis instructions.
33 analyzed_inst: ?*ir.Inst = null,
3435
35 /// These names are used directly as the instruction names in the text format.36 /// These names are used directly as the instruction names in the text format.
36 pub const Tag = enum {37 pub const Tag = enum {
...@@ -1947,11 +1948,20 @@ const DumpTzir = struct {...@@ -1947,11 +1948,20 @@ const DumpTzir = struct {
19471948
1948 .arg => {},1949 .arg => {},
19491950
1951 .br => {
1952 const br = inst.castTag(.br).?;
1953 try dtz.findConst(&br.block.base);
1954 try dtz.findConst(br.operand);
1955 },
1956
1957 .brvoid => {
1958 const brvoid = inst.castTag(.brvoid).?;
1959 try dtz.findConst(&brvoid.block.base);
1960 },
1961
1950 // TODO fill out this debug printing1962 // TODO fill out this debug printing
1951 .assembly,1963 .assembly,
1952 .block,1964 .block,
1953 .br,
1954 .brvoid,
1955 .call,1965 .call,
1956 .condbr,1966 .condbr,
1957 .constant,1967 .constant,
...@@ -2078,11 +2088,66 @@ const DumpTzir = struct {...@@ -2078,11 +2088,66 @@ const DumpTzir = struct {
2078 try writer.print("{s})\n", .{arg.name});2088 try writer.print("{s})\n", .{arg.name});
2079 },2089 },
20802090
2091 .br => {
2092 const br = inst.castTag(.br).?;
2093
2094 var lhs_kinky: ?usize = null;
2095 var rhs_kinky: ?usize = null;
2096
2097 if (dtz.partial_inst_table.get(&br.block.base)) |operand_index| {
2098 try writer.print("%{d}, ", .{operand_index});
2099 } else if (dtz.const_table.get(&br.block.base)) |operand_index| {
2100 try writer.print("@{d}, ", .{operand_index});
2101 } else if (dtz.inst_table.get(&br.block.base)) |operand_index| {
2102 lhs_kinky = operand_index;
2103 try writer.print("%{d}, ", .{operand_index});
2104 } else {
2105 try writer.writeAll("!BADREF!, ");
2106 }
2107
2108 if (dtz.partial_inst_table.get(br.operand)) |operand_index| {
2109 try writer.print("%{d}", .{operand_index});
2110 } else if (dtz.const_table.get(br.operand)) |operand_index| {
2111 try writer.print("@{d}", .{operand_index});
2112 } else if (dtz.inst_table.get(br.operand)) |operand_index| {
2113 rhs_kinky = operand_index;
2114 try writer.print("%{d}", .{operand_index});
2115 } else {
2116 try writer.writeAll("!BADREF!");
2117 }
2118
2119 if (lhs_kinky != null or rhs_kinky != null) {
2120 try writer.writeAll(") // Instruction does not dominate all uses!");
2121 if (lhs_kinky) |lhs| {
2122 try writer.print(" %{d}", .{lhs});
2123 }
2124 if (rhs_kinky) |rhs| {
2125 try writer.print(" %{d}", .{rhs});
2126 }
2127 try writer.writeAll("\n");
2128 } else {
2129 try writer.writeAll(")\n");
2130 }
2131 },
2132
2133 .brvoid => {
2134 const brvoid = inst.castTag(.brvoid).?;
2135 if (dtz.partial_inst_table.get(&brvoid.block.base)) |operand_index| {
2136 try writer.print("%{d})\n", .{operand_index});
2137 } else if (dtz.const_table.get(&brvoid.block.base)) |operand_index| {
2138 try writer.print("@{d})\n", .{operand_index});
2139 } else if (dtz.inst_table.get(&brvoid.block.base)) |operand_index| {
2140 try writer.print("%{d}) // Instruction does not dominate all uses!\n", .{
2141 operand_index,
2142 });
2143 } else {
2144 try writer.writeAll("!BADREF!)\n");
2145 }
2146 },
2147
2081 // TODO fill out this debug printing2148 // TODO fill out this debug printing
2082 .assembly,2149 .assembly,
2083 .block,2150 .block,
2084 .br,
2085 .brvoid,
2086 .call,2151 .call,
2087 .condbr,2152 .condbr,
2088 .constant,2153 .constant,
src/zir_sema.zig+71-68
...@@ -159,16 +159,11 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!...@@ -159,16 +159,11 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
159 }159 }
160}160}
161161
162pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void {162pub fn analyzeBody(mod: *Module, block: *Scope.Block, body: zir.Module.Body) !void {
163 for (body.instructions) |src_inst, i| {163 for (body.instructions) |src_inst| {
164 const analyzed_inst = try analyzeInst(mod, scope, src_inst);164 const analyzed_inst = try analyzeInst(mod, &block.base, src_inst);
165 src_inst.analyzed_inst = analyzed_inst;165 try block.inst_table.putNoClobber(src_inst, analyzed_inst);
166 if (analyzed_inst.ty.zigTypeTag() == .NoReturn) {166 if (analyzed_inst.ty.zigTypeTag() == .NoReturn) {
167 for (body.instructions[i..]) |unreachable_inst| {
168 if (unreachable_inst.castTag(.dbg_stmt)) |dbg_stmt| {
169 return mod.fail(scope, dbg_stmt.base.src, "unreachable code", .{});
170 }
171 }
172 break;167 break;
173 }168 }
174 }169 }
...@@ -180,8 +175,8 @@ pub fn analyzeBodyValueAsType(...@@ -180,8 +175,8 @@ pub fn analyzeBodyValueAsType(
180 zir_result_inst: *zir.Inst,175 zir_result_inst: *zir.Inst,
181 body: zir.Module.Body,176 body: zir.Module.Body,
182) !Type {177) !Type {
183 try analyzeBody(mod, &block_scope.base, body);178 try analyzeBody(mod, block_scope, body);
184 const result_inst = zir_result_inst.analyzed_inst.?;179 const result_inst = block_scope.inst_table.get(zir_result_inst).?;
185 const val = try mod.resolveConstValue(&block_scope.base, result_inst);180 const val = try mod.resolveConstValue(&block_scope.base, result_inst);
186 return val.toType(block_scope.base.arena());181 return val.toType(block_scope.base.arena());
187}182}
...@@ -264,30 +259,9 @@ fn resolveCompleteZirDecl(mod: *Module, scope: *Scope, src_decl: *zir.Decl) Inne...@@ -264,30 +259,9 @@ fn resolveCompleteZirDecl(mod: *Module, scope: *Scope, src_decl: *zir.Decl) Inne
264 return decl;259 return decl;
265}260}
266261
267/// TODO Look into removing this function. The body is only needed for .zir files, not .zig files.262pub fn resolveInst(mod: *Module, scope: *Scope, zir_inst: *zir.Inst) InnerError!*Inst {
268pub fn resolveInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {263 const block = scope.cast(Scope.Block).?;
269 if (old_inst.analyzed_inst) |inst| return inst;264 return block.inst_table.get(zir_inst).?; // Instruction does not dominate all uses!
270
271 // If this assert trips, the instruction that was referenced did not get properly
272 // analyzed before it was referenced.
273 const zir_module = scope.namespace().cast(Scope.ZIRModule).?;
274 const entry = if (old_inst.cast(zir.Inst.DeclVal)) |declval| blk: {
275 const decl_name = declval.positionals.name;
276 const entry = zir_module.contents.module.findDecl(decl_name) orelse
277 return mod.fail(scope, old_inst.src, "decl '{s}' not found", .{decl_name});
278 break :blk entry;
279 } else blk: {
280 // If this assert trips, the instruction that was referenced did not get
281 // properly analyzed by a previous instruction analysis before it was
282 // referenced by the current one.
283 break :blk zir_module.contents.module.findInstDecl(old_inst).?;
284 };
285 const decl = try resolveCompleteZirDecl(mod, scope, entry.decl);
286 const decl_ref = try mod.analyzeDeclRef(scope, old_inst.src, decl);
287 // Note: it would be tempting here to store the result into old_inst.analyzed_inst field,
288 // but this would prevent the analyzeDeclRef from happening, which is needed to properly
289 // detect Decl dependencies and dependency failures on updates.
290 return mod.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src);
291}265}
292266
293fn resolveConstString(mod: *Module, scope: *Scope, old_inst: *zir.Inst) ![]u8 {267fn resolveConstString(mod: *Module, scope: *Scope, old_inst: *zir.Inst) ![]u8 {
...@@ -576,7 +550,7 @@ fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) In...@@ -576,7 +550,7 @@ fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) In
576550
577fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst {551fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst {
578 const b = try mod.requireFunctionBlock(scope, inst.base.src);552 const b = try mod.requireFunctionBlock(scope, inst.base.src);
579 if (b.inlining) |*inlining| {553 if (b.inlining) |inlining| {
580 const param_index = inlining.param_index;554 const param_index = inlining.param_index;
581 inlining.param_index += 1;555 inlining.param_index += 1;
582 return inlining.casted_args[param_index];556 return inlining.casted_args[param_index];
...@@ -613,6 +587,7 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError...@@ -613,6 +587,7 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError
613587
614 var child_block: Scope.Block = .{588 var child_block: Scope.Block = .{
615 .parent = parent_block,589 .parent = parent_block,
590 .inst_table = parent_block.inst_table,
616 .func = parent_block.func,591 .func = parent_block.func,
617 .decl = parent_block.decl,592 .decl = parent_block.decl,
618 .instructions = .{},593 .instructions = .{},
...@@ -622,7 +597,7 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError...@@ -622,7 +597,7 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError
622 };597 };
623 defer child_block.instructions.deinit(mod.gpa);598 defer child_block.instructions.deinit(mod.gpa);
624599
625 try analyzeBody(mod, &child_block.base, inst.positionals.body);600 try analyzeBody(mod, &child_block, inst.positionals.body);
626601
627 // Loop repetition is implied so the last instruction may or may not be a noreturn instruction.602 // Loop repetition is implied so the last instruction may or may not be a noreturn instruction.
628603
...@@ -636,6 +611,7 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c...@@ -636,6 +611,7 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c
636611
637 var child_block: Scope.Block = .{612 var child_block: Scope.Block = .{
638 .parent = parent_block,613 .parent = parent_block,
614 .inst_table = parent_block.inst_table,
639 .func = parent_block.func,615 .func = parent_block.func,
640 .decl = parent_block.decl,616 .decl = parent_block.decl,
641 .instructions = .{},617 .instructions = .{},
...@@ -646,7 +622,7 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c...@@ -646,7 +622,7 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c
646 };622 };
647 defer child_block.instructions.deinit(mod.gpa);623 defer child_block.instructions.deinit(mod.gpa);
648624
649 try analyzeBody(mod, &child_block.base, inst.positionals.body);625 try analyzeBody(mod, &child_block, inst.positionals.body);
650626
651 try parent_block.instructions.appendSlice(mod.gpa, child_block.instructions.items);627 try parent_block.instructions.appendSlice(mod.gpa, child_block.instructions.items);
652628
...@@ -675,6 +651,7 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt...@@ -675,6 +651,7 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt
675651
676 var child_block: Scope.Block = .{652 var child_block: Scope.Block = .{
677 .parent = parent_block,653 .parent = parent_block,
654 .inst_table = parent_block.inst_table,
678 .func = parent_block.func,655 .func = parent_block.func,
679 .decl = parent_block.decl,656 .decl = parent_block.decl,
680 .instructions = .{},657 .instructions = .{},
...@@ -695,7 +672,7 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt...@@ -695,7 +672,7 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt
695 defer child_block.instructions.deinit(mod.gpa);672 defer child_block.instructions.deinit(mod.gpa);
696 defer merges.results.deinit(mod.gpa);673 defer merges.results.deinit(mod.gpa);
697674
698 try analyzeBody(mod, &child_block.base, inst.positionals.body);675 try analyzeBody(mod, &child_block, inst.positionals.body);
699676
700 return analyzeBlockBody(mod, scope, &child_block, merges);677 return analyzeBlockBody(mod, scope, &child_block, merges);
701}678}
...@@ -886,8 +863,30 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError...@@ -886,8 +863,30 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
886 },863 },
887 .body = undefined,864 .body = undefined,
888 };865 };
866 // If this is the top of the inline/comptime call stack, we use this data.
867 // Otherwise we pass on the shared data from the parent scope.
868 var shared_inlining = Scope.Block.Inlining.Shared{
869 .branch_count = 0,
870 .branch_quota = 1000,
871 .caller = b.func,
872 };
873 // This one is shared among sub-blocks within the same callee, but not
874 // shared among the entire inline/comptime call stack.
875 var inlining = Scope.Block.Inlining{
876 .shared = if (b.inlining) |inlining| inlining.shared else &shared_inlining,
877 .param_index = 0,
878 .casted_args = casted_args,
879 .merges = .{
880 .results = .{},
881 .block_inst = block_inst,
882 },
883 };
884 var inst_table = Scope.Block.InstTable.init(mod.gpa);
885 defer inst_table.deinit();
886
889 var child_block: Scope.Block = .{887 var child_block: Scope.Block = .{
890 .parent = null,888 .parent = null,
889 .inst_table = &inst_table,
891 .func = module_fn,890 .func = module_fn,
892 // Note that we pass the caller's Decl, not the callee. This causes891 // Note that we pass the caller's Decl, not the callee. This causes
893 // compile errors to be attached (correctly) to the caller's Decl.892 // compile errors to be attached (correctly) to the caller's Decl.
...@@ -895,16 +894,7 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError...@@ -895,16 +894,7 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
895 .instructions = .{},894 .instructions = .{},
896 .arena = scope.arena(),895 .arena = scope.arena(),
897 .label = null,896 .label = null,
898 // TODO @as here is working around a stage1 miscompilation bug :(897 .inlining = &inlining,
899 .inlining = @as(?Scope.Block.Inlining, Scope.Block.Inlining{
900 .caller = b.func,
901 .param_index = 0,
902 .casted_args = casted_args,
903 .merges = .{
904 .results = .{},
905 .block_inst = block_inst,
906 },
907 }),
908 .is_comptime = is_comptime_call,898 .is_comptime = is_comptime_call,
909 };899 };
910 const merges = &child_block.inlining.?.merges;900 const merges = &child_block.inlining.?.merges;
...@@ -912,11 +902,19 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError...@@ -912,11 +902,19 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
912 defer child_block.instructions.deinit(mod.gpa);902 defer child_block.instructions.deinit(mod.gpa);
913 defer merges.results.deinit(mod.gpa);903 defer merges.results.deinit(mod.gpa);
914904
905 try mod.emitBackwardBranch(&child_block, inst.base.src);
906
915 // This will have return instructions analyzed as break instructions to907 // This will have return instructions analyzed as break instructions to
916 // the block_inst above.908 // the block_inst above.
917 try analyzeBody(mod, &child_block.base, module_fn.zir);909 try analyzeBody(mod, &child_block, module_fn.zir);
918910
919 return analyzeBlockBody(mod, scope, &child_block, merges);911 const result = try analyzeBlockBody(mod, scope, &child_block, merges);
912 if (result.castTag(.constant)) |constant| {
913 log.debug("inline call resulted in {}", .{constant.val});
914 } else {
915 log.debug("inline call resulted in {}", .{result});
916 }
917 return result;
920 }918 }
921919
922 return mod.addCall(b, inst.base.src, ret_type, func, casted_args);920 return mod.addCall(b, inst.base.src, ret_type, func, casted_args);
...@@ -1393,17 +1391,17 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In...@@ -1393,17 +1391,17 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
1393 const item = try mod.resolveConstValue(scope, casted);1391 const item = try mod.resolveConstValue(scope, casted);
13941392
1395 if (target_val.eql(item)) {1393 if (target_val.eql(item)) {
1396 try analyzeBody(mod, scope, case.body);1394 try analyzeBody(mod, scope.cast(Scope.Block).?, case.body);
1397 return mod.constNoReturn(scope, inst.base.src);1395 return mod.constNoReturn(scope, inst.base.src);
1398 }1396 }
1399 }1397 }
1400 try analyzeBody(mod, scope, inst.positionals.else_body);1398 try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body);
1401 return mod.constNoReturn(scope, inst.base.src);1399 return mod.constNoReturn(scope, inst.base.src);
1402 }1400 }
14031401
1404 if (inst.positionals.cases.len == 0) {1402 if (inst.positionals.cases.len == 0) {
1405 // no cases just analyze else_branch1403 // no cases just analyze else_branch
1406 try analyzeBody(mod, scope, inst.positionals.else_body);1404 try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body);
1407 return mod.constNoReturn(scope, inst.base.src);1405 return mod.constNoReturn(scope, inst.base.src);
1408 }1406 }
14091407
...@@ -1412,6 +1410,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In...@@ -1412,6 +1410,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
14121410
1413 var case_block: Scope.Block = .{1411 var case_block: Scope.Block = .{
1414 .parent = parent_block,1412 .parent = parent_block,
1413 .inst_table = parent_block.inst_table,
1415 .func = parent_block.func,1414 .func = parent_block.func,
1416 .decl = parent_block.decl,1415 .decl = parent_block.decl,
1417 .instructions = .{},1416 .instructions = .{},
...@@ -1429,7 +1428,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In...@@ -1429,7 +1428,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
1429 const casted = try mod.coerce(scope, target.ty, resolved);1428 const casted = try mod.coerce(scope, target.ty, resolved);
1430 const item = try mod.resolveConstValue(scope, casted);1429 const item = try mod.resolveConstValue(scope, casted);
14311430
1432 try analyzeBody(mod, &case_block.base, case.body);1431 try analyzeBody(mod, &case_block, case.body);
14331432
1434 cases[i] = .{1433 cases[i] = .{
1435 .item = item,1434 .item = item,
...@@ -1438,7 +1437,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In...@@ -1438,7 +1437,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
1438 }1437 }
14391438
1440 case_block.instructions.items.len = 0;1439 case_block.instructions.items.len = 0;
1441 try analyzeBody(mod, &case_block.base, inst.positionals.else_body);1440 try analyzeBody(mod, &case_block, inst.positionals.else_body);
14421441
1443 const else_body: ir.Body = .{1442 const else_body: ir.Body = .{
1444 .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items),1443 .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items),
...@@ -1756,24 +1755,26 @@ fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir...@@ -1756,24 +1755,26 @@ fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir
1756 }1755 }
1757 const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt;1756 const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt;
17581757
1759 const value = try switch (inst.base.tag) {1758 const value = switch (inst.base.tag) {
1760 .add => blk: {1759 .add => blk: {
1761 const val = if (is_int)1760 const val = if (is_int)
1762 Module.intAdd(scope.arena(), lhs_val, rhs_val)1761 try Module.intAdd(scope.arena(), lhs_val, rhs_val)
1763 else1762 else
1764 mod.floatAdd(scope, res_type, inst.base.src, lhs_val, rhs_val);1763 try mod.floatAdd(scope, res_type, inst.base.src, lhs_val, rhs_val);
1765 break :blk val;1764 break :blk val;
1766 },1765 },
1767 .sub => blk: {1766 .sub => blk: {
1768 const val = if (is_int)1767 const val = if (is_int)
1769 Module.intSub(scope.arena(), lhs_val, rhs_val)1768 try Module.intSub(scope.arena(), lhs_val, rhs_val)
1770 else1769 else
1771 mod.floatSub(scope, res_type, inst.base.src, lhs_val, rhs_val);1770 try mod.floatSub(scope, res_type, inst.base.src, lhs_val, rhs_val);
1772 break :blk val;1771 break :blk val;
1773 },1772 },
1774 else => return mod.fail(scope, inst.base.src, "TODO Implement arithmetic operand '{s}'", .{@tagName(inst.base.tag)}),1773 else => return mod.fail(scope, inst.base.src, "TODO Implement arithmetic operand '{s}'", .{@tagName(inst.base.tag)}),
1775 };1774 };
17761775
1776 log.debug("{s}({}, {}) result: {}", .{ @tagName(inst.base.tag), lhs_val, rhs_val, value });
1777
1777 return mod.constInst(scope, inst.base.src, .{1778 return mod.constInst(scope, inst.base.src, .{
1778 .ty = res_type,1779 .ty = res_type,
1779 .val = value,1780 .val = value,
...@@ -1942,16 +1943,17 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE...@@ -1942,16 +1943,17 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
1942 const uncasted_cond = try resolveInst(mod, scope, inst.positionals.condition);1943 const uncasted_cond = try resolveInst(mod, scope, inst.positionals.condition);
1943 const cond = try mod.coerce(scope, Type.initTag(.bool), uncasted_cond);1944 const cond = try mod.coerce(scope, Type.initTag(.bool), uncasted_cond);
19441945
1946 const parent_block = scope.cast(Scope.Block).?;
1947
1945 if (try mod.resolveDefinedValue(scope, cond)) |cond_val| {1948 if (try mod.resolveDefinedValue(scope, cond)) |cond_val| {
1946 const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body;1949 const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body;
1947 try analyzeBody(mod, scope, body.*);1950 try analyzeBody(mod, parent_block, body.*);
1948 return mod.constNoReturn(scope, inst.base.src);1951 return mod.constNoReturn(scope, inst.base.src);
1949 }1952 }
19501953
1951 const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);
1952
1953 var true_block: Scope.Block = .{1954 var true_block: Scope.Block = .{
1954 .parent = parent_block,1955 .parent = parent_block,
1956 .inst_table = parent_block.inst_table,
1955 .func = parent_block.func,1957 .func = parent_block.func,
1956 .decl = parent_block.decl,1958 .decl = parent_block.decl,
1957 .instructions = .{},1959 .instructions = .{},
...@@ -1960,10 +1962,11 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE...@@ -1960,10 +1962,11 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
1960 .is_comptime = parent_block.is_comptime,1962 .is_comptime = parent_block.is_comptime,
1961 };1963 };
1962 defer true_block.instructions.deinit(mod.gpa);1964 defer true_block.instructions.deinit(mod.gpa);
1963 try analyzeBody(mod, &true_block.base, inst.positionals.then_body);1965 try analyzeBody(mod, &true_block, inst.positionals.then_body);
19641966
1965 var false_block: Scope.Block = .{1967 var false_block: Scope.Block = .{
1966 .parent = parent_block,1968 .parent = parent_block,
1969 .inst_table = parent_block.inst_table,
1967 .func = parent_block.func,1970 .func = parent_block.func,
1968 .decl = parent_block.decl,1971 .decl = parent_block.decl,
1969 .instructions = .{},1972 .instructions = .{},
...@@ -1972,7 +1975,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE...@@ -1972,7 +1975,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
1972 .is_comptime = parent_block.is_comptime,1975 .is_comptime = parent_block.is_comptime,
1973 };1976 };
1974 defer false_block.instructions.deinit(mod.gpa);1977 defer false_block.instructions.deinit(mod.gpa);
1975 try analyzeBody(mod, &false_block.base, inst.positionals.else_body);1978 try analyzeBody(mod, &false_block, inst.positionals.else_body);
19761979
1977 const then_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) };1980 const then_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) };
1978 const else_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) };1981 const else_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) };
...@@ -1998,7 +2001,7 @@ fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!...@@ -1998,7 +2001,7 @@ fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!
1998 const operand = try resolveInst(mod, scope, inst.positionals.operand);2001 const operand = try resolveInst(mod, scope, inst.positionals.operand);
1999 const b = try mod.requireFunctionBlock(scope, inst.base.src);2002 const b = try mod.requireFunctionBlock(scope, inst.base.src);
20002003
2001 if (b.inlining) |*inlining| {2004 if (b.inlining) |inlining| {
2002 // We are inlining a function call; rewrite the `ret` as a `break`.2005 // We are inlining a function call; rewrite the `ret` as a `break`.
2003 try inlining.merges.results.append(mod.gpa, operand);2006 try inlining.merges.results.append(mod.gpa, operand);
2004 return mod.addBr(b, inst.base.src, inlining.merges.block_inst, operand);2007 return mod.addBr(b, inst.base.src, inlining.merges.block_inst, operand);
...@@ -2009,7 +2012,7 @@ fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!...@@ -2009,7 +2012,7 @@ fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!
20092012
2010fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {2013fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
2011 const b = try mod.requireFunctionBlock(scope, inst.base.src);2014 const b = try mod.requireFunctionBlock(scope, inst.base.src);
2012 if (b.inlining) |*inlining| {2015 if (b.inlining) |inlining| {
2013 // We are inlining a function call; rewrite the `retvoid` as a `breakvoid`.2016 // We are inlining a function call; rewrite the `retvoid` as a `breakvoid`.
2014 const void_inst = try mod.constVoid(scope, inst.base.src);2017 const void_inst = try mod.constVoid(scope, inst.base.src);
2015 try inlining.merges.results.append(mod.gpa, void_inst);2018 try inlining.merges.results.append(mod.gpa, void_inst);
test/stage2/test.zig+47-1
...@@ -27,7 +27,6 @@ const wasi = std.zig.CrossTarget{...@@ -27,7 +27,6 @@ const wasi = std.zig.CrossTarget{
27};27};
2828
29pub fn addCases(ctx: *TestContext) !void {29pub fn addCases(ctx: *TestContext) !void {
30 try @import("zir.zig").addCases(ctx);
31 try @import("cbe.zig").addCases(ctx);30 try @import("cbe.zig").addCases(ctx);
32 try @import("spu-ii.zig").addCases(ctx);31 try @import("spu-ii.zig").addCases(ctx);
33 try @import("arm.zig").addCases(ctx);32 try @import("arm.zig").addCases(ctx);
...@@ -1430,4 +1429,51 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1430,4 +1429,51 @@ pub fn addCases(ctx: *TestContext) !void {
1430 "",1429 "",
1431 );1430 );
1432 }1431 }
1432 {
1433 var case = ctx.exe("recursive inline function", linux_x64);
1434 case.addCompareOutput(
1435 \\export fn _start() noreturn {
1436 \\ const y = fibonacci(7);
1437 \\ exit(y - 21);
1438 \\}
1439 \\
1440 \\inline fn fibonacci(n: usize) usize {
1441 \\ if (n <= 2) return n;
1442 \\ return fibonacci(n - 2) + fibonacci(n - 1);
1443 \\}
1444 \\
1445 \\fn exit(code: usize) noreturn {
1446 \\ asm volatile ("syscall"
1447 \\ :
1448 \\ : [number] "{rax}" (231),
1449 \\ [arg1] "{rdi}" (code)
1450 \\ : "rcx", "r11", "memory"
1451 \\ );
1452 \\ unreachable;
1453 \\}
1454 ,
1455 "",
1456 );
1457 case.addError(
1458 \\export fn _start() noreturn {
1459 \\ const y = fibonacci(999);
1460 \\ exit(y - 21);
1461 \\}
1462 \\
1463 \\inline fn fibonacci(n: usize) usize {
1464 \\ if (n <= 2) return n;
1465 \\ return fibonacci(n - 2) + fibonacci(n - 1);
1466 \\}
1467 \\
1468 \\fn exit(code: usize) noreturn {
1469 \\ asm volatile ("syscall"
1470 \\ :
1471 \\ : [number] "{rax}" (231),
1472 \\ [arg1] "{rdi}" (code)
1473 \\ : "rcx", "r11", "memory"
1474 \\ );
1475 \\ unreachable;
1476 \\}
1477 , &[_][]const u8{":8:10: error: evaluation exceeded 1000 backwards branches"});
1478 }
1433}1479}
test/stage2/zir.zig deleted-316
...@@ -1,316 +0,0 @@
1const std = @import("std");
2const TestContext = @import("../../src/test.zig").TestContext;
3// self-hosted does not yet support PE executable files / COFF object files
4// or mach-o files. So we do the ZIR transform test cases cross compiling for
5// x86_64-linux.
6const linux_x64 = std.zig.CrossTarget{
7 .cpu_arch = .x86_64,
8 .os_tag = .linux,
9};
10
11pub fn addCases(ctx: *TestContext) !void {
12 ctx.transformZIR("referencing decls which appear later in the file", linux_x64,
13 \\@void = primitive(void)
14 \\@fnty = fntype([], @void, cc=C)
15 \\
16 \\@9 = str("entry")
17 \\@11 = export(@9, "entry")
18 \\
19 \\@entry = fn(@fnty, {
20 \\ %11 = returnvoid()
21 \\})
22 ,
23 \\@void = primitive(void)
24 \\@fnty = fntype([], @void, cc=C)
25 \\@9 = declref("9__anon_0")
26 \\@9__anon_0 = str("entry")
27 \\@unnamed$4 = str("entry")
28 \\@unnamed$5 = export(@unnamed$4, "entry")
29 \\@11 = primitive(void_value)
30 \\@unnamed$7 = fntype([], @void, cc=C)
31 \\@entry = fn(@unnamed$7, {
32 \\ %0 = returnvoid() ; deaths=0b1000000000000000
33 \\}, is_inline=0)
34 \\
35 );
36 ctx.transformZIR("elemptr, add, cmp, condbr, return, breakpoint", linux_x64,
37 \\@void = primitive(void)
38 \\@usize = primitive(usize)
39 \\@fnty = fntype([], @void, cc=C)
40 \\@0 = int(0)
41 \\@1 = int(1)
42 \\@2 = int(2)
43 \\@3 = int(3)
44 \\
45 \\@entry = fn(@fnty, {
46 \\ %a = str("\x32\x08\x01\x0a")
47 \\ %a_ref = ref(%a)
48 \\ %eptr0 = elemptr(%a_ref, @0)
49 \\ %eptr1 = elemptr(%a_ref, @1)
50 \\ %eptr2 = elemptr(%a_ref, @2)
51 \\ %eptr3 = elemptr(%a_ref, @3)
52 \\ %v0 = deref(%eptr0)
53 \\ %v1 = deref(%eptr1)
54 \\ %v2 = deref(%eptr2)
55 \\ %v3 = deref(%eptr3)
56 \\ %x0 = add(%v0, %v1)
57 \\ %x1 = add(%v2, %v3)
58 \\ %result = add(%x0, %x1)
59 \\
60 \\ %expected = int(69)
61 \\ %ok = cmp_eq(%result, %expected)
62 \\ %10 = condbr(%ok, {
63 \\ %11 = returnvoid()
64 \\ }, {
65 \\ %12 = breakpoint()
66 \\ })
67 \\})
68 \\
69 \\@9 = str("entry")
70 \\@11 = export(@9, "entry")
71 ,
72 \\@void = primitive(void)
73 \\@fnty = fntype([], @void, cc=C)
74 \\@0 = int(0)
75 \\@1 = int(1)
76 \\@2 = int(2)
77 \\@3 = int(3)
78 \\@unnamed$6 = fntype([], @void, cc=C)
79 \\@entry = fn(@unnamed$6, {
80 \\ %0 = returnvoid() ; deaths=0b1000000000000000
81 \\}, is_inline=0)
82 \\@entry__anon_1 = str("2\x08\x01\n")
83 \\@9 = declref("9__anon_0")
84 \\@9__anon_0 = str("entry")
85 \\@unnamed$11 = str("entry")
86 \\@unnamed$12 = export(@unnamed$11, "entry")
87 \\@11 = primitive(void_value)
88 \\
89 );
90
91 {
92 var case = ctx.objZIR("reference cycle with compile error in the cycle", linux_x64);
93 case.addTransform(
94 \\@void = primitive(void)
95 \\@fnty = fntype([], @void, cc=C)
96 \\
97 \\@9 = str("entry")
98 \\@11 = export(@9, "entry")
99 \\
100 \\@entry = fn(@fnty, {
101 \\ %0 = call(@a, [])
102 \\ %1 = returnvoid()
103 \\})
104 \\
105 \\@a = fn(@fnty, {
106 \\ %0 = call(@b, [])
107 \\ %1 = returnvoid()
108 \\})
109 \\
110 \\@b = fn(@fnty, {
111 \\ %0 = call(@a, [])
112 \\ %1 = returnvoid()
113 \\})
114 ,
115 \\@void = primitive(void)
116 \\@fnty = fntype([], @void, cc=C)
117 \\@9 = declref("9__anon_0")
118 \\@9__anon_0 = str("entry")
119 \\@unnamed$4 = str("entry")
120 \\@unnamed$5 = export(@unnamed$4, "entry")
121 \\@11 = primitive(void_value)
122 \\@unnamed$7 = fntype([], @void, cc=C)
123 \\@entry = fn(@unnamed$7, {
124 \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001
125 \\ %1 = returnvoid() ; deaths=0b1000000000000000
126 \\}, is_inline=0)
127 \\@unnamed$9 = fntype([], @void, cc=C)
128 \\@a = fn(@unnamed$9, {
129 \\ %0 = call(@b, [], modifier=auto) ; deaths=0b1000000000000001
130 \\ %1 = returnvoid() ; deaths=0b1000000000000000
131 \\}, is_inline=0)
132 \\@unnamed$11 = fntype([], @void, cc=C)
133 \\@b = fn(@unnamed$11, {
134 \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001
135 \\ %1 = returnvoid() ; deaths=0b1000000000000000
136 \\}, is_inline=0)
137 \\
138 );
139 // Now we introduce a compile error
140 case.addError(
141 \\@void = primitive(void)
142 \\@fnty = fntype([], @void, cc=C)
143 \\
144 \\@9 = str("entry")
145 \\@11 = export(@9, "entry")
146 \\
147 \\@entry = fn(@fnty, {
148 \\ %0 = call(@a, [])
149 \\ %1 = returnvoid()
150 \\})
151 \\
152 \\@a = fn(@fnty, {
153 \\ %0 = call(@c, [])
154 \\ %1 = returnvoid()
155 \\})
156 \\
157 \\@b = str("message")
158 \\
159 \\@c = fn(@fnty, {
160 \\ %9 = compileerror(@b)
161 \\ %0 = call(@a, [])
162 \\ %1 = returnvoid()
163 \\})
164 ,
165 &[_][]const u8{
166 ":20:21: error: message",
167 },
168 );
169 // Now we remove the call to `a`. `a` and `b` form a cycle, but no entry points are
170 // referencing either of them. This tests that the cycle is detected, and the error
171 // goes away.
172 case.addTransform(
173 \\@void = primitive(void)
174 \\@fnty = fntype([], @void, cc=C)
175 \\
176 \\@9 = str("entry")
177 \\@11 = export(@9, "entry")
178 \\
179 \\@entry = fn(@fnty, {
180 \\ %0 = returnvoid()
181 \\})
182 \\
183 \\@a = fn(@fnty, {
184 \\ %0 = call(@c, [])
185 \\ %1 = returnvoid()
186 \\})
187 \\
188 \\@b = str("message")
189 \\
190 \\@c = fn(@fnty, {
191 \\ %9 = compileerror(@b)
192 \\ %0 = call(@a, [])
193 \\ %1 = returnvoid()
194 \\})
195 ,
196 \\@void = primitive(void)
197 \\@fnty = fntype([], @void, cc=C)
198 \\@9 = declref("9__anon_3")
199 \\@9__anon_3 = str("entry")
200 \\@unnamed$4 = str("entry")
201 \\@unnamed$5 = export(@unnamed$4, "entry")
202 \\@11 = primitive(void_value)
203 \\@unnamed$7 = fntype([], @void, cc=C)
204 \\@entry = fn(@unnamed$7, {
205 \\ %0 = returnvoid() ; deaths=0b1000000000000000
206 \\}, is_inline=0)
207 \\
208 );
209 }
210
211 if (std.Target.current.os.tag != .linux or
212 std.Target.current.cpu.arch != .x86_64)
213 {
214 // TODO implement self-hosted PE (.exe file) linking
215 // TODO implement more ZIR so we don't depend on x86_64-linux
216 return;
217 }
218
219 ctx.compareOutputZIR("hello world ZIR",
220 \\@noreturn = primitive(noreturn)
221 \\@void = primitive(void)
222 \\@usize = primitive(usize)
223 \\@0 = int(0)
224 \\@1 = int(1)
225 \\@2 = int(2)
226 \\@3 = int(3)
227 \\
228 \\@msg = str("Hello, world!\n")
229 \\
230 \\@start_fnty = fntype([], @noreturn, cc=Naked)
231 \\@start = fn(@start_fnty, {
232 \\ %SYS_exit_group = int(231)
233 \\ %exit_code = as(@usize, @0)
234 \\
235 \\ %syscall = str("syscall")
236 \\ %sysoutreg = str("={rax}")
237 \\ %rax = str("{rax}")
238 \\ %rdi = str("{rdi}")
239 \\ %rcx = str("rcx")
240 \\ %rdx = str("{rdx}")
241 \\ %rsi = str("{rsi}")
242 \\ %r11 = str("r11")
243 \\ %memory = str("memory")
244 \\
245 \\ %SYS_write = as(@usize, @1)
246 \\ %STDOUT_FILENO = as(@usize, @1)
247 \\
248 \\ %msg_addr = ptrtoint(@msg)
249 \\
250 \\ %len_name = str("len")
251 \\ %msg_len_ptr = fieldptr(@msg, %len_name)
252 \\ %msg_len = deref(%msg_len_ptr)
253 \\ %rc_write = asm(%syscall, @usize,
254 \\ volatile=1,
255 \\ output=%sysoutreg,
256 \\ inputs=[%rax, %rdi, %rsi, %rdx],
257 \\ clobbers=[%rcx, %r11, %memory],
258 \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
259 \\
260 \\ %rc_exit = asm(%syscall, @usize,
261 \\ volatile=1,
262 \\ output=%sysoutreg,
263 \\ inputs=[%rax, %rdi],
264 \\ clobbers=[%rcx, %r11, %memory],
265 \\ args=[%SYS_exit_group, %exit_code])
266 \\
267 \\ %99 = unreachable()
268 \\});
269 \\
270 \\@9 = str("_start")
271 \\@11 = export(@9, "start")
272 ,
273 \\Hello, world!
274 \\
275 );
276
277 ctx.compareOutputZIR("function call with no args no return value",
278 \\@noreturn = primitive(noreturn)
279 \\@void = primitive(void)
280 \\@usize = primitive(usize)
281 \\@0 = int(0)
282 \\@1 = int(1)
283 \\@2 = int(2)
284 \\@3 = int(3)
285 \\
286 \\@exit0_fnty = fntype([], @noreturn)
287 \\@exit0 = fn(@exit0_fnty, {
288 \\ %SYS_exit_group = int(231)
289 \\ %exit_code = as(@usize, @0)
290 \\
291 \\ %syscall = str("syscall")
292 \\ %sysoutreg = str("={rax}")
293 \\ %rax = str("{rax}")
294 \\ %rdi = str("{rdi}")
295 \\ %rcx = str("rcx")
296 \\ %r11 = str("r11")
297 \\ %memory = str("memory")
298 \\
299 \\ %rc = asm(%syscall, @usize,
300 \\ volatile=1,
301 \\ output=%sysoutreg,
302 \\ inputs=[%rax, %rdi],
303 \\ clobbers=[%rcx, %r11, %memory],
304 \\ args=[%SYS_exit_group, %exit_code])
305 \\
306 \\ %99 = unreachable()
307 \\});
308 \\
309 \\@start_fnty = fntype([], @noreturn, cc=Naked)
310 \\@start = fn(@start_fnty, {
311 \\ %0 = call(@exit0, [])
312 \\})
313 \\@9 = str("_start")
314 \\@11 = export(@9, "start")
315 , "");
316}