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 {
220220 }
221221
222222 const log_scopes = b.option([]const []const u8, "log", "Which log scopes to enable") orelse &[0][]const u8{};
223 const zir_dumps = b.option([]const []const u8, "dump-zir", "Which functions to dump ZIR for before codegen") orelse &[0][]const u8{};
224223
225224 const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git.");
226225 const version = if (opt_version_string) |version| version else v: {
......@@ -277,7 +276,6 @@ pub fn build(b: *Builder) !void {
277276 exe.addBuildOption(std.SemanticVersion, "semver", semver);
278277
279278 exe.addBuildOption([]const []const u8, "log_scopes", log_scopes);
280 exe.addBuildOption([]const []const u8, "zir_dumps", zir_dumps);
281279 exe.addBuildOption(bool, "enable_tracy", tracy != null);
282280 exe.addBuildOption(bool, "is_stage1", is_stage1);
283281 if (tracy) |tracy_path| {
src/Compilation.zig+10-5
......@@ -1459,24 +1459,29 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
14591459 const module = self.bin_file.options.module.?;
14601460 if (decl.typed_value.most_recent.typed_value.val.castTag(.function)) |payload| {
14611461 const func = payload.data;
1462 switch (func.analysis) {
1462 switch (func.state) {
14631463 .queued => module.analyzeFnBody(decl, func) catch |err| switch (err) {
14641464 error.AnalysisFail => {
1465 assert(func.analysis != .in_progress);
1465 assert(func.state != .in_progress);
14661466 continue;
14671467 },
14681468 error.OutOfMemory => return error.OutOfMemory,
14691469 },
14701470 .in_progress => unreachable,
1471 .inline_only => unreachable, // don't queue work for this
14711472 .sema_failure, .dependency_failure => continue,
14721473 .success => {},
14731474 }
1474 // Here we tack on additional allocations to the Decl's arena. The allocations are
1475 // lifetime annotations in the ZIR.
1475 // Here we tack on additional allocations to the Decl's arena. The allocations
1476 // are lifetime annotations in the ZIR.
14761477 var decl_arena = decl.typed_value.most_recent.arena.?.promote(module.gpa);
14771478 defer decl.typed_value.most_recent.arena.?.* = decl_arena.state;
14781479 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 }
14801485 }
14811486
14821487 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
src/Module.zig+188-76
......@@ -268,6 +268,11 @@ pub const Decl = struct {
268268 }
269269 }
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
271276 fn removeDependant(self: *Decl, other: *Decl) void {
272277 self.dependants.removeAssertDiscard(other);
273278 }
......@@ -281,46 +286,32 @@ pub const Decl = struct {
281286/// Extern functions do not have this data structure; they are represented by
282287/// the `Decl` only, with a `Value` tag of `extern_fn`.
283288pub const Fn = struct {
284 /// This memory owned by the Decl's TypedValue.Managed arena allocator.
285 analysis: union(enum) {
286 queued: *ZIR,
289 owner_decl: *Decl,
290 /// Contains un-analyzed ZIR instructions generated from Zig source AST.
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,
287303 in_progress,
288304 /// There will be a corresponding ErrorMsg in Module.failed_decls
289305 sema_failure,
290 /// This Fn might be OK but it depends on another Decl which did not successfully complete
291 /// semantic analysis.
306 /// This Fn might be OK but it depends on another Decl which did not
307 /// successfully complete semantic analysis.
292308 dependency_failure,
293 success: Body,
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,
309 success,
307310 };
308311
309312 /// For debugging purposes.
310313 pub fn dump(self: *Fn, mod: Module) void {
311 std.debug.print("Module.Function(name={s}) ", .{self.owner_decl.name});
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 }
314 zir.dumpFn(mod, self);
324315 }
325316};
326317
......@@ -761,21 +752,60 @@ pub const Scope = struct {
761752 /// during semantic analysis of the block.
762753 pub const Block = struct {
763754 pub const base_tag: Tag = .block;
755
764756 base: Scope = Scope{ .tag = base_tag },
765757 parent: ?*Block,
758 /// Maps ZIR to TZIR. Shared to sub-blocks.
759 inst_table: *InstTable,
766760 func: ?*Fn,
767761 decl: *Decl,
768762 instructions: ArrayListUnmanaged(*Inst),
769763 /// Points to the arena allocator of DeclAnalysis
770764 arena: *Allocator,
771765 label: ?Label = null,
766 inlining: ?*Inlining,
772767 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.
774773 pub const Label = struct {
775774 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 {
776801 results: ArrayListUnmanaged(*Inst),
777802 block_inst: *Inst.Block,
778803 };
804
805 /// For debugging purposes.
806 pub fn dump(self: *Block, mod: Module) void {
807 zir.dumpBlock(mod, self);
808 }
779809 };
780810
781811 /// This is a temporary structure, references to it are valid only
......@@ -992,11 +1022,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
9921022 defer tracy.end();
9931023
9941024 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);
9961026 const ast_node = tree.root_node.decls()[decl.src_index];
9971027 switch (ast_node.tag) {
9981028 .FnProto => {
999 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node);
1029 const fn_proto = ast_node.castTag(.FnProto).?;
10001030
10011031 decl.analysis = .in_progress;
10021032
......@@ -1062,7 +1092,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
10621092 .param_types = param_types,
10631093 }, .{});
10641094
1065 if (self.comp.verbose_ir) {
1095 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {
10661096 zir.dumpZir(self.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {};
10671097 }
10681098
......@@ -1071,12 +1101,17 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
10711101 errdefer decl_arena.deinit();
10721102 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
10741107 var block_scope: Scope.Block = .{
10751108 .parent = null,
1109 .inst_table = &inst_table,
10761110 .func = null,
10771111 .decl = decl,
10781112 .instructions = .{},
10791113 .arena = &decl_arena.allocator,
1114 .inlining = null,
10801115 .is_comptime = false,
10811116 };
10821117 defer block_scope.instructions.deinit(self.gpa);
......@@ -1113,14 +1148,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
11131148 const new_func = try decl_arena.allocator.create(Fn);
11141149 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
11151150
1116 const fn_zir = blk: {
1117 // This scope's arena memory is discarded after the ZIR generation
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();
1151 const fn_zir: zir.Module.Body = blk: {
1152 // We put the ZIR inside the Decl arena.
11211153 var gen_scope: Scope.GenZIR = .{
11221154 .decl = decl,
1123 .arena = &gen_scope_arena.allocator,
1155 .arena = &decl_arena.allocator,
11241156 .parent = decl.scope,
11251157 };
11261158 defer gen_scope.instructions.deinit(self.gpa);
......@@ -1131,8 +1163,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
11311163 for (fn_proto.params()) |param, i| {
11321164 const name_token = param.name_token.?;
11331165 const src = tree.token_locs[name_token].start;
1134 const param_name = tree.tokenSlice(name_token); // TODO: call identifierTokenString
1135 const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg);
1166 const param_name = try self.identifierTokenString(&gen_scope.base, name_token);
1167 const arg = try decl_arena.allocator.create(zir.Inst.Arg);
11361168 arg.* = .{
11371169 .base = .{
11381170 .tag = .arg,
......@@ -1144,7 +1176,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
11441176 .kw_args = .{},
11451177 };
11461178 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);
11481180 sub_scope.* = .{
11491181 .parent = params_scope,
11501182 .gen_zir = &gen_scope,
......@@ -1165,22 +1197,29 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
11651197 _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid);
11661198 }
11671199
1168 if (self.comp.verbose_ir) {
1200 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {
11691201 zir.dumpZir(self.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {};
11701202 }
11711203
1172 const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR);
1173 fn_zir.* = .{
1174 .body = .{
1175 .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),
1176 },
1177 .arena = gen_scope_arena.state,
1204 break :blk .{
1205 .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),
11781206 };
1179 break :blk fn_zir;
11801207 };
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
11821219 new_func.* = .{
1183 .analysis = .{ .queued = fn_zir },
1220 .state = anal_state,
1221 .zir = fn_zir,
1222 .body = undefined,
11841223 .owner_decl = decl,
11851224 };
11861225 fn_payload.* = .{
......@@ -1189,11 +1228,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
11891228 };
11901229
11911230 var prev_type_has_bits = false;
1231 var prev_is_inline = false;
11921232 var type_changed = true;
11931233
11941234 if (decl.typedValueManaged()) |tvm| {
11951235 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
11961236 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
11981242 tvm.deinit(self.gpa);
11991243 }
......@@ -1211,18 +1255,26 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12111255 decl.analysis = .complete;
12121256 decl.generation = self.generation;
12131257
1214 if (fn_type.hasCodeGenBits()) {
1258 if (!is_inline and fn_type.hasCodeGenBits()) {
12151259 // We don't fully codegen the decl until later, but we do need to reserve a global
12161260 // offset table index for it. This allows us to codegen decls out of dependency order,
12171261 // increasing how many computations can be done in parallel.
12181262 try self.comp.bin_file.allocateDeclIndexes(decl);
12191263 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) {
12211265 self.comp.bin_file.freeDecl(decl);
12221266 }
12231267
12241268 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
12251269 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 }
12261278 const export_src = tree.token_locs[maybe_export_token].start;
12271279 const name_loc = tree.token_locs[fn_proto.getNameToken().?];
12281280 const name = tree.tokenSliceLoc(name_loc);
......@@ -1230,7 +1282,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12301282 try self.analyzeExport(&block_scope.base, export_src, name, decl);
12311283 }
12321284 }
1233 return type_changed;
1285 return type_changed or is_inline != prev_is_inline;
12341286 },
12351287 .VarDecl => {
12361288 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node);
......@@ -1242,12 +1294,17 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12421294 errdefer decl_arena.deinit();
12431295 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
12451300 var block_scope: Scope.Block = .{
12461301 .parent = null,
1302 .inst_table = &decl_inst_table,
12471303 .func = null,
12481304 .decl = decl,
12491305 .instructions = .{},
12501306 .arena = &decl_arena.allocator,
1307 .inlining = null,
12511308 .is_comptime = true,
12521309 };
12531310 defer block_scope.instructions.deinit(self.gpa);
......@@ -1303,23 +1360,30 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
13031360
13041361 const src = tree.token_locs[init_node.firstToken()].start;
13051362 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) {
13071364 zir.dumpZir(self.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {};
13081365 }
13091366
1367 var var_inst_table = Scope.Block.InstTable.init(self.gpa);
1368 defer var_inst_table.deinit();
1369
13101370 var inner_block: Scope.Block = .{
13111371 .parent = null,
1372 .inst_table = &var_inst_table,
13121373 .func = null,
13131374 .decl = decl,
13141375 .instructions = .{},
13151376 .arena = &gen_scope_arena.allocator,
1377 .inlining = null,
13161378 .is_comptime = true,
13171379 };
13181380 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
13211385 // 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).?;
13231387 // The is_comptime in the Scope.Block guarantees the result is comptime-known.
13241388 const val = analyzed_init_inst.value().?;
13251389
......@@ -1347,7 +1411,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
13471411 .val = Value.initTag(.type_type),
13481412 });
13491413 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) {
13511415 zir.dumpZir(self.gpa, "var_type", decl.name, type_scope.instructions.items) catch {};
13521416 }
13531417
......@@ -1423,21 +1487,26 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
14231487 defer gen_scope.instructions.deinit(self.gpa);
14241488
14251489 _ = 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) {
14271491 zir.dumpZir(self.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {};
14281492 }
14291493
1494 var inst_table = Scope.Block.InstTable.init(self.gpa);
1495 defer inst_table.deinit();
1496
14301497 var block_scope: Scope.Block = .{
14311498 .parent = null,
1499 .inst_table = &inst_table,
14321500 .func = null,
14331501 .decl = decl,
14341502 .instructions = .{},
14351503 .arena = &analysis_arena.allocator,
1504 .inlining = null,
14361505 .is_comptime = true,
14371506 };
14381507 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, .{
14411510 .instructions = gen_scope.instructions.items,
14421511 });
14431512
......@@ -1496,12 +1565,10 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
14961565 }
14971566}
14981567
1499fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree {
1568pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
15001569 const tracy = trace(@src());
15011570 defer tracy.end();
15021571
1503 const root_scope = container_scope.file_scope;
1504
15051572 switch (root_scope.status) {
15061573 .never_loaded, .unloaded_success => {
15071574 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
15491616
15501617 // We may be analyzing it for the first time, or this may be
15511618 // 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);
15531620 const decls = tree.root_node.decls();
15541621
15551622 try self.comp.work_queue.ensureUnusedCapacity(decls.len);
......@@ -1806,25 +1873,28 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
18061873 // Use the Decl's arena for function memory.
18071874 var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
18081875 defer decl.typed_value.most_recent.arena.?.* = arena.state;
1876 var inst_table = Scope.Block.InstTable.init(self.gpa);
1877 defer inst_table.deinit();
18091878 var inner_block: Scope.Block = .{
18101879 .parent = null,
1880 .inst_table = &inst_table,
18111881 .func = func,
18121882 .decl = decl,
18131883 .instructions = .{},
18141884 .arena = &arena.allocator,
1885 .inlining = null,
18151886 .is_comptime = false,
18161887 };
18171888 defer inner_block.instructions.deinit(self.gpa);
18181889
1819 const fn_zir = func.analysis.queued;
1820 defer fn_zir.arena.promote(self.gpa).deinit();
1821 func.analysis = .{ .in_progress = {} };
1890 func.state = .in_progress;
18221891 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
18261895 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 };
18281898 log.debug("set {s} to success\n", .{decl.name});
18291899}
18301900
......@@ -2321,7 +2391,7 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn
23212391 self.ensureDeclAnalyzed(decl) catch |err| {
23222392 if (scope.cast(Scope.Block)) |block| {
23232393 if (block.func) |func| {
2324 func.analysis = .dependency_failure;
2394 func.state = .dependency_failure;
23252395 } else {
23262396 block.decl.analysis = .dependency_failure;
23272397 }
......@@ -3020,11 +3090,20 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Com
30203090 },
30213091 .block => {
30223092 const block = scope.cast(Scope.Block).?;
3023 if (block.func) |func| {
3024 func.analysis = .sema_failure;
3093 if (block.inlining) |inlining| {
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 }
30253100 } else {
3026 block.decl.analysis = .sema_failure;
3027 block.decl.generation = self.generation;
3101 if (block.func) |func| {
3102 func.state = .sema_failure;
3103 } else {
3104 block.decl.analysis = .sema_failure;
3105 block.decl.generation = self.generation;
3106 }
30283107 }
30293108 self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);
30303109 },
......@@ -3380,10 +3459,12 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic
33803459
33813460 var fail_block: Scope.Block = .{
33823461 .parent = parent_block,
3462 .inst_table = parent_block.inst_table,
33833463 .func = parent_block.func,
33843464 .decl = parent_block.decl,
33853465 .instructions = .{},
33863466 .arena = parent_block.arena,
3467 .inlining = parent_block.inlining,
33873468 .is_comptime = parent_block.is_comptime,
33883469 };
33893470 defer fail_block.instructions.deinit(mod.gpa);
......@@ -3427,3 +3508,34 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void
34273508 return mod.fail(scope, src, "variable of type '{}' must be const or comptime", .{ty});
34283509 }
34293510}
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
384384 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
385385 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
386386 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);
388388 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
389389 } else {
390390 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
426426 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
427427 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
428428 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);
430430 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
431431 } else {
432432 return mod.failTok(parent_scope, src, "continue expression outside loop", .{});
......@@ -551,7 +551,7 @@ fn varDecl(
551551 }
552552 const tree = scope.tree();
553553 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
556556 // Local variables shadowing detection, including function parameters.
557557 {
......@@ -843,7 +843,7 @@ fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_ins
843843fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst {
844844 const tree = scope.tree();
845845 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
848848 return addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{});
849849}
......@@ -864,7 +864,7 @@ fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Erro
864864
865865 for (decls) |decl, i| {
866866 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);
868868 }
869869
870870 // analyzing the error set results in a decl ref, so we might need to dereference it
......@@ -988,36 +988,16 @@ fn orelseCatchExpr(
988988/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.
989989/// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used.
990990fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
991 const ident_name_1 = try identifierTokenString(mod, scope, token1);
992 const ident_name_2 = try identifierTokenString(mod, scope, token2);
991 const ident_name_1 = try mod.identifierTokenString(scope, token1);
992 const ident_name_2 = try mod.identifierTokenString(scope, token2);
993993 return mem.eql(u8, ident_name_1, ident_name_2);
994994}
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
1016996pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
1017997 const tree = scope.tree();
1018998 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
10221002 return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});
10231003}
......@@ -1936,7 +1916,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo
19361916 defer tracy.end();
19371917
19381918 const tree = scope.tree();
1939 const ident_name = try identifierTokenString(mod, scope, ident.token);
1919 const ident_name = try mod.identifierTokenString(scope, ident.token);
19401920 const src = tree.token_locs[ident.token].start;
19411921 if (mem.eql(u8, ident_name, "_")) {
19421922 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 {
532532 self.code.items.len += 4;
533533
534534 try self.dbgSetPrologueEnd();
535 try self.genBody(self.mod_fn.analysis.success);
535 try self.genBody(self.mod_fn.body);
536536
537537 const stack_end = self.max_end_stack;
538538 if (stack_end > math.maxInt(i32))
......@@ -576,7 +576,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
576576 });
577577 } else {
578578 try self.dbgSetPrologueEnd();
579 try self.genBody(self.mod_fn.analysis.success);
579 try self.genBody(self.mod_fn.body);
580580 try self.dbgSetEpilogueBegin();
581581 }
582582 },
......@@ -593,7 +593,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
593593
594594 try self.dbgSetPrologueEnd();
595595
596 try self.genBody(self.mod_fn.analysis.success);
596 try self.genBody(self.mod_fn.body);
597597
598598 // Backpatch stack offset
599599 const stack_end = self.max_end_stack;
......@@ -638,13 +638,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
638638 writeInt(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32());
639639 } else {
640640 try self.dbgSetPrologueEnd();
641 try self.genBody(self.mod_fn.analysis.success);
641 try self.genBody(self.mod_fn.body);
642642 try self.dbgSetEpilogueBegin();
643643 }
644644 },
645645 else => {
646646 try self.dbgSetPrologueEnd();
647 try self.genBody(self.mod_fn.analysis.success);
647 try self.genBody(self.mod_fn.body);
648648 try self.dbgSetEpilogueBegin();
649649 },
650650 }
src/codegen/c.zig+1-1
......@@ -275,7 +275,7 @@ pub fn generate(file: *C, module: *Module, decl: *Decl) !void {
275275 try writer.writeAll(" {");
276276
277277 const func: *Module.Fn = func_payload.data;
278 const instructions = func.analysis.success.instructions;
278 const instructions = func.body.instructions;
279279 if (instructions.len > 0) {
280280 try writer.writeAll("\n");
281281 for (instructions) |inst| {
src/codegen/wasm.zig+1-1
......@@ -63,7 +63,7 @@ pub fn genCode(buf: *ArrayList(u8), decl: *Decl) !void {
6363 // TODO: check for and handle death of instructions
6464 const tv = decl.typed_value.most_recent.typed_value;
6565 const mod_fn = tv.val.castTag(.function).?.data;
66 for (mod_fn.analysis.success.instructions) |inst| try genInst(buf, decl, inst);
66 for (mod_fn.body.instructions) |inst| try genInst(buf, decl, inst);
6767
6868 // Write 'end' opcode
6969 try writer.writeByte(0x0B);
src/config.zig.in-1
......@@ -2,7 +2,6 @@ pub const have_llvm = true;
22pub const version: [:0]const u8 = "@ZIG_VERSION@";
33pub const semver = try @import("std").SemanticVersion.parse(version);
44pub const log_scopes: []const []const u8 = &[_][]const u8{};
5pub const zir_dumps: []const []const u8 = &[_][]const u8{};
65pub const enable_tracy = false;
76pub const is_stage1 = true;
87pub const skip_non_native = false;
src/link/Elf.zig-10
......@@ -2178,16 +2178,6 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
21782178 else => false,
21792179 };
21802180 if (is_fn) {
2181 const zir_dumps = if (std.builtin.is_test) &[0][]const u8{} else build_options.zir_dumps;
2182 if (zir_dumps.len != 0) {
2183 for (zir_dumps) |fn_name| {
2184 if (mem.eql(u8, mem.spanZ(decl.name), fn_name)) {
2185 std.debug.print("\n{s}\n", .{decl.name});
2186 typed_value.val.castTag(.function).?.data.dump(module.*);
2187 }
2188 }
2189 }
2190
21912181 // For functions we need to add a prologue to the debug line program.
21922182 try dbg_line_buffer.ensureCapacity(26);
21932183
src/link/MachO/DebugSymbols.zig-10
......@@ -936,16 +936,6 @@ pub fn initDeclDebugBuffers(
936936 const typed_value = decl.typed_value.most_recent.typed_value;
937937 switch (typed_value.ty.zigTypeTag()) {
938938 .Fn => {
939 const zir_dumps = if (std.builtin.is_test) &[0][]const u8{} else build_options.zir_dumps;
940 if (zir_dumps.len != 0) {
941 for (zir_dumps) |fn_name| {
942 if (mem.eql(u8, mem.spanZ(decl.name), fn_name)) {
943 std.debug.print("\n{}\n", .{decl.name});
944 typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*);
945 }
946 }
947 }
948
949939 // For functions we need to add a prologue to the debug line program.
950940 try dbg_line_buffer.ensureCapacity(26);
951941
src/llvm_backend.zig+1-1
......@@ -294,7 +294,7 @@ pub const LLVMIRModule = struct {
294294 const entry_block = llvm_func.appendBasicBlock("Entry");
295295 self.builder.positionBuilderAtEnd(entry_block);
296296
297 const instructions = func.analysis.success.instructions;
297 const instructions = func.body.instructions;
298298 for (instructions) |inst| {
299299 switch (inst.tag) {
300300 .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),
src/main.zig+1-1
......@@ -1818,7 +1818,7 @@ fn buildOutputType(
18181818 };
18191819
18201820 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),
18221822 else => |e| return e,
18231823 };
18241824 try comp.makeBinFileExecutable();
src/value.zig+8-5
......@@ -330,11 +330,14 @@ pub const Value = extern union {
330330 .int_type => return self.copyPayloadShallow(allocator, Payload.IntType),
331331 .int_u64 => return self.copyPayloadShallow(allocator, Payload.U64),
332332 .int_i64 => return self.copyPayloadShallow(allocator, Payload.I64),
333 .int_big_positive => {
334 @panic("TODO implement copying of big ints");
335 },
336 .int_big_negative => {
337 @panic("TODO implement copying of big ints");
333 .int_big_positive, .int_big_negative => {
334 const old_payload = self.cast(Payload.BigInt).?;
335 const new_payload = try allocator.create(Payload.BigInt);
336 new_payload.* = .{
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 };
338341 },
339342 .function => return self.copyPayloadShallow(allocator, Payload.Function),
340343 .extern_fn => return self.copyPayloadShallow(allocator, Payload.Decl),
src/zir.zig+324-37
......@@ -25,12 +25,13 @@ pub const Decl = struct {
2525
2626/// These are instructions that correspond to the ZIR text format. See `ir.Inst` for
2727/// 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.
2831pub const Inst = struct {
2932 tag: Tag,
3033 /// Byte offset into the source.
3134 src: usize,
32 /// Pre-allocated field for mapping ZIR text instructions to post-analysis instructions.
33 analyzed_inst: ?*ir.Inst = null,
3435
3536 /// These names are used directly as the instruction names in the text format.
3637 pub const Tag = enum {
......@@ -793,7 +794,9 @@ pub const Inst = struct {
793794 fn_type: *Inst,
794795 body: Module.Body,
795796 },
796 kw_args: struct {},
797 kw_args: struct {
798 is_inline: bool = false,
799 },
797800 };
798801
799802 pub const FnType = struct {
......@@ -1847,44 +1850,325 @@ pub fn emit(allocator: *Allocator, old_module: *IrModule) !Module {
18471850/// For debugging purposes, prints a function representation to stderr.
18481851pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void {
18491852 const allocator = old_module.gpa;
1850 var ctx: EmitZIR = .{
1853 var ctx: DumpTzir = .{
18511854 .allocator = allocator,
1852 .decls = .{},
18531855 .arena = std.heap.ArenaAllocator.init(allocator),
18541856 .old_module = &old_module,
1855 .next_auto_name = 0,
1856 .names = std.StringArrayHashMap(void).init(allocator),
1857 .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),
1858 .indent = 0,
1859 .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator),
1860 .loop_table = std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop).init(allocator),
1861 .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator),
1862 .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator),
1857 .module_fn = module_fn,
1858 .indent = 2,
1859 .inst_table = DumpTzir.InstTable.init(allocator),
1860 .partial_inst_table = DumpTzir.InstTable.init(allocator),
1861 .const_table = DumpTzir.InstTable.init(allocator),
18631862 };
1864 defer ctx.metadata.deinit();
1865 defer ctx.body_metadata.deinit();
1866 defer ctx.block_table.deinit();
1867 defer ctx.loop_table.deinit();
1868 defer ctx.decls.deinit(allocator);
1869 defer ctx.names.deinit();
1870 defer ctx.primitive_table.deinit();
1863 defer ctx.inst_table.deinit();
1864 defer ctx.partial_inst_table.deinit();
1865 defer ctx.const_table.deinit();
18711866 defer ctx.arena.deinit();
18721867
1873 const fn_ty = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
1874 _ = ctx.emitFn(module_fn, 0, fn_ty) catch |err| {
1875 std.debug.print("unable to dump function: {s}\n", .{@errorName(err)});
1876 return;
1877 };
1878 var module = Module{
1879 .decls = ctx.decls.items,
1880 .arena = ctx.arena,
1881 .metadata = ctx.metadata,
1882 .body_metadata = ctx.body_metadata,
1883 };
1884
1885 module.dump();
1868 switch (module_fn.state) {
1869 .queued => std.debug.print("(queued)", .{}),
1870 .inline_only => std.debug.print("(inline_only)", .{}),
1871 .in_progress => std.debug.print("(in_progress)", .{}),
1872 .sema_failure => std.debug.print("(sema_failure)", .{}),
1873 .dependency_failure => std.debug.print("(dependency_failure)", .{}),
1874 .success => {
1875 const writer = std.io.getStdErr().writer();
1876 ctx.dump(module_fn.body, writer) catch @panic("failed to dump TZIR");
1877 },
1878 }
18861879}
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
18882172const EmitZIR = struct {
18892173 allocator: *Allocator,
18902174 arena: std.heap.ArenaAllocator,
......@@ -2072,11 +2356,12 @@ const EmitZIR = struct {
20722356 var instructions = std.ArrayList(*Inst).init(self.allocator);
20732357 defer instructions.deinit();
20742358
2075 switch (module_fn.analysis) {
2359 switch (module_fn.state) {
20762360 .queued => unreachable,
20772361 .in_progress => unreachable,
2078 .success => |body| {
2079 try self.emitBody(body, &inst_table, &instructions);
2362 .inline_only => unreachable,
2363 .success => {
2364 try self.emitBody(module_fn.body, &inst_table, &instructions);
20802365 },
20812366 .sema_failure => {
20822367 const err_msg = self.old_module.failed_decls.get(module_fn.owner_decl).?;
......@@ -2154,7 +2439,9 @@ const EmitZIR = struct {
21542439 .fn_type = fn_type.inst,
21552440 .body = .{ .instructions = arena_instrs },
21562441 },
2157 .kw_args = .{},
2442 .kw_args = .{
2443 .is_inline = module_fn.state == .inline_only,
2444 },
21582445 };
21592446 return self.emitUnnamedDecl(&fn_inst.base);
21602447 }
src/zir_sema.zig+189-88
......@@ -159,16 +159,11 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
159159 }
160160}
161161
162pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void {
163 for (body.instructions) |src_inst, i| {
164 const analyzed_inst = try analyzeInst(mod, scope, src_inst);
165 src_inst.analyzed_inst = analyzed_inst;
162pub fn analyzeBody(mod: *Module, block: *Scope.Block, body: zir.Module.Body) !void {
163 for (body.instructions) |src_inst| {
164 const analyzed_inst = try analyzeInst(mod, &block.base, src_inst);
165 try block.inst_table.putNoClobber(src_inst, analyzed_inst);
166166 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 }
172167 break;
173168 }
174169 }
......@@ -180,8 +175,8 @@ pub fn analyzeBodyValueAsType(
180175 zir_result_inst: *zir.Inst,
181176 body: zir.Module.Body,
182177) !Type {
183 try analyzeBody(mod, &block_scope.base, body);
184 const result_inst = zir_result_inst.analyzed_inst.?;
178 try analyzeBody(mod, block_scope, body);
179 const result_inst = block_scope.inst_table.get(zir_result_inst).?;
185180 const val = try mod.resolveConstValue(&block_scope.base, result_inst);
186181 return val.toType(block_scope.base.arena());
187182}
......@@ -264,30 +259,9 @@ fn resolveCompleteZirDecl(mod: *Module, scope: *Scope, src_decl: *zir.Decl) Inne
264259 return decl;
265260}
266261
267/// TODO Look into removing this function. The body is only needed for .zir files, not .zig files.
268pub fn resolveInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
269 if (old_inst.analyzed_inst) |inst| return inst;
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);
262pub fn resolveInst(mod: *Module, scope: *Scope, zir_inst: *zir.Inst) InnerError!*Inst {
263 const block = scope.cast(Scope.Block).?;
264 return block.inst_table.get(zir_inst).?; // Instruction does not dominate all uses!
291265}
292266
293267fn 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
575549}
576550
577551fn 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 }
579558 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
580559 const param_index = b.instructions.items.len;
581560 const param_count = fn_ty.fnParamLen();
......@@ -608,15 +587,17 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError
608587
609588 var child_block: Scope.Block = .{
610589 .parent = parent_block,
590 .inst_table = parent_block.inst_table,
611591 .func = parent_block.func,
612592 .decl = parent_block.decl,
613593 .instructions = .{},
614594 .arena = parent_block.arena,
595 .inlining = parent_block.inlining,
615596 .is_comptime = parent_block.is_comptime,
616597 };
617598 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
621602 // 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
630611
631612 var child_block: Scope.Block = .{
632613 .parent = parent_block,
614 .inst_table = parent_block.inst_table,
633615 .func = parent_block.func,
634616 .decl = parent_block.decl,
635617 .instructions = .{},
636618 .arena = parent_block.arena,
637619 .label = null,
620 .inlining = parent_block.inlining,
638621 .is_comptime = parent_block.is_comptime or is_comptime,
639622 };
640623 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
644627 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
668651
669652 var child_block: Scope.Block = .{
670653 .parent = parent_block,
654 .inst_table = parent_block.inst_table,
671655 .func = parent_block.func,
672656 .decl = parent_block.decl,
673657 .instructions = .{},
......@@ -675,38 +659,53 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt
675659 // TODO @as here is working around a stage1 miscompilation bug :(
676660 .label = @as(?Scope.Block.Label, Scope.Block.Label{
677661 .zir_block = inst,
678 .results = .{},
679 .block_inst = block_inst,
662 .merges = .{
663 .results = .{},
664 .block_inst = block_inst,
665 },
680666 }),
667 .inlining = parent_block.inlining,
681668 .is_comptime = is_comptime or parent_block.is_comptime,
682669 };
683 const label = &child_block.label.?;
670 const merges = &child_block.label.?.merges;
684671
685672 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
690688 // Blocks must terminate with noreturn instruction.
691689 assert(child_block.instructions.items.len != 0);
692690 assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());
693691
694 if (label.results.items.len == 0) {
695 // No need for a block instruction. We can put the new instructions directly into the parent block.
692 if (merges.results.items.len == 0) {
693 // No need for a block instruction. We can put the new instructions
694 // directly into the parent block.
696695 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items);
697696 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
698697 return copied_instructions[copied_instructions.len - 1];
699698 }
700 if (label.results.items.len == 1) {
699 if (merges.results.items.len == 1) {
701700 const last_inst_index = child_block.instructions.items.len - 1;
702701 const last_inst = child_block.instructions.items[last_inst_index];
703702 if (last_inst.breakBlock()) |br_block| {
704 if (br_block == block_inst) {
703 if (br_block == merges.block_inst) {
705704 // No need for a block instruction. We can put the new instructions directly into the parent block.
706705 // Here we omit the break instruction.
707706 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]);
708707 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
709 return label.results.items[0];
708 return merges.results.items[0];
710709 }
711710 }
712711 }
......@@ -715,10 +714,10 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt
715714
716715 // Need to set the type and emit the Block instruction. This allows machine code generation
717716 // 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);
719 block_inst.base.ty = try mod.resolvePeerTypes(scope, label.results.items);
720 block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
721 return &block_inst.base;
717 try parent_block.instructions.append(mod.gpa, &merges.block_inst.base);
718 merges.block_inst.base.ty = try mod.resolvePeerTypes(scope, merges.results.items);
719 merges.block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
720 return &merges.block_inst.base;
722721}
723722
724723fn 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
826825
827826 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
830920 return mod.addCall(b, inst.base.src, ret_type, func, casted_args);
831921}
832922
833923fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {
834924 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 };
848925 const new_func = try scope.arena().create(Module.Fn);
849926 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,
851930 .owner_decl = scope.decl().?,
852931 };
853932 return mod.constInst(scope, fn_inst.base.src, .{
......@@ -1312,17 +1391,17 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
13121391 const item = try mod.resolveConstValue(scope, casted);
13131392
13141393 if (target_val.eql(item)) {
1315 try analyzeBody(mod, scope, case.body);
1394 try analyzeBody(mod, scope.cast(Scope.Block).?, case.body);
13161395 return mod.constNoReturn(scope, inst.base.src);
13171396 }
13181397 }
1319 try analyzeBody(mod, scope, inst.positionals.else_body);
1398 try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body);
13201399 return mod.constNoReturn(scope, inst.base.src);
13211400 }
13221401
13231402 if (inst.positionals.cases.len == 0) {
13241403 // 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);
13261405 return mod.constNoReturn(scope, inst.base.src);
13271406 }
13281407
......@@ -1331,10 +1410,12 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
13311410
13321411 var case_block: Scope.Block = .{
13331412 .parent = parent_block,
1413 .inst_table = parent_block.inst_table,
13341414 .func = parent_block.func,
13351415 .decl = parent_block.decl,
13361416 .instructions = .{},
13371417 .arena = parent_block.arena,
1418 .inlining = parent_block.inlining,
13381419 .is_comptime = parent_block.is_comptime,
13391420 };
13401421 defer case_block.instructions.deinit(mod.gpa);
......@@ -1347,7 +1428,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
13471428 const casted = try mod.coerce(scope, target.ty, resolved);
13481429 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
13521433 cases[i] = .{
13531434 .item = item,
......@@ -1356,7 +1437,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
13561437 }
13571438
13581439 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
13611442 const else_body: ir.Body = .{
13621443 .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
15091590 return mod.fail(scope, inst.base.src, "unable to find '{s}'", .{operand});
15101591 },
15111592 else => {
1512 // TODO user friendly error to string
1593 // TODO: make sure this gets retried and not cached
15131594 return mod.fail(scope, inst.base.src, "unable to open '{s}': {s}", .{ operand, @errorName(err) });
15141595 },
15151596 };
......@@ -1674,24 +1755,26 @@ fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir
16741755 }
16751756 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) {
16781759 .add => blk: {
16791760 const val = if (is_int)
1680 Module.intAdd(scope.arena(), lhs_val, rhs_val)
1761 try Module.intAdd(scope.arena(), lhs_val, rhs_val)
16811762 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);
16831764 break :blk val;
16841765 },
16851766 .sub => blk: {
16861767 const val = if (is_int)
1687 Module.intSub(scope.arena(), lhs_val, rhs_val)
1768 try Module.intSub(scope.arena(), lhs_val, rhs_val)
16881769 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);
16901771 break :blk val;
16911772 },
16921773 else => return mod.fail(scope, inst.base.src, "TODO Implement arithmetic operand '{s}'", .{@tagName(inst.base.tag)}),
16931774 };
16941775
1776 log.debug("{s}({}, {}) result: {}", .{ @tagName(inst.base.tag), lhs_val, rhs_val, value });
1777
16951778 return mod.constInst(scope, inst.base.src, .{
16961779 .ty = res_type,
16971780 .val = value,
......@@ -1860,35 +1943,39 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
18601943 const uncasted_cond = try resolveInst(mod, scope, inst.positionals.condition);
18611944 const cond = try mod.coerce(scope, Type.initTag(.bool), uncasted_cond);
18621945
1946 const parent_block = scope.cast(Scope.Block).?;
1947
18631948 if (try mod.resolveDefinedValue(scope, cond)) |cond_val| {
18641949 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.*);
18661951 return mod.constNoReturn(scope, inst.base.src);
18671952 }
18681953
1869 const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);
1870
18711954 var true_block: Scope.Block = .{
18721955 .parent = parent_block,
1956 .inst_table = parent_block.inst_table,
18731957 .func = parent_block.func,
18741958 .decl = parent_block.decl,
18751959 .instructions = .{},
18761960 .arena = parent_block.arena,
1961 .inlining = parent_block.inlining,
18771962 .is_comptime = parent_block.is_comptime,
18781963 };
18791964 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
18821967 var false_block: Scope.Block = .{
18831968 .parent = parent_block,
1969 .inst_table = parent_block.inst_table,
18841970 .func = parent_block.func,
18851971 .decl = parent_block.decl,
18861972 .instructions = .{},
18871973 .arena = parent_block.arena,
1974 .inlining = parent_block.inlining,
18881975 .is_comptime = parent_block.is_comptime,
18891976 };
18901977 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
18931980 const then_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) };
18941981 const else_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) };
......@@ -1912,12 +1999,26 @@ fn analyzeInstUnreachable(
19121999
19132000fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
19142001 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
19162010 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand);
19172011}
19182012
19192013fn 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
19212022 if (b.func) |func| {
19222023 // Need to emit a compile error if returning void is not allowed.
19232024 const void_inst = try mod.constVoid(scope, inst.base.src);
......@@ -1949,9 +2050,9 @@ fn analyzeBreak(
19492050 while (opt_block) |block| {
19502051 if (block.label) |*label| {
19512052 if (label.zir_block == zir_block) {
1952 try label.results.append(mod.gpa, operand);
1953 const b = try mod.requireRuntimeBlock(scope, src);
1954 return mod.addBr(b, src, label.block_inst, operand);
2053 try label.merges.results.append(mod.gpa, operand);
2054 const b = try mod.requireFunctionBlock(scope, src);
2055 return mod.addBr(b, src, label.merges.block_inst, operand);
19552056 }
19562057 }
19572058 opt_block = block.parent;
test/stage2/test.zig+147-2
......@@ -27,7 +27,6 @@ const wasi = std.zig.CrossTarget{
2727};
2828
2929pub fn addCases(ctx: *TestContext) !void {
30 try @import("zir.zig").addCases(ctx);
3130 try @import("cbe.zig").addCases(ctx);
3231 try @import("spu-ii.zig").addCases(ctx);
3332 try @import("arm.zig").addCases(ctx);
......@@ -318,7 +317,7 @@ pub fn addCases(ctx: *TestContext) !void {
318317 }
319318
320319 {
321 var case = ctx.exe("adding numbers at runtime", linux_x64);
320 var case = ctx.exe("adding numbers at runtime and comptime", linux_x64);
322321 case.addCompareOutput(
323322 \\export fn _start() noreturn {
324323 \\ add(3, 4);
......@@ -342,6 +341,54 @@ pub fn addCases(ctx: *TestContext) !void {
342341 ,
343342 "",
344343 );
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 );
345392 }
346393
347394 {
......@@ -1331,4 +1378,102 @@ pub fn addCases(ctx: *TestContext) !void {
13311378 \\}
13321379 , &[_][]const u8{":2:9: error: variable of type '@Type(.Null)' must be const or comptime"});
13331380 }
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 }
13341479}
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}