| author | |
| committer | |
| log | d8f3f14532c4b5d65377efaef015c3855137dccf |
| tree | d0927df77323d64bff52501b50ef8543a077d4d8 |
| parent | 3d151fbfc8db71f87ee84dd33c49910584708a04 |
| parent | 654832253a7857e78aab85e28ed09fb16b632dd2 |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
stage2: comptime function calls and inline function calls17 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 | } |
| 221 | 221 | ||
| 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{}; | ||
| 224 | 223 | ||
| 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); |
| 278 | 277 | ||
| 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 are | 1475 | // 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 | } |
| 1481 | 1486 | ||
| 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 | } |
| 270 | 270 | ||
| 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 by | 286 | /// 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`. |
| 283 | pub const Fn = struct { | 288 | pub 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_decls | 304 | /// 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 complete | 306 | /// 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 | }; |
| 308 | 311 | ||
| 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 | }; |
| 326 | 317 | ||
| ... | @@ -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 DeclAnalysis | 763 | /// 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, |
| 773 | 768 | ||
| 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 | }; |
| 780 | 810 | ||
| 781 | /// This is a temporary structure, references to it are valid only | 811 | /// 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(); |
| 993 | 1023 | ||
| 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).?; |
| 1000 | 1030 | ||
| 1001 | decl.analysis = .in_progress; | 1031 | decl.analysis = .in_progress; |
| 1002 | 1032 | ||
| ... | @@ -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 | }, .{}); |
| 1064 | 1094 | ||
| 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 | } |
| 1068 | 1098 | ||
| ... | @@ -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); |
| 1073 | 1103 | ||
| 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); |
| 1115 | 1150 | ||
| 1116 | const fn_zir = blk: { | 1151 | const fn_zir: zir.Module.Body = blk: { |
| 1117 | // This scope's arena memory is discarded after the ZIR generation | 1152 | // 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 identifierTokenString | 1166 | 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 | } |
| 1167 | 1199 | ||
| 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 | } |
| 1171 | 1203 | ||
| 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 | }; |
| 1181 | 1208 | ||
| 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 | }; |
| 1190 | 1229 | ||
| 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; |
| 1193 | 1233 | ||
| 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 | } | ||
| 1197 | 1241 | ||
| 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; |
| 1213 | 1257 | ||
| 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 global | 1259 | // 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 | } |
| 1223 | 1267 | ||
| 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); |
| 1244 | 1296 | ||
| 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 { |
| 1303 | 1360 | ||
| 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 | } |
| 1309 | 1366 | ||
| 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 | }); | ||
| 1320 | 1384 | ||
| 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().?; |
| 1325 | 1389 | ||
| ... | @@ -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 | } |
| 1353 | 1417 | ||
| ... | @@ -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); |
| 1424 | 1488 | ||
| 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 | } |
| 1429 | 1493 | ||
| 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); |
| 1439 | 1508 | ||
| 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 | }); |
| 1443 | 1512 | ||
| ... | @@ -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 | } |
| 1498 | 1567 | ||
| 1499 | fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree { | 1568 | pub 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(); |
| 1502 | 1571 | ||
| 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 |
| 1549 | 1616 | ||
| 1550 | // We may be analyzing it for the first time, or this may be | 1617 | // 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(); |
| 1554 | 1621 | ||
| 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); |
| 1818 | 1889 | ||
| 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}); |
| 1823 | 1892 | ||
| 1824 | try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body); | 1893 | try zir_sema.analyzeBody(self, &inner_block, func.zir); |
| 1825 | 1894 | ||
| 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 | } |
| 1830 | 1900 | ||
| ... | @@ -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 |
| 3380 | 3459 | ||
| 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()) | ||
| 3513 | pub 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 | |||
| 3532 | pub 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); |
| 555 | 555 | ||
| 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 |
| 843 | fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst { | 843 | fn 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); |
| 847 | 847 | ||
| 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 |
| 864 | 864 | ||
| 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 | } |
| 869 | 869 | ||
| 870 | // analyzing the error set results in a decl ref, so we might need to dereference it | 870 | // 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. |
| 990 | fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool { | 990 | fn 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 | } |
| 995 | 995 | ||
| 996 | /// Identifier token -> String (allocated in scope.arena()) | ||
| 997 | fn 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 | |||
| 1016 | pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst { | 996 | pub 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; |
| 1019 | 999 | ||
| 1020 | const ident_name = try identifierTokenString(mod, scope, node.token); | 1000 | const ident_name = try mod.identifierTokenString(scope, node.token); |
| 1021 | 1001 | ||
| 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(); |
| 1937 | 1917 | ||
| 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; |
| 533 | 533 | ||
| 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); |
| 536 | 536 | ||
| 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 { |
| 593 | 593 | ||
| 594 | try self.dbgSetPrologueEnd(); | 594 | try self.dbgSetPrologueEnd(); |
| 595 | 595 | ||
| 596 | try self.genBody(self.mod_fn.analysis.success); | 596 | try self.genBody(self.mod_fn.body); |
| 597 | 597 | ||
| 598 | // Backpatch stack offset | 598 | // 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(" {"); |
| 276 | 276 | ||
| 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 instructions | 63 | // 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); |
| 67 | 67 | ||
| 68 | // Write 'end' opcode | 68 | // 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; |
| 2 | pub const version: [:0]const u8 = "@ZIG_VERSION@"; | 2 | pub const version: [:0]const u8 = "@ZIG_VERSION@"; |
| 3 | pub const semver = try @import("std").SemanticVersion.parse(version); | 3 | pub const semver = try @import("std").SemanticVersion.parse(version); |
| 4 | pub const log_scopes: []const []const u8 = &[_][]const u8{}; | 4 | pub const log_scopes: []const []const u8 = &[_][]const u8{}; |
| 5 | pub const zir_dumps: []const []const u8 = &[_][]const u8{}; | ||
| 6 | pub const enable_tracy = false; | 5 | pub const enable_tracy = false; |
| 7 | pub const is_stage1 = true; | 6 | pub const is_stage1 = true; |
| 8 | pub const skip_non_native = false; | 7 | pub 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); |
| 2193 | 2183 |
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); |
| 951 | 941 |
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); |
| 296 | 296 | ||
| 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 | }; |
| 1819 | 1819 | ||
| 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 { |
| 25 | 25 | ||
| 26 | /// These are instructions that correspond to the ZIR text format. See `ir.Inst` for | 26 | /// 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. | ||
| 28 | pub const Inst = struct { | 31 | pub 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, | ||
| 34 | 35 | ||
| 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 | }; |
| 798 | 801 | ||
| 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. |
| 1848 | pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void { | 1851 | pub 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(); |
| 1872 | 1867 | ||
| 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 | } |
| 1887 | 1880 | ||
| 1881 | const 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 | |||
| 1888 | const EmitZIR = struct { | 2172 | const 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(); |
| 2074 | 2358 | ||
| 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 | } |
| 161 | 161 | ||
| 162 | pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void { | 162 | pub 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 | } |
| 266 | 261 | ||
| 267 | /// TODO Look into removing this function. The body is only needed for .zir files, not .zig files. | 262 | pub fn resolveInst(mod: *Module, scope: *Scope, zir_inst: *zir.Inst) InnerError!*Inst { |
| 268 | pub 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 | } |
| 292 | 266 | ||
| 293 | fn resolveConstString(mod: *Module, scope: *Scope, old_inst: *zir.Inst) ![]u8 { | 267 | fn 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 | } |
| 576 | 550 | ||
| 577 | fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst { | 551 | fn 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 |
| 608 | 587 | ||
| 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); |
| 618 | 599 | ||
| 619 | try analyzeBody(mod, &child_block.base, inst.positionals.body); | 600 | try analyzeBody(mod, &child_block, inst.positionals.body); |
| 620 | 601 | ||
| 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. |
| 622 | 603 | ||
| ... | @@ -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 |
| 630 | 611 | ||
| 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); |
| 641 | 624 | ||
| 642 | try analyzeBody(mod, &child_block.base, inst.positionals.body); | 625 | try analyzeBody(mod, &child_block, inst.positionals.body); |
| 643 | 626 | ||
| 644 | try parent_block.instructions.appendSlice(mod.gpa, child_block.instructions.items); | 627 | try parent_block.instructions.appendSlice(mod.gpa, child_block.instructions.items); |
| 645 | 628 | ||
| ... | @@ -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 |
| 668 | 651 | ||
| 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; |
| 684 | 671 | ||
| 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); |
| 687 | 674 | ||
| 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 | |||
| 680 | fn 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).?; | ||
| 689 | 687 | ||
| 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()); |
| 693 | 691 | ||
| 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 |
| 715 | 714 | ||
| 716 | // Need to set the type and emit the Block instruction. This allows machine code generation | 715 | // 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 | } |
| 723 | 722 | ||
| 724 | fn analyzeInstBreakpoint(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | 723 | fn 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 |
| 826 | 825 | ||
| 827 | const ret_type = func.ty.fnReturnType(); | 826 | const ret_type = func.ty.fnReturnType(); |
| 828 | 827 | ||
| 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 | } |
| 832 | 922 | ||
| 833 | fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst { | 923 | fn 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); |
| 1313 | 1392 | ||
| 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 | } |
| 1322 | 1401 | ||
| 1323 | if (inst.positionals.cases.len == 0) { | 1402 | if (inst.positionals.cases.len == 0) { |
| 1324 | // no cases just analyze else_branch | 1403 | // 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 | } |
| 1328 | 1407 | ||
| ... | @@ -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 |
| 1331 | 1410 | ||
| 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); |
| 1349 | 1430 | ||
| 1350 | try analyzeBody(mod, &case_block.base, case.body); | 1431 | try analyzeBody(mod, &case_block, case.body); |
| 1351 | 1432 | ||
| 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 | } |
| 1357 | 1438 | ||
| 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); |
| 1360 | 1441 | ||
| 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 string | 1593 | // 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; |
| 1676 | 1757 | ||
| 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 | else | 1762 | 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 | else | 1769 | 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 | }; |
| 1694 | 1775 | ||
| 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); |
| 1862 | 1945 | ||
| 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 | } |
| 1868 | 1953 | ||
| 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); |
| 1881 | 1966 | ||
| 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); |
| 1892 | 1979 | ||
| 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( |
| 1912 | 1999 | ||
| 1913 | fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | 2000 | fn 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 | } |
| 1918 | 2012 | ||
| 1919 | fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | 2013 | fn 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 | }; |
| 28 | 28 | ||
| 29 | pub fn addCases(ctx: *TestContext) !void { | 29 | pub 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 | } |
| 319 | 318 | ||
| 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 | } |
| 346 | 393 | ||
| 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const 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. | ||
| 6 | const linux_x64 = std.zig.CrossTarget{ | ||
| 7 | .cpu_arch = .x86_64, | ||
| 8 | .os_tag = .linux, | ||
| 9 | }; | ||
| 10 | |||
| 11 | pub 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 | } | ||