authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-02 22:01:51-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-01-02 22:01:51-08:00
logd8f3f14532c4b5d65377efaef015c3855137dccf
treed0927df77323d64bff52501b50ef8543a077d4d8
parent3d151fbfc8db71f87ee84dd33c49910584708a04
parent654832253a7857e78aab85e28ed09fb16b632dd2
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7647 from ziglang/stage2-comptime-fn-call

stage2: comptime function calls and inline function calls

17 files changed, 884 insertions(+), 590 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+10-5
...@@ -1459,24 +1459,29 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -1459,24 +1459,29 @@ 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.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.state != .in_progress);
1466 continue;1466 continue;
1467 },1467 },
1468 error.OutOfMemory => return error.OutOfMemory,1468 error.OutOfMemory => return error.OutOfMemory,
1469 },1469 },
1470 .in_progress => unreachable,1470 .in_progress => unreachable,
1471 .inline_only => unreachable, // don't queue work for this
1471 .sema_failure, .dependency_failure => continue,1472 .sema_failure, .dependency_failure => continue,
1472 .success => {},1473 .success => {},
1473 }1474 }
1474 // Here we tack on additional allocations to the Decl's arena. The allocations are1475 // Here we tack on additional allocations to the Decl's arena. The allocations
1475 // lifetime annotations in the ZIR.1476 // are lifetime annotations in the ZIR.
1476 var decl_arena = decl.typed_value.most_recent.arena.?.promote(module.gpa);1477 var decl_arena = decl.typed_value.most_recent.arena.?.promote(module.gpa);
1477 defer decl.typed_value.most_recent.arena.?.* = decl_arena.state;1478 defer decl.typed_value.most_recent.arena.?.* = decl_arena.state;
1478 log.debug("analyze liveness of {s}\n", .{decl.name});1479 log.debug("analyze liveness of {s}\n", .{decl.name});
1479 try liveness.analyze(module.gpa, &decl_arena.allocator, func.analysis.success);1480 try liveness.analyze(module.gpa, &decl_arena.allocator, func.body);
1481
1482 if (std.builtin.mode == .Debug and self.verbose_ir) {
1483 func.dump(module.*);
1484 }
1480 }1485 }
14811486
1482 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());1487 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
src/Module.zig+188-76
...@@ -268,6 +268,11 @@ pub const Decl = struct {...@@ -268,6 +268,11 @@ pub const Decl = struct {
268 }268 }
269 }269 }
270270
271 /// Asserts that the `Decl` is part of AST and not ZIRModule.
272 pub fn getFileScope(self: *Decl) *Scope.File {
273 return self.scope.cast(Scope.Container).?.file_scope;
274 }
275
271 fn removeDependant(self: *Decl, other: *Decl) void {276 fn removeDependant(self: *Decl, other: *Decl) void {
272 self.dependants.removeAssertDiscard(other);277 self.dependants.removeAssertDiscard(other);
273 }278 }
...@@ -281,46 +286,32 @@ pub const Decl = struct {...@@ -281,46 +286,32 @@ pub const Decl = struct {
281/// Extern functions do not have this data structure; they are represented by286/// Extern functions do not have this data structure; they are represented by
282/// the `Decl` only, with a `Value` tag of `extern_fn`.287/// the `Decl` only, with a `Value` tag of `extern_fn`.
283pub const Fn = struct {288pub const Fn = struct {
284 /// This memory owned by the Decl's TypedValue.Managed arena allocator.289 owner_decl: *Decl,
285 analysis: union(enum) {290 /// Contains un-analyzed ZIR instructions generated from Zig source AST.
286 queued: *ZIR,291 /// Even after we finish analysis, the ZIR is kept in memory, so that
292 /// comptime and inline function calls can happen.
293 zir: zir.Module.Body,
294 /// undefined unless analysis state is `success`.
295 body: Body,
296 state: Analysis,
297
298 pub const Analysis = enum {
299 queued,
300 /// This function intentionally only has ZIR generated because it is marked
301 /// inline, which means no runtime version of the function will be generated.
302 inline_only,
287 in_progress,303 in_progress,
288 /// There will be a corresponding ErrorMsg in Module.failed_decls304 /// There will be a corresponding ErrorMsg in Module.failed_decls
289 sema_failure,305 sema_failure,
290 /// This Fn might be OK but it depends on another Decl which did not successfully complete306 /// This Fn might be OK but it depends on another Decl which did not
291 /// semantic analysis.307 /// successfully complete semantic analysis.
292 dependency_failure,308 dependency_failure,
293 success: Body,309 success,
294 },
295 owner_decl: *Decl,
296
297 /// This memory is temporary and points to stack memory for the duration
298 /// of Fn analysis.
299 pub const Analysis = struct {
300 inner_block: Scope.Block,
301 };
302
303 /// Contains un-analyzed ZIR instructions generated from Zig source AST.
304 pub const ZIR = struct {
305 body: zir.Module.Body,
306 arena: std.heap.ArenaAllocator.State,
307 };310 };
308311
309 /// For debugging purposes.312 /// For debugging purposes.
310 pub fn dump(self: *Fn, mod: Module) void {313 pub fn dump(self: *Fn, mod: Module) void {
311 std.debug.print("Module.Function(name={s}) ", .{self.owner_decl.name});314 zir.dumpFn(mod, self);
312 switch (self.analysis) {
313 .queued => {
314 std.debug.print("queued\n", .{});
315 },
316 .in_progress => {
317 std.debug.print("in_progress\n", .{});
318 },
319 else => {
320 std.debug.print("\n", .{});
321 zir.dumpFn(mod, self);
322 },
323 }
324 }315 }
325};316};
326317
...@@ -761,21 +752,60 @@ pub const Scope = struct {...@@ -761,21 +752,60 @@ pub const Scope = struct {
761 /// during semantic analysis of the block.752 /// during semantic analysis of the block.
762 pub const Block = struct {753 pub const Block = struct {
763 pub const base_tag: Tag = .block;754 pub const base_tag: Tag = .block;
755
764 base: Scope = Scope{ .tag = base_tag },756 base: Scope = Scope{ .tag = base_tag },
765 parent: ?*Block,757 parent: ?*Block,
758 /// Maps ZIR to TZIR. Shared to sub-blocks.
759 inst_table: *InstTable,
766 func: ?*Fn,760 func: ?*Fn,
767 decl: *Decl,761 decl: *Decl,
768 instructions: ArrayListUnmanaged(*Inst),762 instructions: ArrayListUnmanaged(*Inst),
769 /// Points to the arena allocator of DeclAnalysis763 /// Points to the arena allocator of DeclAnalysis
770 arena: *Allocator,764 arena: *Allocator,
771 label: ?Label = null,765 label: ?Label = null,
766 inlining: ?*Inlining,
772 is_comptime: bool,767 is_comptime: bool,
773768
769 pub const InstTable = std.AutoHashMap(*zir.Inst, *Inst);
770
771 /// This `Block` maps a block ZIR instruction to the corresponding
772 /// TZIR instruction for break instruction analysis.
774 pub const Label = struct {773 pub const Label = struct {
775 zir_block: *zir.Inst.Block,774 zir_block: *zir.Inst.Block,
775 merges: Merges,
776 };
777
778 /// This `Block` indicates that an inline function call is happening
779 /// and return instructions should be analyzed as a break instruction
780 /// to this TZIR block instruction.
781 /// It is shared among all the blocks in an inline or comptime called
782 /// function.
783 pub const Inlining = struct {
784 /// Shared state among the entire inline/comptime call stack.
785 shared: *Shared,
786 /// We use this to count from 0 so that arg instructions know
787 /// which parameter index they are, without having to store
788 /// a parameter index with each arg instruction.
789 param_index: usize,
790 casted_args: []*Inst,
791 merges: Merges,
792
793 pub const Shared = struct {
794 caller: ?*Fn,
795 branch_count: u64,
796 branch_quota: u64,
797 };
798 };
799
800 pub const Merges = struct {
776 results: ArrayListUnmanaged(*Inst),801 results: ArrayListUnmanaged(*Inst),
777 block_inst: *Inst.Block,802 block_inst: *Inst.Block,
778 };803 };
804
805 /// For debugging purposes.
806 pub fn dump(self: *Block, mod: Module) void {
807 zir.dumpBlock(mod, self);
808 }
779 };809 };
780810
781 /// This is a temporary structure, references to it are valid only811 /// This is a temporary structure, references to it are valid only
...@@ -992,11 +1022,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -992,11 +1022,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
992 defer tracy.end();1022 defer tracy.end();
9931023
994 const container_scope = decl.scope.cast(Scope.Container).?;1024 const container_scope = decl.scope.cast(Scope.Container).?;
995 const tree = try self.getAstTree(container_scope);1025 const tree = try self.getAstTree(container_scope.file_scope);
996 const ast_node = tree.root_node.decls()[decl.src_index];1026 const ast_node = tree.root_node.decls()[decl.src_index];
997 switch (ast_node.tag) {1027 switch (ast_node.tag) {
998 .FnProto => {1028 .FnProto => {
999 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node);1029 const fn_proto = ast_node.castTag(.FnProto).?;
10001030
1001 decl.analysis = .in_progress;1031 decl.analysis = .in_progress;
10021032
...@@ -1062,7 +1092,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1062,7 +1092,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1062 .param_types = param_types,1092 .param_types = param_types,
1063 }, .{});1093 }, .{});
10641094
1065 if (self.comp.verbose_ir) {1095 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {
1066 zir.dumpZir(self.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {};1096 zir.dumpZir(self.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {};
1067 }1097 }
10681098
...@@ -1071,12 +1101,17 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1071,12 +1101,17 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1071 errdefer decl_arena.deinit();1101 errdefer decl_arena.deinit();
1072 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);
10731103
1104 var inst_table = Scope.Block.InstTable.init(self.gpa);
1105 defer inst_table.deinit();
1106
1074 var block_scope: Scope.Block = .{1107 var block_scope: Scope.Block = .{
1075 .parent = null,1108 .parent = null,
1109 .inst_table = &inst_table,
1076 .func = null,1110 .func = null,
1077 .decl = decl,1111 .decl = decl,
1078 .instructions = .{},1112 .instructions = .{},
1079 .arena = &decl_arena.allocator,1113 .arena = &decl_arena.allocator,
1114 .inlining = null,
1080 .is_comptime = false,1115 .is_comptime = false,
1081 };1116 };
1082 defer block_scope.instructions.deinit(self.gpa);1117 defer block_scope.instructions.deinit(self.gpa);
...@@ -1113,14 +1148,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1113,14 +1148,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1113 const new_func = try decl_arena.allocator.create(Fn);1148 const new_func = try decl_arena.allocator.create(Fn);
1114 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);1149 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
11151150
1116 const fn_zir = blk: {1151 const fn_zir: zir.Module.Body = blk: {
1117 // This scope's arena memory is discarded after the ZIR generation1152 // We put the ZIR inside the Decl arena.
1118 // pass completes, and semantic analysis of it completes.
1119 var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
1120 errdefer gen_scope_arena.deinit();
1121 var gen_scope: Scope.GenZIR = .{1153 var gen_scope: Scope.GenZIR = .{
1122 .decl = decl,1154 .decl = decl,
1123 .arena = &gen_scope_arena.allocator,1155 .arena = &decl_arena.allocator,
1124 .parent = decl.scope,1156 .parent = decl.scope,
1125 };1157 };
1126 defer gen_scope.instructions.deinit(self.gpa);1158 defer gen_scope.instructions.deinit(self.gpa);
...@@ -1131,8 +1163,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1131,8 +1163,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1131 for (fn_proto.params()) |param, i| {1163 for (fn_proto.params()) |param, i| {
1132 const name_token = param.name_token.?;1164 const name_token = param.name_token.?;
1133 const src = tree.token_locs[name_token].start;1165 const src = tree.token_locs[name_token].start;
1134 const param_name = tree.tokenSlice(name_token); // TODO: call identifierTokenString1166 const param_name = try self.identifierTokenString(&gen_scope.base, name_token);
1135 const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg);1167 const arg = try decl_arena.allocator.create(zir.Inst.Arg);
1136 arg.* = .{1168 arg.* = .{
1137 .base = .{1169 .base = .{
1138 .tag = .arg,1170 .tag = .arg,
...@@ -1144,7 +1176,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1144,7 +1176,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1144 .kw_args = .{},1176 .kw_args = .{},
1145 };1177 };
1146 gen_scope.instructions.items[i] = &arg.base;1178 gen_scope.instructions.items[i] = &arg.base;
1147 const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVal);1179 const sub_scope = try decl_arena.allocator.create(Scope.LocalVal);
1148 sub_scope.* = .{1180 sub_scope.* = .{
1149 .parent = params_scope,1181 .parent = params_scope,
1150 .gen_zir = &gen_scope,1182 .gen_zir = &gen_scope,
...@@ -1165,22 +1197,29 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1165,22 +1197,29 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1165 _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid);1197 _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid);
1166 }1198 }
11671199
1168 if (self.comp.verbose_ir) {1200 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {
1169 zir.dumpZir(self.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {};1201 zir.dumpZir(self.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {};
1170 }1202 }
11711203
1172 const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR);1204 break :blk .{
1173 fn_zir.* = .{1205 .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),
1174 .body = .{
1175 .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),
1176 },
1177 .arena = gen_scope_arena.state,
1178 };1206 };
1179 break :blk fn_zir;
1180 };1207 };
11811208
1209 const is_inline = blk: {
1210 if (fn_proto.getExternExportInlineToken()) |maybe_inline_token| {
1211 if (tree.token_ids[maybe_inline_token] == .Keyword_inline) {
1212 break :blk true;
1213 }
1214 }
1215 break :blk false;
1216 };
1217 const anal_state = ([2]Fn.Analysis{ .queued, .inline_only })[@boolToInt(is_inline)];
1218
1182 new_func.* = .{1219 new_func.* = .{
1183 .analysis = .{ .queued = fn_zir },1220 .state = anal_state,
1221 .zir = fn_zir,
1222 .body = undefined,
1184 .owner_decl = decl,1223 .owner_decl = decl,
1185 };1224 };
1186 fn_payload.* = .{1225 fn_payload.* = .{
...@@ -1189,11 +1228,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1189,11 +1228,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1189 };1228 };
11901229
1191 var prev_type_has_bits = false;1230 var prev_type_has_bits = false;
1231 var prev_is_inline = false;
1192 var type_changed = true;1232 var type_changed = true;
11931233
1194 if (decl.typedValueManaged()) |tvm| {1234 if (decl.typedValueManaged()) |tvm| {
1195 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();1235 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
1196 type_changed = !tvm.typed_value.ty.eql(fn_type);1236 type_changed = !tvm.typed_value.ty.eql(fn_type);
1237 if (tvm.typed_value.val.castTag(.function)) |payload| {
1238 const prev_func = payload.data;
1239 prev_is_inline = prev_func.state == .inline_only;
1240 }
11971241
1198 tvm.deinit(self.gpa);1242 tvm.deinit(self.gpa);
1199 }1243 }
...@@ -1211,18 +1255,26 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1211,18 +1255,26 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1211 decl.analysis = .complete;1255 decl.analysis = .complete;
1212 decl.generation = self.generation;1256 decl.generation = self.generation;
12131257
1214 if (fn_type.hasCodeGenBits()) {1258 if (!is_inline and fn_type.hasCodeGenBits()) {
1215 // We don't fully codegen the decl until later, but we do need to reserve a global1259 // We don't fully codegen the decl until later, but we do need to reserve a global
1216 // offset table index for it. This allows us to codegen decls out of dependency order,1260 // offset table index for it. This allows us to codegen decls out of dependency order,
1217 // increasing how many computations can be done in parallel.1261 // increasing how many computations can be done in parallel.
1218 try self.comp.bin_file.allocateDeclIndexes(decl);1262 try self.comp.bin_file.allocateDeclIndexes(decl);
1219 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });1263 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
1220 } else if (prev_type_has_bits) {1264 } else if (!prev_is_inline and prev_type_has_bits) {
1221 self.comp.bin_file.freeDecl(decl);1265 self.comp.bin_file.freeDecl(decl);
1222 }1266 }
12231267
1224 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {1268 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
1225 if (tree.token_ids[maybe_export_token] == .Keyword_export) {1269 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1270 if (is_inline) {
1271 return self.failTok(
1272 &block_scope.base,
1273 maybe_export_token,
1274 "export of inline function",
1275 .{},
1276 );
1277 }
1226 const export_src = tree.token_locs[maybe_export_token].start;1278 const export_src = tree.token_locs[maybe_export_token].start;
1227 const name_loc = tree.token_locs[fn_proto.getNameToken().?];1279 const name_loc = tree.token_locs[fn_proto.getNameToken().?];
1228 const name = tree.tokenSliceLoc(name_loc);1280 const name = tree.tokenSliceLoc(name_loc);
...@@ -1230,7 +1282,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1230,7 +1282,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1230 try self.analyzeExport(&block_scope.base, export_src, name, decl);1282 try self.analyzeExport(&block_scope.base, export_src, name, decl);
1231 }1283 }
1232 }1284 }
1233 return type_changed;1285 return type_changed or is_inline != prev_is_inline;
1234 },1286 },
1235 .VarDecl => {1287 .VarDecl => {
1236 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node);1288 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node);
...@@ -1242,12 +1294,17 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1242,12 +1294,17 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1242 errdefer decl_arena.deinit();1294 errdefer decl_arena.deinit();
1243 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);
12441296
1297 var decl_inst_table = Scope.Block.InstTable.init(self.gpa);
1298 defer decl_inst_table.deinit();
1299
1245 var block_scope: Scope.Block = .{1300 var block_scope: Scope.Block = .{
1246 .parent = null,1301 .parent = null,
1302 .inst_table = &decl_inst_table,
1247 .func = null,1303 .func = null,
1248 .decl = decl,1304 .decl = decl,
1249 .instructions = .{},1305 .instructions = .{},
1250 .arena = &decl_arena.allocator,1306 .arena = &decl_arena.allocator,
1307 .inlining = null,
1251 .is_comptime = true,1308 .is_comptime = true,
1252 };1309 };
1253 defer block_scope.instructions.deinit(self.gpa);1310 defer block_scope.instructions.deinit(self.gpa);
...@@ -1303,23 +1360,30 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1303,23 +1360,30 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
13031360
1304 const src = tree.token_locs[init_node.firstToken()].start;1361 const src = tree.token_locs[init_node.firstToken()].start;
1305 const init_inst = try astgen.expr(self, &gen_scope.base, init_result_loc, init_node);1362 const init_inst = try astgen.expr(self, &gen_scope.base, init_result_loc, init_node);
1306 if (self.comp.verbose_ir) {1363 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {
1307 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 {};
1308 }1365 }
13091366
1367 var var_inst_table = Scope.Block.InstTable.init(self.gpa);
1368 defer var_inst_table.deinit();
1369
1310 var inner_block: Scope.Block = .{1370 var inner_block: Scope.Block = .{
1311 .parent = null,1371 .parent = null,
1372 .inst_table = &var_inst_table,
1312 .func = null,1373 .func = null,
1313 .decl = decl,1374 .decl = decl,
1314 .instructions = .{},1375 .instructions = .{},
1315 .arena = &gen_scope_arena.allocator,1376 .arena = &gen_scope_arena.allocator,
1377 .inlining = null,
1316 .is_comptime = true,1378 .is_comptime = true,
1317 };1379 };
1318 defer inner_block.instructions.deinit(self.gpa);1380 defer inner_block.instructions.deinit(self.gpa);
1319 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 });
13201384
1321 // The result location guarantees the type coercion.1385 // The result location guarantees the type coercion.
1322 const analyzed_init_inst = init_inst.analyzed_inst.?;1386 const analyzed_init_inst = var_inst_table.get(init_inst).?;
1323 // 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.
1324 const val = analyzed_init_inst.value().?;1388 const val = analyzed_init_inst.value().?;
13251389
...@@ -1347,7 +1411,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1347,7 +1411,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1347 .val = Value.initTag(.type_type),1411 .val = Value.initTag(.type_type),
1348 });1412 });
1349 const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node);1413 const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node);
1350 if (self.comp.verbose_ir) {1414 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {
1351 zir.dumpZir(self.gpa, "var_type", decl.name, type_scope.instructions.items) catch {};1415 zir.dumpZir(self.gpa, "var_type", decl.name, type_scope.instructions.items) catch {};
1352 }1416 }
13531417
...@@ -1423,21 +1487,26 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1423,21 +1487,26 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1423 defer gen_scope.instructions.deinit(self.gpa);1487 defer gen_scope.instructions.deinit(self.gpa);
14241488
1425 _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr);1489 _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr);
1426 if (self.comp.verbose_ir) {1490 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {
1427 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 {};
1428 }1492 }
14291493
1494 var inst_table = Scope.Block.InstTable.init(self.gpa);
1495 defer inst_table.deinit();
1496
1430 var block_scope: Scope.Block = .{1497 var block_scope: Scope.Block = .{
1431 .parent = null,1498 .parent = null,
1499 .inst_table = &inst_table,
1432 .func = null,1500 .func = null,
1433 .decl = decl,1501 .decl = decl,
1434 .instructions = .{},1502 .instructions = .{},
1435 .arena = &analysis_arena.allocator,1503 .arena = &analysis_arena.allocator,
1504 .inlining = null,
1436 .is_comptime = true,1505 .is_comptime = true,
1437 };1506 };
1438 defer block_scope.instructions.deinit(self.gpa);1507 defer block_scope.instructions.deinit(self.gpa);
14391508
1440 _ = try zir_sema.analyzeBody(self, &block_scope.base, .{1509 _ = try zir_sema.analyzeBody(self, &block_scope, .{
1441 .instructions = gen_scope.instructions.items,1510 .instructions = gen_scope.instructions.items,
1442 });1511 });
14431512
...@@ -1496,12 +1565,10 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {...@@ -1496,12 +1565,10 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
1496 }1565 }
1497}1566}
14981567
1499fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree {1568pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
1500 const tracy = trace(@src());1569 const tracy = trace(@src());
1501 defer tracy.end();1570 defer tracy.end();
15021571
1503 const root_scope = container_scope.file_scope;
1504
1505 switch (root_scope.status) {1572 switch (root_scope.status) {
1506 .never_loaded, .unloaded_success => {1573 .never_loaded, .unloaded_success => {
1507 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);1574 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
...@@ -1549,7 +1616,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void...@@ -1549,7 +1616,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void
15491616
1550 // We may be analyzing it for the first time, or this may be1617 // We may be analyzing it for the first time, or this may be
1551 // an incremental update. This code handles both cases.1618 // an incremental update. This code handles both cases.
1552 const tree = try self.getAstTree(container_scope);1619 const tree = try self.getAstTree(container_scope.file_scope);
1553 const decls = tree.root_node.decls();1620 const decls = tree.root_node.decls();
15541621
1555 try self.comp.work_queue.ensureUnusedCapacity(decls.len);1622 try self.comp.work_queue.ensureUnusedCapacity(decls.len);
...@@ -1806,25 +1873,28 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {...@@ -1806,25 +1873,28 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
1806 // Use the Decl's arena for function memory.1873 // Use the Decl's arena for function memory.
1807 var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);1874 var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
1808 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();
1809 var inner_block: Scope.Block = .{1878 var inner_block: Scope.Block = .{
1810 .parent = null,1879 .parent = null,
1880 .inst_table = &inst_table,
1811 .func = func,1881 .func = func,
1812 .decl = decl,1882 .decl = decl,
1813 .instructions = .{},1883 .instructions = .{},
1814 .arena = &arena.allocator,1884 .arena = &arena.allocator,
1885 .inlining = null,
1815 .is_comptime = false,1886 .is_comptime = false,
1816 };1887 };
1817 defer inner_block.instructions.deinit(self.gpa);1888 defer inner_block.instructions.deinit(self.gpa);
18181889
1819 const fn_zir = func.analysis.queued;1890 func.state = .in_progress;
1820 defer fn_zir.arena.promote(self.gpa).deinit();
1821 func.analysis = .{ .in_progress = {} };
1822 log.debug("set {s} to in_progress\n", .{decl.name});1891 log.debug("set {s} to in_progress\n", .{decl.name});
18231892
1824 try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body);1893 try zir_sema.analyzeBody(self, &inner_block, func.zir);
18251894
1826 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);1895 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
1827 func.analysis = .{ .success = .{ .instructions = instructions } };1896 func.state = .success;
1897 func.body = .{ .instructions = instructions };
1828 log.debug("set {s} to success\n", .{decl.name});1898 log.debug("set {s} to success\n", .{decl.name});
1829}1899}
18301900
...@@ -2321,7 +2391,7 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn...@@ -2321,7 +2391,7 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn
2321 self.ensureDeclAnalyzed(decl) catch |err| {2391 self.ensureDeclAnalyzed(decl) catch |err| {
2322 if (scope.cast(Scope.Block)) |block| {2392 if (scope.cast(Scope.Block)) |block| {
2323 if (block.func) |func| {2393 if (block.func) |func| {
2324 func.analysis = .dependency_failure;2394 func.state = .dependency_failure;
2325 } else {2395 } else {
2326 block.decl.analysis = .dependency_failure;2396 block.decl.analysis = .dependency_failure;
2327 }2397 }
...@@ -3020,11 +3090,20 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Com...@@ -3020,11 +3090,20 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Com
3020 },3090 },
3021 .block => {3091 .block => {
3022 const block = scope.cast(Scope.Block).?;3092 const block = scope.cast(Scope.Block).?;
3023 if (block.func) |func| {3093 if (block.inlining) |inlining| {
3024 func.analysis = .sema_failure;3094 if (inlining.shared.caller) |func| {
3095 func.state = .sema_failure;
3096 } else {
3097 block.decl.analysis = .sema_failure;
3098 block.decl.generation = self.generation;
3099 }
3025 } else {3100 } else {
3026 block.decl.analysis = .sema_failure;3101 if (block.func) |func| {
3027 block.decl.generation = self.generation;3102 func.state = .sema_failure;
3103 } else {
3104 block.decl.analysis = .sema_failure;
3105 block.decl.generation = self.generation;
3106 }
3028 }3107 }
3029 self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);3108 self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);
3030 },3109 },
...@@ -3380,10 +3459,12 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic...@@ -3380,10 +3459,12 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic
33803459
3381 var fail_block: Scope.Block = .{3460 var fail_block: Scope.Block = .{
3382 .parent = parent_block,3461 .parent = parent_block,
3462 .inst_table = parent_block.inst_table,
3383 .func = parent_block.func,3463 .func = parent_block.func,
3384 .decl = parent_block.decl,3464 .decl = parent_block.decl,
3385 .instructions = .{},3465 .instructions = .{},
3386 .arena = parent_block.arena,3466 .arena = parent_block.arena,
3467 .inlining = parent_block.inlining,
3387 .is_comptime = parent_block.is_comptime,3468 .is_comptime = parent_block.is_comptime,
3388 };3469 };
3389 defer fail_block.instructions.deinit(mod.gpa);3470 defer fail_block.instructions.deinit(mod.gpa);
...@@ -3427,3 +3508,34 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void...@@ -3427,3 +3508,34 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void
3427 return mod.fail(scope, src, "variable of type '{}' must be const or comptime", .{ty});3508 return mod.fail(scope, src, "variable of type '{}' must be const or comptime", .{ty});
3428 }3509 }
3429}3510}
3511
3512/// Identifier token -> String (allocated in scope.arena())
3513pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
3514 const tree = scope.tree();
3515
3516 const ident_name = tree.tokenSlice(token);
3517 if (mem.startsWith(u8, ident_name, "@")) {
3518 const raw_string = ident_name[1..];
3519 var bad_index: usize = undefined;
3520 return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {
3521 error.InvalidCharacter => {
3522 const bad_byte = raw_string[bad_index];
3523 const src = tree.token_locs[token].start;
3524 return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
3525 },
3526 else => |e| return e,
3527 };
3528 }
3529 return ident_name;
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/astgen.zig+9-29
...@@ -384,7 +384,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr...@@ -384,7 +384,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr
384 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,384 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
385 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,385 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
386 else => if (node.getLabel()) |break_label| {386 else => if (node.getLabel()) |break_label| {
387 const label_name = try identifierTokenString(mod, parent_scope, break_label);387 const label_name = try mod.identifierTokenString(parent_scope, break_label);
388 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});388 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
389 } else {389 } else {
390 return mod.failTok(parent_scope, src, "break expression outside loop", .{});390 return mod.failTok(parent_scope, src, "break expression outside loop", .{});
...@@ -426,7 +426,7 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE...@@ -426,7 +426,7 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE
426 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,426 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
427 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,427 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
428 else => if (node.getLabel()) |break_label| {428 else => if (node.getLabel()) |break_label| {
429 const label_name = try identifierTokenString(mod, parent_scope, break_label);429 const label_name = try mod.identifierTokenString(parent_scope, break_label);
430 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});430 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
431 } else {431 } else {
432 return mod.failTok(parent_scope, src, "continue expression outside loop", .{});432 return mod.failTok(parent_scope, src, "continue expression outside loop", .{});
...@@ -551,7 +551,7 @@ fn varDecl(...@@ -551,7 +551,7 @@ fn varDecl(
551 }551 }
552 const tree = scope.tree();552 const tree = scope.tree();
553 const name_src = tree.token_locs[node.name_token].start;553 const name_src = tree.token_locs[node.name_token].start;
554 const ident_name = try identifierTokenString(mod, scope, node.name_token);554 const ident_name = try mod.identifierTokenString(scope, node.name_token);
555555
556 // Local variables shadowing detection, including function parameters.556 // Local variables shadowing detection, including function parameters.
557 {557 {
...@@ -843,7 +843,7 @@ fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_ins...@@ -843,7 +843,7 @@ fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_ins
843fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst {843fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst {
844 const tree = scope.tree();844 const tree = scope.tree();
845 const src = tree.token_locs[node.name].start;845 const src = tree.token_locs[node.name].start;
846 const name = try identifierTokenString(mod, scope, node.name);846 const name = try mod.identifierTokenString(scope, node.name);
847847
848 return addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{});848 return addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{});
849}849}
...@@ -864,7 +864,7 @@ fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Erro...@@ -864,7 +864,7 @@ fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Erro
864864
865 for (decls) |decl, i| {865 for (decls) |decl, i| {
866 const tag = decl.castTag(.ErrorTag).?;866 const tag = decl.castTag(.ErrorTag).?;
867 fields[i] = try identifierTokenString(mod, scope, tag.name_token);867 fields[i] = try mod.identifierTokenString(scope, tag.name_token);
868 }868 }
869869
870 // analyzing the error set results in a decl ref, so we might need to dereference it870 // analyzing the error set results in a decl ref, so we might need to dereference it
...@@ -988,36 +988,16 @@ fn orelseCatchExpr(...@@ -988,36 +988,16 @@ fn orelseCatchExpr(
988/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.988/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.
989/// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used.989/// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used.
990fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {990fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
991 const ident_name_1 = try identifierTokenString(mod, scope, token1);991 const ident_name_1 = try mod.identifierTokenString(scope, token1);
992 const ident_name_2 = try identifierTokenString(mod, scope, token2);992 const ident_name_2 = try mod.identifierTokenString(scope, token2);
993 return mem.eql(u8, ident_name_1, ident_name_2);993 return mem.eql(u8, ident_name_1, ident_name_2);
994}994}
995995
996/// Identifier token -> String (allocated in scope.arena())
997fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
998 const tree = scope.tree();
999
1000 const ident_name = tree.tokenSlice(token);
1001 if (mem.startsWith(u8, ident_name, "@")) {
1002 const raw_string = ident_name[1..];
1003 var bad_index: usize = undefined;
1004 return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {
1005 error.InvalidCharacter => {
1006 const bad_byte = raw_string[bad_index];
1007 const src = tree.token_locs[token].start;
1008 return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
1009 },
1010 else => |e| return e,
1011 };
1012 }
1013 return ident_name;
1014}
1015
1016pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {996pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
1017 const tree = scope.tree();997 const tree = scope.tree();
1018 const src = tree.token_locs[node.token].start;998 const src = tree.token_locs[node.token].start;
1019999
1020 const ident_name = try identifierTokenString(mod, scope, node.token);1000 const ident_name = try mod.identifierTokenString(scope, node.token);
10211001
1022 return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});1002 return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});
1023}1003}
...@@ -1936,7 +1916,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo...@@ -1936,7 +1916,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo
1936 defer tracy.end();1916 defer tracy.end();
19371917
1938 const tree = scope.tree();1918 const tree = scope.tree();
1939 const ident_name = try identifierTokenString(mod, scope, ident.token);1919 const ident_name = try mod.identifierTokenString(scope, ident.token);
1940 const src = tree.token_locs[ident.token].start;1920 const src = tree.token_locs[ident.token].start;
1941 if (mem.eql(u8, ident_name, "_")) {1921 if (mem.eql(u8, ident_name, "_")) {
1942 return mod.failNode(scope, &ident.base, "TODO implement '_' identifier", .{});1922 return mod.failNode(scope, &ident.base, "TODO implement '_' identifier", .{});
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.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.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.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.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.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.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.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.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/main.zig+1-1
...@@ -1818,7 +1818,7 @@ fn buildOutputType(...@@ -1818,7 +1818,7 @@ fn buildOutputType(
1818 };1818 };
18191819
1820 updateModule(gpa, comp, zir_out_path, hook) catch |err| switch (err) {1820 updateModule(gpa, comp, zir_out_path, hook) catch |err| switch (err) {
1821 error.SemanticAnalyzeFail => process.exit(1),1821 error.SemanticAnalyzeFail => if (!watch) process.exit(1),
1822 else => |e| return e,1822 else => |e| return e,
1823 };1823 };
1824 try comp.makeBinFileExecutable();1824 try comp.makeBinFileExecutable();
src/value.zig+8-5
...@@ -330,11 +330,14 @@ pub const Value = extern union {...@@ -330,11 +330,14 @@ pub const Value = extern union {
330 .int_type => return self.copyPayloadShallow(allocator, Payload.IntType),330 .int_type => return self.copyPayloadShallow(allocator, Payload.IntType),
331 .int_u64 => return self.copyPayloadShallow(allocator, Payload.U64),331 .int_u64 => return self.copyPayloadShallow(allocator, Payload.U64),
332 .int_i64 => return self.copyPayloadShallow(allocator, Payload.I64),332 .int_i64 => return self.copyPayloadShallow(allocator, Payload.I64),
333 .int_big_positive => {333 .int_big_positive, .int_big_negative => {
334 @panic("TODO implement copying of big ints");334 const old_payload = self.cast(Payload.BigInt).?;
335 },335 const new_payload = try allocator.create(Payload.BigInt);
336 .int_big_negative => {336 new_payload.* = .{
337 @panic("TODO implement copying of big ints");337 .base = .{ .tag = self.ptr_otherwise.tag },
338 .data = try allocator.dupe(std.math.big.Limb, old_payload.data),
339 };
340 return Value{ .ptr_otherwise = &new_payload.base };
338 },341 },
339 .function => return self.copyPayloadShallow(allocator, Payload.Function),342 .function => return self.copyPayloadShallow(allocator, Payload.Function),
340 .extern_fn => return self.copyPayloadShallow(allocator, Payload.Decl),343 .extern_fn => return self.copyPayloadShallow(allocator, Payload.Decl),
src/zir.zig+324-37
...@@ -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 {
...@@ -793,7 +794,9 @@ pub const Inst = struct {...@@ -793,7 +794,9 @@ pub const Inst = struct {
793 fn_type: *Inst,794 fn_type: *Inst,
794 body: Module.Body,795 body: Module.Body,
795 },796 },
796 kw_args: struct {},797 kw_args: struct {
798 is_inline: bool = false,
799 },
797 };800 };
798801
799 pub const FnType = struct {802 pub const FnType = struct {
...@@ -1847,44 +1850,325 @@ pub fn emit(allocator: *Allocator, old_module: *IrModule) !Module {...@@ -1847,44 +1850,325 @@ pub fn emit(allocator: *Allocator, old_module: *IrModule) !Module {
1847/// For debugging purposes, prints a function representation to stderr.1850/// For debugging purposes, prints a function representation to stderr.
1848pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void {1851pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void {
1849 const allocator = old_module.gpa;1852 const allocator = old_module.gpa;
1850 var ctx: EmitZIR = .{1853 var ctx: DumpTzir = .{
1851 .allocator = allocator,1854 .allocator = allocator,
1852 .decls = .{},
1853 .arena = std.heap.ArenaAllocator.init(allocator),1855 .arena = std.heap.ArenaAllocator.init(allocator),
1854 .old_module = &old_module,1856 .old_module = &old_module,
1855 .next_auto_name = 0,1857 .module_fn = module_fn,
1856 .names = std.StringArrayHashMap(void).init(allocator),1858 .indent = 2,
1857 .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),1859 .inst_table = DumpTzir.InstTable.init(allocator),
1858 .indent = 0,1860 .partial_inst_table = DumpTzir.InstTable.init(allocator),
1859 .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator),1861 .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 };1862 };
1864 defer ctx.metadata.deinit();1863 defer ctx.inst_table.deinit();
1865 defer ctx.body_metadata.deinit();1864 defer ctx.partial_inst_table.deinit();
1866 defer ctx.block_table.deinit();1865 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();1866 defer ctx.arena.deinit();
18721867
1873 const fn_ty = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;1868 switch (module_fn.state) {
1874 _ = ctx.emitFn(module_fn, 0, fn_ty) catch |err| {1869 .queued => std.debug.print("(queued)", .{}),
1875 std.debug.print("unable to dump function: {s}\n", .{@errorName(err)});1870 .inline_only => std.debug.print("(inline_only)", .{}),
1876 return;1871 .in_progress => std.debug.print("(in_progress)", .{}),
1877 };1872 .sema_failure => std.debug.print("(sema_failure)", .{}),
1878 var module = Module{1873 .dependency_failure => std.debug.print("(dependency_failure)", .{}),
1879 .decls = ctx.decls.items,1874 .success => {
1880 .arena = ctx.arena,1875 const writer = std.io.getStdErr().writer();
1881 .metadata = ctx.metadata,1876 ctx.dump(module_fn.body, writer) catch @panic("failed to dump TZIR");
1882 .body_metadata = ctx.body_metadata,1877 },
1883 };1878 }
1884
1885 module.dump();
1886}1879}
18871880
1881const DumpTzir = struct {
1882 allocator: *Allocator,
1883 arena: std.heap.ArenaAllocator,
1884 old_module: *const IrModule,
1885 module_fn: *IrModule.Fn,
1886 indent: usize,
1887 inst_table: InstTable,
1888 partial_inst_table: InstTable,
1889 const_table: InstTable,
1890 next_index: usize = 0,
1891 next_partial_index: usize = 0,
1892 next_const_index: usize = 0,
1893
1894 const InstTable = std.AutoArrayHashMap(*ir.Inst, usize);
1895
1896 fn dump(dtz: *DumpTzir, body: ir.Body, writer: std.fs.File.Writer) !void {
1897 // First pass to pre-populate the table so that we can show even invalid references.
1898 // Must iterate the same order we iterate the second time.
1899 // We also look for constants and put them in the const_table.
1900 for (body.instructions) |inst| {
1901 try dtz.inst_table.put(inst, dtz.next_index);
1902 dtz.next_index += 1;
1903 switch (inst.tag) {
1904 .alloc,
1905 .retvoid,
1906 .unreach,
1907 .breakpoint,
1908 .dbg_stmt,
1909 => {},
1910
1911 .ref,
1912 .ret,
1913 .bitcast,
1914 .not,
1915 .isnonnull,
1916 .isnull,
1917 .iserr,
1918 .ptrtoint,
1919 .floatcast,
1920 .intcast,
1921 .load,
1922 .unwrap_optional,
1923 .wrap_optional,
1924 => {
1925 const un_op = inst.cast(ir.Inst.UnOp).?;
1926 try dtz.findConst(un_op.operand);
1927 },
1928
1929 .add,
1930 .sub,
1931 .cmp_lt,
1932 .cmp_lte,
1933 .cmp_eq,
1934 .cmp_gte,
1935 .cmp_gt,
1936 .cmp_neq,
1937 .store,
1938 .booland,
1939 .boolor,
1940 .bitand,
1941 .bitor,
1942 .xor,
1943 => {
1944 const bin_op = inst.cast(ir.Inst.BinOp).?;
1945 try dtz.findConst(bin_op.lhs);
1946 try dtz.findConst(bin_op.rhs);
1947 },
1948
1949 .arg => {},
1950
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
1962 // TODO fill out this debug printing
1963 .assembly,
1964 .block,
1965 .call,
1966 .condbr,
1967 .constant,
1968 .loop,
1969 .varptr,
1970 .switchbr,
1971 => {},
1972 }
1973 }
1974
1975 std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name});
1976
1977 for (dtz.const_table.items()) |entry| {
1978 const constant = entry.key.castTag(.constant).?;
1979 try writer.print(" @{d}: {} = {};\n", .{
1980 entry.value, constant.base.ty, constant.val,
1981 });
1982 }
1983
1984 return dtz.dumpBody(body, writer);
1985 }
1986
1987 fn dumpBody(dtz: *DumpTzir, body: ir.Body, writer: std.fs.File.Writer) !void {
1988 for (body.instructions) |inst| {
1989 const my_index = dtz.next_partial_index;
1990 try dtz.partial_inst_table.put(inst, my_index);
1991 dtz.next_partial_index += 1;
1992
1993 try writer.writeByteNTimes(' ', dtz.indent);
1994 try writer.print("%{d}: {} = {s}(", .{
1995 my_index, inst.ty, @tagName(inst.tag),
1996 });
1997 switch (inst.tag) {
1998 .alloc,
1999 .retvoid,
2000 .unreach,
2001 .breakpoint,
2002 .dbg_stmt,
2003 => try writer.writeAll(")\n"),
2004
2005 .ref,
2006 .ret,
2007 .bitcast,
2008 .not,
2009 .isnonnull,
2010 .isnull,
2011 .iserr,
2012 .ptrtoint,
2013 .floatcast,
2014 .intcast,
2015 .load,
2016 .unwrap_optional,
2017 .wrap_optional,
2018 => {
2019 const un_op = inst.cast(ir.Inst.UnOp).?;
2020 if (dtz.partial_inst_table.get(un_op.operand)) |operand_index| {
2021 try writer.print("%{d})\n", .{operand_index});
2022 } else if (dtz.const_table.get(un_op.operand)) |operand_index| {
2023 try writer.print("@{d})\n", .{operand_index});
2024 } else if (dtz.inst_table.get(un_op.operand)) |operand_index| {
2025 try writer.print("%{d}) // Instruction does not dominate all uses!\n", .{
2026 operand_index,
2027 });
2028 } else {
2029 try writer.writeAll("!BADREF!)\n");
2030 }
2031 },
2032
2033 .add,
2034 .sub,
2035 .cmp_lt,
2036 .cmp_lte,
2037 .cmp_eq,
2038 .cmp_gte,
2039 .cmp_gt,
2040 .cmp_neq,
2041 .store,
2042 .booland,
2043 .boolor,
2044 .bitand,
2045 .bitor,
2046 .xor,
2047 => {
2048 var lhs_kinky: ?usize = null;
2049 var rhs_kinky: ?usize = null;
2050
2051 const bin_op = inst.cast(ir.Inst.BinOp).?;
2052 if (dtz.partial_inst_table.get(bin_op.lhs)) |operand_index| {
2053 try writer.print("%{d}, ", .{operand_index});
2054 } else if (dtz.const_table.get(bin_op.lhs)) |operand_index| {
2055 try writer.print("@{d}, ", .{operand_index});
2056 } else if (dtz.inst_table.get(bin_op.lhs)) |operand_index| {
2057 lhs_kinky = operand_index;
2058 try writer.print("%{d}, ", .{operand_index});
2059 } else {
2060 try writer.writeAll("!BADREF!, ");
2061 }
2062 if (dtz.partial_inst_table.get(bin_op.rhs)) |operand_index| {
2063 try writer.print("%{d}", .{operand_index});
2064 } else if (dtz.const_table.get(bin_op.rhs)) |operand_index| {
2065 try writer.print("@{d}", .{operand_index});
2066 } else if (dtz.inst_table.get(bin_op.rhs)) |operand_index| {
2067 rhs_kinky = operand_index;
2068 try writer.print("%{d}", .{operand_index});
2069 } else {
2070 try writer.writeAll("!BADREF!");
2071 }
2072 if (lhs_kinky != null or rhs_kinky != null) {
2073 try writer.writeAll(") // Instruction does not dominate all uses!");
2074 if (lhs_kinky) |lhs| {
2075 try writer.print(" %{d}", .{lhs});
2076 }
2077 if (rhs_kinky) |rhs| {
2078 try writer.print(" %{d}", .{rhs});
2079 }
2080 try writer.writeAll("\n");
2081 } else {
2082 try writer.writeAll(")\n");
2083 }
2084 },
2085
2086 .arg => {
2087 const arg = inst.castTag(.arg).?;
2088 try writer.print("{s})\n", .{arg.name});
2089 },
2090
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
2148 // TODO fill out this debug printing
2149 .assembly,
2150 .block,
2151 .call,
2152 .condbr,
2153 .constant,
2154 .loop,
2155 .varptr,
2156 .switchbr,
2157 => {
2158 try writer.writeAll("!TODO!)\n");
2159 },
2160 }
2161 }
2162 }
2163
2164 fn findConst(dtz: *DumpTzir, operand: *ir.Inst) !void {
2165 if (operand.tag == .constant) {
2166 try dtz.const_table.put(operand, dtz.next_const_index);
2167 dtz.next_const_index += 1;
2168 }
2169 }
2170};
2171
1888const EmitZIR = struct {2172const EmitZIR = struct {
1889 allocator: *Allocator,2173 allocator: *Allocator,
1890 arena: std.heap.ArenaAllocator,2174 arena: std.heap.ArenaAllocator,
...@@ -2072,11 +2356,12 @@ const EmitZIR = struct {...@@ -2072,11 +2356,12 @@ const EmitZIR = struct {
2072 var instructions = std.ArrayList(*Inst).init(self.allocator);2356 var instructions = std.ArrayList(*Inst).init(self.allocator);
2073 defer instructions.deinit();2357 defer instructions.deinit();
20742358
2075 switch (module_fn.analysis) {2359 switch (module_fn.state) {
2076 .queued => unreachable,2360 .queued => unreachable,
2077 .in_progress => unreachable,2361 .in_progress => unreachable,
2078 .success => |body| {2362 .inline_only => unreachable,
2079 try self.emitBody(body, &inst_table, &instructions);2363 .success => {
2364 try self.emitBody(module_fn.body, &inst_table, &instructions);
2080 },2365 },
2081 .sema_failure => {2366 .sema_failure => {
2082 const err_msg = self.old_module.failed_decls.get(module_fn.owner_decl).?;2367 const err_msg = self.old_module.failed_decls.get(module_fn.owner_decl).?;
...@@ -2154,7 +2439,9 @@ const EmitZIR = struct {...@@ -2154,7 +2439,9 @@ const EmitZIR = struct {
2154 .fn_type = fn_type.inst,2439 .fn_type = fn_type.inst,
2155 .body = .{ .instructions = arena_instrs },2440 .body = .{ .instructions = arena_instrs },
2156 },2441 },
2157 .kw_args = .{},2442 .kw_args = .{
2443 .is_inline = module_fn.state == .inline_only,
2444 },
2158 };2445 };
2159 return self.emitUnnamedDecl(&fn_inst.base);2446 return self.emitUnnamedDecl(&fn_inst.base);
2160 }2447 }
src/zir_sema.zig+189-88
...@@ -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 {
...@@ -575,7 +549,12 @@ fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) In...@@ -575,7 +549,12 @@ fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) In
575}549}
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.requireRuntimeBlock(scope, inst.base.src);552 const b = try mod.requireFunctionBlock(scope, inst.base.src);
553 if (b.inlining) |inlining| {
554 const param_index = inlining.param_index;
555 inlining.param_index += 1;
556 return inlining.casted_args[param_index];
557 }
579 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;558 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
580 const param_index = b.instructions.items.len;559 const param_index = b.instructions.items.len;
581 const param_count = fn_ty.fnParamLen();560 const param_count = fn_ty.fnParamLen();
...@@ -608,15 +587,17 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError...@@ -608,15 +587,17 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError
608587
609 var child_block: Scope.Block = .{588 var child_block: Scope.Block = .{
610 .parent = parent_block,589 .parent = parent_block,
590 .inst_table = parent_block.inst_table,
611 .func = parent_block.func,591 .func = parent_block.func,
612 .decl = parent_block.decl,592 .decl = parent_block.decl,
613 .instructions = .{},593 .instructions = .{},
614 .arena = parent_block.arena,594 .arena = parent_block.arena,
595 .inlining = parent_block.inlining,
615 .is_comptime = parent_block.is_comptime,596 .is_comptime = parent_block.is_comptime,
616 };597 };
617 defer child_block.instructions.deinit(mod.gpa);598 defer child_block.instructions.deinit(mod.gpa);
618599
619 try analyzeBody(mod, &child_block.base, inst.positionals.body);600 try analyzeBody(mod, &child_block, inst.positionals.body);
620601
621 // 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.
622603
...@@ -630,16 +611,18 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c...@@ -630,16 +611,18 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c
630611
631 var child_block: Scope.Block = .{612 var child_block: Scope.Block = .{
632 .parent = parent_block,613 .parent = parent_block,
614 .inst_table = parent_block.inst_table,
633 .func = parent_block.func,615 .func = parent_block.func,
634 .decl = parent_block.decl,616 .decl = parent_block.decl,
635 .instructions = .{},617 .instructions = .{},
636 .arena = parent_block.arena,618 .arena = parent_block.arena,
637 .label = null,619 .label = null,
620 .inlining = parent_block.inlining,
638 .is_comptime = parent_block.is_comptime or is_comptime,621 .is_comptime = parent_block.is_comptime or is_comptime,
639 };622 };
640 defer child_block.instructions.deinit(mod.gpa);623 defer child_block.instructions.deinit(mod.gpa);
641624
642 try analyzeBody(mod, &child_block.base, inst.positionals.body);625 try analyzeBody(mod, &child_block, inst.positionals.body);
643626
644 try parent_block.instructions.appendSlice(mod.gpa, child_block.instructions.items);627 try parent_block.instructions.appendSlice(mod.gpa, child_block.instructions.items);
645628
...@@ -668,6 +651,7 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt...@@ -668,6 +651,7 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt
668651
669 var child_block: Scope.Block = .{652 var child_block: Scope.Block = .{
670 .parent = parent_block,653 .parent = parent_block,
654 .inst_table = parent_block.inst_table,
671 .func = parent_block.func,655 .func = parent_block.func,
672 .decl = parent_block.decl,656 .decl = parent_block.decl,
673 .instructions = .{},657 .instructions = .{},
...@@ -675,38 +659,53 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt...@@ -675,38 +659,53 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt
675 // TODO @as here is working around a stage1 miscompilation bug :(659 // TODO @as here is working around a stage1 miscompilation bug :(
676 .label = @as(?Scope.Block.Label, Scope.Block.Label{660 .label = @as(?Scope.Block.Label, Scope.Block.Label{
677 .zir_block = inst,661 .zir_block = inst,
678 .results = .{},662 .merges = .{
679 .block_inst = block_inst,663 .results = .{},
664 .block_inst = block_inst,
665 },
680 }),666 }),
667 .inlining = parent_block.inlining,
681 .is_comptime = is_comptime or parent_block.is_comptime,668 .is_comptime = is_comptime or parent_block.is_comptime,
682 };669 };
683 const label = &child_block.label.?;670 const merges = &child_block.label.?.merges;
684671
685 defer child_block.instructions.deinit(mod.gpa);672 defer child_block.instructions.deinit(mod.gpa);
686 defer label.results.deinit(mod.gpa);673 defer merges.results.deinit(mod.gpa);
687674
688 try analyzeBody(mod, &child_block.base, inst.positionals.body);675 try analyzeBody(mod, &child_block, inst.positionals.body);
676
677 return analyzeBlockBody(mod, scope, &child_block, merges);
678}
679
680fn analyzeBlockBody(
681 mod: *Module,
682 scope: *Scope,
683 child_block: *Scope.Block,
684 merges: *Scope.Block.Merges,
685) InnerError!*Inst {
686 const parent_block = scope.cast(Scope.Block).?;
689687
690 // Blocks must terminate with noreturn instruction.688 // Blocks must terminate with noreturn instruction.
691 assert(child_block.instructions.items.len != 0);689 assert(child_block.instructions.items.len != 0);
692 assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());690 assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());
693691
694 if (label.results.items.len == 0) {692 if (merges.results.items.len == 0) {
695 // No need for a block instruction. We can put the new instructions directly into the parent block.693 // No need for a block instruction. We can put the new instructions
694 // directly into the parent block.
696 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items);695 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items);
697 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);696 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
698 return copied_instructions[copied_instructions.len - 1];697 return copied_instructions[copied_instructions.len - 1];
699 }698 }
700 if (label.results.items.len == 1) {699 if (merges.results.items.len == 1) {
701 const last_inst_index = child_block.instructions.items.len - 1;700 const last_inst_index = child_block.instructions.items.len - 1;
702 const last_inst = child_block.instructions.items[last_inst_index];701 const last_inst = child_block.instructions.items[last_inst_index];
703 if (last_inst.breakBlock()) |br_block| {702 if (last_inst.breakBlock()) |br_block| {
704 if (br_block == block_inst) {703 if (br_block == merges.block_inst) {
705 // No need for a block instruction. We can put the new instructions directly into the parent block.704 // No need for a block instruction. We can put the new instructions directly into the parent block.
706 // Here we omit the break instruction.705 // Here we omit the break instruction.
707 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]);706 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]);
708 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);707 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
709 return label.results.items[0];708 return merges.results.items[0];
710 }709 }
711 }710 }
712 }711 }
...@@ -715,10 +714,10 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt...@@ -715,10 +714,10 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt
715714
716 // Need to set the type and emit the Block instruction. This allows machine code generation715 // Need to set the type and emit the Block instruction. This allows machine code generation
717 // to emit a jump instruction to after the block when it encounters the break.716 // to emit a jump instruction to after the block when it encounters the break.
718 try parent_block.instructions.append(mod.gpa, &block_inst.base);717 try parent_block.instructions.append(mod.gpa, &merges.block_inst.base);
719 block_inst.base.ty = try mod.resolvePeerTypes(scope, label.results.items);718 merges.block_inst.base.ty = try mod.resolvePeerTypes(scope, merges.results.items);
720 block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };719 merges.block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
721 return &block_inst.base;720 return &merges.block_inst.base;
722}721}
723722
724fn analyzeInstBreakpoint(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {723fn analyzeInstBreakpoint(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
...@@ -826,28 +825,108 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError...@@ -826,28 +825,108 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
826825
827 const ret_type = func.ty.fnReturnType();826 const ret_type = func.ty.fnReturnType();
828827
829 const b = try mod.requireRuntimeBlock(scope, inst.base.src);828 const b = try mod.requireFunctionBlock(scope, inst.base.src);
829 const is_comptime_call = b.is_comptime or inst.kw_args.modifier == .compile_time;
830 const is_inline_call = is_comptime_call or inst.kw_args.modifier == .always_inline or blk: {
831 // This logic will get simplified by
832 // https://github.com/ziglang/zig/issues/6429
833 if (try mod.resolveDefinedValue(scope, func)) |func_val| {
834 const module_fn = switch (func_val.tag()) {
835 .function => func_val.castTag(.function).?.data,
836 else => break :blk false,
837 };
838 break :blk module_fn.state == .inline_only;
839 }
840 break :blk false;
841 };
842 if (is_inline_call) {
843 const func_val = try mod.resolveConstValue(scope, func);
844 const module_fn = switch (func_val.tag()) {
845 .function => func_val.castTag(.function).?.data,
846 .extern_fn => return mod.fail(scope, inst.base.src, "{s} call of extern function", .{
847 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
848 }),
849 else => unreachable,
850 };
851
852 // Analyze the ZIR. The same ZIR gets analyzed into a runtime function
853 // or an inlined call depending on what union tag the `label` field is
854 // set to in the `Scope.Block`.
855 // This block instruction will be used to capture the return value from the
856 // inlined function.
857 const block_inst = try scope.arena().create(Inst.Block);
858 block_inst.* = .{
859 .base = .{
860 .tag = Inst.Block.base_tag,
861 .ty = ret_type,
862 .src = inst.base.src,
863 },
864 .body = undefined,
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
887 var child_block: Scope.Block = .{
888 .parent = null,
889 .inst_table = &inst_table,
890 .func = module_fn,
891 // Note that we pass the caller's Decl, not the callee. This causes
892 // compile errors to be attached (correctly) to the caller's Decl.
893 .decl = scope.decl().?,
894 .instructions = .{},
895 .arena = scope.arena(),
896 .label = null,
897 .inlining = &inlining,
898 .is_comptime = is_comptime_call,
899 };
900 const merges = &child_block.inlining.?.merges;
901
902 defer child_block.instructions.deinit(mod.gpa);
903 defer merges.results.deinit(mod.gpa);
904
905 try mod.emitBackwardBranch(&child_block, inst.base.src);
906
907 // This will have return instructions analyzed as break instructions to
908 // the block_inst above.
909 try analyzeBody(mod, &child_block, module_fn.zir);
910
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;
918 }
919
830 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);
831}921}
832922
833fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {923fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {
834 const fn_type = try resolveType(mod, scope, fn_inst.positionals.fn_type);924 const fn_type = try resolveType(mod, scope, fn_inst.positionals.fn_type);
835 const fn_zir = blk: {
836 var fn_arena = std.heap.ArenaAllocator.init(mod.gpa);
837 errdefer fn_arena.deinit();
838
839 const fn_zir = try scope.arena().create(Module.Fn.ZIR);
840 fn_zir.* = .{
841 .body = .{
842 .instructions = fn_inst.positionals.body.instructions,
843 },
844 .arena = fn_arena.state,
845 };
846 break :blk fn_zir;
847 };
848 const new_func = try scope.arena().create(Module.Fn);925 const new_func = try scope.arena().create(Module.Fn);
849 new_func.* = .{926 new_func.* = .{
850 .analysis = .{ .queued = fn_zir },927 .state = if (fn_inst.kw_args.is_inline) .inline_only else .queued,
928 .zir = fn_inst.positionals.body,
929 .body = undefined,
851 .owner_decl = scope.decl().?,930 .owner_decl = scope.decl().?,
852 };931 };
853 return mod.constInst(scope, fn_inst.base.src, .{932 return mod.constInst(scope, fn_inst.base.src, .{
...@@ -1312,17 +1391,17 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In...@@ -1312,17 +1391,17 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
1312 const item = try mod.resolveConstValue(scope, casted);1391 const item = try mod.resolveConstValue(scope, casted);
13131392
1314 if (target_val.eql(item)) {1393 if (target_val.eql(item)) {
1315 try analyzeBody(mod, scope, case.body);1394 try analyzeBody(mod, scope.cast(Scope.Block).?, case.body);
1316 return mod.constNoReturn(scope, inst.base.src);1395 return mod.constNoReturn(scope, inst.base.src);
1317 }1396 }
1318 }1397 }
1319 try analyzeBody(mod, scope, inst.positionals.else_body);1398 try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body);
1320 return mod.constNoReturn(scope, inst.base.src);1399 return mod.constNoReturn(scope, inst.base.src);
1321 }1400 }
13221401
1323 if (inst.positionals.cases.len == 0) {1402 if (inst.positionals.cases.len == 0) {
1324 // no cases just analyze else_branch1403 // no cases just analyze else_branch
1325 try analyzeBody(mod, scope, inst.positionals.else_body);1404 try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body);
1326 return mod.constNoReturn(scope, inst.base.src);1405 return mod.constNoReturn(scope, inst.base.src);
1327 }1406 }
13281407
...@@ -1331,10 +1410,12 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In...@@ -1331,10 +1410,12 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
13311410
1332 var case_block: Scope.Block = .{1411 var case_block: Scope.Block = .{
1333 .parent = parent_block,1412 .parent = parent_block,
1413 .inst_table = parent_block.inst_table,
1334 .func = parent_block.func,1414 .func = parent_block.func,
1335 .decl = parent_block.decl,1415 .decl = parent_block.decl,
1336 .instructions = .{},1416 .instructions = .{},
1337 .arena = parent_block.arena,1417 .arena = parent_block.arena,
1418 .inlining = parent_block.inlining,
1338 .is_comptime = parent_block.is_comptime,1419 .is_comptime = parent_block.is_comptime,
1339 };1420 };
1340 defer case_block.instructions.deinit(mod.gpa);1421 defer case_block.instructions.deinit(mod.gpa);
...@@ -1347,7 +1428,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In...@@ -1347,7 +1428,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
1347 const casted = try mod.coerce(scope, target.ty, resolved);1428 const casted = try mod.coerce(scope, target.ty, resolved);
1348 const item = try mod.resolveConstValue(scope, casted);1429 const item = try mod.resolveConstValue(scope, casted);
13491430
1350 try analyzeBody(mod, &case_block.base, case.body);1431 try analyzeBody(mod, &case_block, case.body);
13511432
1352 cases[i] = .{1433 cases[i] = .{
1353 .item = item,1434 .item = item,
...@@ -1356,7 +1437,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In...@@ -1356,7 +1437,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
1356 }1437 }
13571438
1358 case_block.instructions.items.len = 0;1439 case_block.instructions.items.len = 0;
1359 try analyzeBody(mod, &case_block.base, inst.positionals.else_body);1440 try analyzeBody(mod, &case_block, inst.positionals.else_body);
13601441
1361 const else_body: ir.Body = .{1442 const else_body: ir.Body = .{
1362 .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items),1443 .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items),
...@@ -1509,7 +1590,7 @@ fn analyzeInstImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerErr...@@ -1509,7 +1590,7 @@ fn analyzeInstImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerErr
1509 return mod.fail(scope, inst.base.src, "unable to find '{s}'", .{operand});1590 return mod.fail(scope, inst.base.src, "unable to find '{s}'", .{operand});
1510 },1591 },
1511 else => {1592 else => {
1512 // TODO user friendly error to string1593 // TODO: make sure this gets retried and not cached
1513 return mod.fail(scope, inst.base.src, "unable to open '{s}': {s}", .{ operand, @errorName(err) });1594 return mod.fail(scope, inst.base.src, "unable to open '{s}': {s}", .{ operand, @errorName(err) });
1514 },1595 },
1515 };1596 };
...@@ -1674,24 +1755,26 @@ fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir...@@ -1674,24 +1755,26 @@ fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir
1674 }1755 }
1675 const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt;1756 const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt;
16761757
1677 const value = try switch (inst.base.tag) {1758 const value = switch (inst.base.tag) {
1678 .add => blk: {1759 .add => blk: {
1679 const val = if (is_int)1760 const val = if (is_int)
1680 Module.intAdd(scope.arena(), lhs_val, rhs_val)1761 try Module.intAdd(scope.arena(), lhs_val, rhs_val)
1681 else1762 else
1682 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);
1683 break :blk val;1764 break :blk val;
1684 },1765 },
1685 .sub => blk: {1766 .sub => blk: {
1686 const val = if (is_int)1767 const val = if (is_int)
1687 Module.intSub(scope.arena(), lhs_val, rhs_val)1768 try Module.intSub(scope.arena(), lhs_val, rhs_val)
1688 else1769 else
1689 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);
1690 break :blk val;1771 break :blk val;
1691 },1772 },
1692 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)}),
1693 };1774 };
16941775
1776 log.debug("{s}({}, {}) result: {}", .{ @tagName(inst.base.tag), lhs_val, rhs_val, value });
1777
1695 return mod.constInst(scope, inst.base.src, .{1778 return mod.constInst(scope, inst.base.src, .{
1696 .ty = res_type,1779 .ty = res_type,
1697 .val = value,1780 .val = value,
...@@ -1860,35 +1943,39 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE...@@ -1860,35 +1943,39 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
1860 const uncasted_cond = try resolveInst(mod, scope, inst.positionals.condition);1943 const uncasted_cond = try resolveInst(mod, scope, inst.positionals.condition);
1861 const cond = try mod.coerce(scope, Type.initTag(.bool), uncasted_cond);1944 const cond = try mod.coerce(scope, Type.initTag(.bool), uncasted_cond);
18621945
1946 const parent_block = scope.cast(Scope.Block).?;
1947
1863 if (try mod.resolveDefinedValue(scope, cond)) |cond_val| {1948 if (try mod.resolveDefinedValue(scope, cond)) |cond_val| {
1864 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;
1865 try analyzeBody(mod, scope, body.*);1950 try analyzeBody(mod, parent_block, body.*);
1866 return mod.constNoReturn(scope, inst.base.src);1951 return mod.constNoReturn(scope, inst.base.src);
1867 }1952 }
18681953
1869 const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);
1870
1871 var true_block: Scope.Block = .{1954 var true_block: Scope.Block = .{
1872 .parent = parent_block,1955 .parent = parent_block,
1956 .inst_table = parent_block.inst_table,
1873 .func = parent_block.func,1957 .func = parent_block.func,
1874 .decl = parent_block.decl,1958 .decl = parent_block.decl,
1875 .instructions = .{},1959 .instructions = .{},
1876 .arena = parent_block.arena,1960 .arena = parent_block.arena,
1961 .inlining = parent_block.inlining,
1877 .is_comptime = parent_block.is_comptime,1962 .is_comptime = parent_block.is_comptime,
1878 };1963 };
1879 defer true_block.instructions.deinit(mod.gpa);1964 defer true_block.instructions.deinit(mod.gpa);
1880 try analyzeBody(mod, &true_block.base, inst.positionals.then_body);1965 try analyzeBody(mod, &true_block, inst.positionals.then_body);
18811966
1882 var false_block: Scope.Block = .{1967 var false_block: Scope.Block = .{
1883 .parent = parent_block,1968 .parent = parent_block,
1969 .inst_table = parent_block.inst_table,
1884 .func = parent_block.func,1970 .func = parent_block.func,
1885 .decl = parent_block.decl,1971 .decl = parent_block.decl,
1886 .instructions = .{},1972 .instructions = .{},
1887 .arena = parent_block.arena,1973 .arena = parent_block.arena,
1974 .inlining = parent_block.inlining,
1888 .is_comptime = parent_block.is_comptime,1975 .is_comptime = parent_block.is_comptime,
1889 };1976 };
1890 defer false_block.instructions.deinit(mod.gpa);1977 defer false_block.instructions.deinit(mod.gpa);
1891 try analyzeBody(mod, &false_block.base, inst.positionals.else_body);1978 try analyzeBody(mod, &false_block, inst.positionals.else_body);
18921979
1893 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) };
1894 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) };
...@@ -1912,12 +1999,26 @@ fn analyzeInstUnreachable(...@@ -1912,12 +1999,26 @@ fn analyzeInstUnreachable(
19121999
1913fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {2000fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1914 const operand = try resolveInst(mod, scope, inst.positionals.operand);2001 const operand = try resolveInst(mod, scope, inst.positionals.operand);
1915 const b = try mod.requireRuntimeBlock(scope, inst.base.src);2002 const b = try mod.requireFunctionBlock(scope, inst.base.src);
2003
2004 if (b.inlining) |inlining| {
2005 // We are inlining a function call; rewrite the `ret` as a `break`.
2006 try inlining.merges.results.append(mod.gpa, operand);
2007 return mod.addBr(b, inst.base.src, inlining.merges.block_inst, operand);
2008 }
2009
1916 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand);2010 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand);
1917}2011}
19182012
1919fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {2013fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
1920 const b = try mod.requireRuntimeBlock(scope, inst.base.src);2014 const b = try mod.requireFunctionBlock(scope, inst.base.src);
2015 if (b.inlining) |inlining| {
2016 // We are inlining a function call; rewrite the `retvoid` as a `breakvoid`.
2017 const void_inst = try mod.constVoid(scope, inst.base.src);
2018 try inlining.merges.results.append(mod.gpa, void_inst);
2019 return mod.addBr(b, inst.base.src, inlining.merges.block_inst, void_inst);
2020 }
2021
1921 if (b.func) |func| {2022 if (b.func) |func| {
1922 // Need to emit a compile error if returning void is not allowed.2023 // Need to emit a compile error if returning void is not allowed.
1923 const void_inst = try mod.constVoid(scope, inst.base.src);2024 const void_inst = try mod.constVoid(scope, inst.base.src);
...@@ -1949,9 +2050,9 @@ fn analyzeBreak(...@@ -1949,9 +2050,9 @@ fn analyzeBreak(
1949 while (opt_block) |block| {2050 while (opt_block) |block| {
1950 if (block.label) |*label| {2051 if (block.label) |*label| {
1951 if (label.zir_block == zir_block) {2052 if (label.zir_block == zir_block) {
1952 try label.results.append(mod.gpa, operand);2053 try label.merges.results.append(mod.gpa, operand);
1953 const b = try mod.requireRuntimeBlock(scope, src);2054 const b = try mod.requireFunctionBlock(scope, src);
1954 return mod.addBr(b, src, label.block_inst, operand);2055 return mod.addBr(b, src, label.merges.block_inst, operand);
1955 }2056 }
1956 }2057 }
1957 opt_block = block.parent;2058 opt_block = block.parent;
test/stage2/test.zig+147-2
...@@ -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);
...@@ -318,7 +317,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -318,7 +317,7 @@ pub fn addCases(ctx: *TestContext) !void {
318 }317 }
319318
320 {319 {
321 var case = ctx.exe("adding numbers at runtime", linux_x64);320 var case = ctx.exe("adding numbers at runtime and comptime", linux_x64);
322 case.addCompareOutput(321 case.addCompareOutput(
323 \\export fn _start() noreturn {322 \\export fn _start() noreturn {
324 \\ add(3, 4);323 \\ add(3, 4);
...@@ -342,6 +341,54 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -342,6 +341,54 @@ pub fn addCases(ctx: *TestContext) !void {
342 ,341 ,
343 "",342 "",
344 );343 );
344 // comptime function call
345 case.addCompareOutput(
346 \\export fn _start() noreturn {
347 \\ exit();
348 \\}
349 \\
350 \\fn add(a: u32, b: u32) u32 {
351 \\ return a + b;
352 \\}
353 \\
354 \\const x = add(3, 4);
355 \\
356 \\fn exit() noreturn {
357 \\ asm volatile ("syscall"
358 \\ :
359 \\ : [number] "{rax}" (231),
360 \\ [arg1] "{rdi}" (x - 7)
361 \\ : "rcx", "r11", "memory"
362 \\ );
363 \\ unreachable;
364 \\}
365 ,
366 "",
367 );
368 // Inline function call
369 case.addCompareOutput(
370 \\export fn _start() noreturn {
371 \\ var x: usize = 3;
372 \\ const y = add(1, 2, x);
373 \\ exit(y - 6);
374 \\}
375 \\
376 \\inline fn add(a: usize, b: usize, c: usize) usize {
377 \\ return a + b + c;
378 \\}
379 \\
380 \\fn exit(code: usize) noreturn {
381 \\ asm volatile ("syscall"
382 \\ :
383 \\ : [number] "{rax}" (231),
384 \\ [arg1] "{rdi}" (code)
385 \\ : "rcx", "r11", "memory"
386 \\ );
387 \\ unreachable;
388 \\}
389 ,
390 "",
391 );
345 }392 }
346393
347 {394 {
...@@ -1331,4 +1378,102 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1331,4 +1378,102 @@ pub fn addCases(ctx: *TestContext) !void {
1331 \\}1378 \\}
1332 , &[_][]const u8{":2:9: error: variable of type '@Type(.Null)' must be const or comptime"});1379 , &[_][]const u8{":2:9: error: variable of type '@Type(.Null)' must be const or comptime"});
1333 }1380 }
1381
1382 {
1383 var case = ctx.exe("compile error in inline fn call fixed", linux_x64);
1384 case.addError(
1385 \\export fn _start() noreturn {
1386 \\ var x: usize = 3;
1387 \\ const y = add(10, 2, x);
1388 \\ exit(y - 6);
1389 \\}
1390 \\
1391 \\inline fn add(a: usize, b: usize, c: usize) usize {
1392 \\ if (a == 10) @compileError("bad");
1393 \\ return a + b + c;
1394 \\}
1395 \\
1396 \\fn exit(code: usize) noreturn {
1397 \\ asm volatile ("syscall"
1398 \\ :
1399 \\ : [number] "{rax}" (231),
1400 \\ [arg1] "{rdi}" (code)
1401 \\ : "rcx", "r11", "memory"
1402 \\ );
1403 \\ unreachable;
1404 \\}
1405 , &[_][]const u8{":8:18: error: bad"});
1406
1407 case.addCompareOutput(
1408 \\export fn _start() noreturn {
1409 \\ var x: usize = 3;
1410 \\ const y = add(1, 2, x);
1411 \\ exit(y - 6);
1412 \\}
1413 \\
1414 \\inline fn add(a: usize, b: usize, c: usize) usize {
1415 \\ if (a == 10) @compileError("bad");
1416 \\ return a + b + c;
1417 \\}
1418 \\
1419 \\fn exit(code: usize) noreturn {
1420 \\ asm volatile ("syscall"
1421 \\ :
1422 \\ : [number] "{rax}" (231),
1423 \\ [arg1] "{rdi}" (code)
1424 \\ : "rcx", "r11", "memory"
1425 \\ );
1426 \\ unreachable;
1427 \\}
1428 ,
1429 "",
1430 );
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 }
1334}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 \\})
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 \\})
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 \\})
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 \\})
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 \\})
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 \\})
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}