| author | |
| committer | |
| log | 9362f382ab7023592cc1d71044217b847b122406 |
| tree | 3587f4c88b949673a94e995367414d80a5ef68af |
| parent | fea8659b82ea1a785f933c58ba9d65ceb05a4094 |
* remove the -Ddump-zir thing. that's handled through --verbose-ir
* rework Fn to have an is_inline flag without requiring any more memory
on the heap per function.
* implement a rough first version of dumping typed zir (tzir) which is
a lot more helpful for debugging than what we had before. We don't
have a way to parse it though.
* keep track of whether the inline-ness of a function changes because
if it does we have to go update callsites.
* add compile error for inline and export used together.
inline function calls and comptime function calls are implemented the
same way. A block instruction is set up to capture the result, and then
a scope is set up that has a flag for is_comptime and some state if the
scope is being inlined.
when analyzing `ret` instructions, zig looks for inlining state in the
scope, and if found, treats `ret` as a `break` instruction instead, with
the target block being the one set up at the inline callsite.
Follow-up items:
* Complete out the debug TZIR dumping code.
* Don't redundantly generate ZIR for each inline/comptime function
call. Instead we should add a new state enum tag to Fn.
* comptime and inlining branch quotas.
* Add more test cases.13 files changed, 549 insertions(+), 254 deletions(-)
build.zig-2| ... | @@ -220,7 +220,6 @@ pub fn build(b: *Builder) !void { | ... | @@ -220,7 +220,6 @@ pub fn build(b: *Builder) !void { |
| 220 | } | 220 | } |
| 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+9-5| ... | @@ -1459,10 +1459,10 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor | ... | @@ -1459,10 +1459,10 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor |
| 1459 | const module = self.bin_file.options.module.?; | 1459 | const module = self.bin_file.options.module.?; |
| 1460 | if (decl.typed_value.most_recent.typed_value.val.castTag(.function)) |payload| { | 1460 | if (decl.typed_value.most_recent.typed_value.val.castTag(.function)) |payload| { |
| 1461 | const func = payload.data; | 1461 | const func = payload.data; |
| 1462 | switch (func.analysis) { | 1462 | switch (func.bits.state) { |
| 1463 | .queued => module.analyzeFnBody(decl, func) catch |err| switch (err) { | 1463 | .queued => module.analyzeFnBody(decl, func) catch |err| switch (err) { |
| 1464 | error.AnalysisFail => { | 1464 | error.AnalysisFail => { |
| 1465 | assert(func.analysis != .in_progress); | 1465 | assert(func.bits.state != .in_progress); |
| 1466 | continue; | 1466 | continue; |
| 1467 | }, | 1467 | }, |
| 1468 | error.OutOfMemory => return error.OutOfMemory, | 1468 | error.OutOfMemory => return error.OutOfMemory, |
| ... | @@ -1471,12 +1471,16 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor | ... | @@ -1471,12 +1471,16 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor |
| 1471 | .sema_failure, .dependency_failure => continue, | 1471 | .sema_failure, .dependency_failure => continue, |
| 1472 | .success => {}, | 1472 | .success => {}, |
| 1473 | } | 1473 | } |
| 1474 | // Here we tack on additional allocations to the Decl's arena. The allocations are | 1474 | // Here we tack on additional allocations to the Decl's arena. The allocations |
| 1475 | // lifetime annotations in the ZIR. | 1475 | // are lifetime annotations in the ZIR. |
| 1476 | var decl_arena = decl.typed_value.most_recent.arena.?.promote(module.gpa); | 1476 | var decl_arena = decl.typed_value.most_recent.arena.?.promote(module.gpa); |
| 1477 | defer decl.typed_value.most_recent.arena.?.* = decl_arena.state; | 1477 | defer decl.typed_value.most_recent.arena.?.* = decl_arena.state; |
| 1478 | log.debug("analyze liveness of {s}\n", .{decl.name}); | 1478 | log.debug("analyze liveness of {s}\n", .{decl.name}); |
| 1479 | try liveness.analyze(module.gpa, &decl_arena.allocator, func.analysis.success); | 1479 | try liveness.analyze(module.gpa, &decl_arena.allocator, func.data.body); |
| 1480 | |||
| 1481 | if (self.verbose_ir) { | ||
| 1482 | func.dump(module.*); | ||
| 1483 | } | ||
| 1480 | } | 1484 | } |
| 1481 | 1485 | ||
| 1482 | assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); | 1486 | assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); |
src/Module.zig+114-36| ... | @@ -286,23 +286,40 @@ pub const Decl = struct { | ... | @@ -286,23 +286,40 @@ pub const Decl = struct { |
| 286 | /// Extern functions do not have this data structure; they are represented by | 286 | /// Extern functions do not have this data structure; they are represented by |
| 287 | /// the `Decl` only, with a `Value` tag of `extern_fn`. | 287 | /// the `Decl` only, with a `Value` tag of `extern_fn`. |
| 288 | pub const Fn = struct { | 288 | pub const Fn = struct { |
| 289 | /// This memory owned by the Decl's TypedValue.Managed arena allocator. | 289 | bits: packed struct { |
| 290 | analysis: union(enum) { | 290 | /// Get and set this field via `analysis` and `setAnalysis`. |
| 291 | state: Analysis.Tag, | ||
| 292 | /// We carry this state into `Fn` instead of leaving it in the AST so that | ||
| 293 | /// analysis of function calls can happen even on functions whose AST has | ||
| 294 | /// been unloaded from memory. | ||
| 295 | is_inline: bool, | ||
| 296 | unused_bits: u4 = 0, | ||
| 297 | }, | ||
| 298 | /// Get and set this data via `analysis` and `setAnalysis`. | ||
| 299 | data: union { | ||
| 300 | none: void, | ||
| 301 | zir: *ZIR, | ||
| 302 | body: Body, | ||
| 303 | }, | ||
| 304 | owner_decl: *Decl, | ||
| 305 | |||
| 306 | pub const Analysis = union(Tag) { | ||
| 291 | queued: *ZIR, | 307 | queued: *ZIR, |
| 292 | in_progress, | 308 | in_progress, |
| 293 | /// There will be a corresponding ErrorMsg in Module.failed_decls | ||
| 294 | sema_failure, | 309 | sema_failure, |
| 295 | /// This Fn might be OK but it depends on another Decl which did not successfully complete | ||
| 296 | /// semantic analysis. | ||
| 297 | dependency_failure, | 310 | dependency_failure, |
| 298 | success: Body, | 311 | success: Body, |
| 299 | }, | ||
| 300 | owner_decl: *Decl, | ||
| 301 | 312 | ||
| 302 | /// This memory is temporary and points to stack memory for the duration | 313 | pub const Tag = enum(u3) { |
| 303 | /// of Fn analysis. | 314 | queued, |
| 304 | pub const Analysis = struct { | 315 | in_progress, |
| 305 | inner_block: Scope.Block, | 316 | /// There will be a corresponding ErrorMsg in Module.failed_decls |
| 317 | sema_failure, | ||
| 318 | /// This Fn might be OK but it depends on another Decl which did not | ||
| 319 | /// successfully complete semantic analysis. | ||
| 320 | dependency_failure, | ||
| 321 | success, | ||
| 322 | }; | ||
| 306 | }; | 323 | }; |
| 307 | 324 | ||
| 308 | /// Contains un-analyzed ZIR instructions generated from Zig source AST. | 325 | /// Contains un-analyzed ZIR instructions generated from Zig source AST. |
| ... | @@ -311,22 +328,37 @@ pub const Fn = struct { | ... | @@ -311,22 +328,37 @@ pub const Fn = struct { |
| 311 | arena: std.heap.ArenaAllocator.State, | 328 | arena: std.heap.ArenaAllocator.State, |
| 312 | }; | 329 | }; |
| 313 | 330 | ||
| 314 | /// For debugging purposes. | 331 | pub fn analysis(self: Fn) Analysis { |
| 315 | pub fn dump(self: *Fn, mod: Module) void { | 332 | return switch (self.bits.state) { |
| 316 | std.debug.print("Module.Function(name={s}) ", .{self.owner_decl.name}); | 333 | .queued => .{ .queued = self.data.zir }, |
| 317 | switch (self.analysis) { | 334 | .success => .{ .success = self.data.body }, |
| 318 | .queued => { | 335 | .in_progress => .in_progress, |
| 319 | std.debug.print("queued\n", .{}); | 336 | .sema_failure => .sema_failure, |
| 337 | .dependency_failure => .dependency_failure, | ||
| 338 | }; | ||
| 339 | } | ||
| 340 | |||
| 341 | pub fn setAnalysis(self: *Fn, anal: Analysis) void { | ||
| 342 | switch (anal) { | ||
| 343 | .queued => |zir_ptr| { | ||
| 344 | self.bits.state = .queued; | ||
| 345 | self.data = .{ .zir = zir_ptr }; | ||
| 320 | }, | 346 | }, |
| 321 | .in_progress => { | 347 | .success => |body| { |
| 322 | std.debug.print("in_progress\n", .{}); | 348 | self.bits.state = .success; |
| 349 | self.data = .{ .body = body }; | ||
| 323 | }, | 350 | }, |
| 324 | else => { | 351 | .in_progress, .sema_failure, .dependency_failure => { |
| 325 | std.debug.print("\n", .{}); | 352 | self.bits.state = anal; |
| 326 | zir.dumpFn(mod, self); | 353 | self.data = .{ .none = {} }; |
| 327 | }, | 354 | }, |
| 328 | } | 355 | } |
| 329 | } | 356 | } |
| 357 | |||
| 358 | /// For debugging purposes. | ||
| 359 | pub fn dump(self: *Fn, mod: Module) void { | ||
| 360 | zir.dumpFn(mod, self); | ||
| 361 | } | ||
| 330 | }; | 362 | }; |
| 331 | 363 | ||
| 332 | pub const Var = struct { | 364 | pub const Var = struct { |
| ... | @@ -773,13 +805,33 @@ pub const Scope = struct { | ... | @@ -773,13 +805,33 @@ pub const Scope = struct { |
| 773 | instructions: ArrayListUnmanaged(*Inst), | 805 | instructions: ArrayListUnmanaged(*Inst), |
| 774 | /// Points to the arena allocator of DeclAnalysis | 806 | /// Points to the arena allocator of DeclAnalysis |
| 775 | arena: *Allocator, | 807 | arena: *Allocator, |
| 776 | label: ?Label = null, | 808 | label: Label = Label.none, |
| 777 | is_comptime: bool, | 809 | is_comptime: bool, |
| 778 | 810 | ||
| 779 | pub const Label = struct { | 811 | pub const Label = union(enum) { |
| 780 | zir_block: *zir.Inst.Block, | 812 | none, |
| 781 | results: ArrayListUnmanaged(*Inst), | 813 | /// This `Block` maps a block ZIR instruction to the corresponding |
| 782 | block_inst: *Inst.Block, | 814 | /// TZIR instruction for break instruction analysis. |
| 815 | breaking: struct { | ||
| 816 | zir_block: *zir.Inst.Block, | ||
| 817 | merges: Merges, | ||
| 818 | }, | ||
| 819 | /// This `Block` indicates that an inline function call is happening | ||
| 820 | /// and return instructions should be analyzed as a break instruction | ||
| 821 | /// to this TZIR block instruction. | ||
| 822 | inlining: struct { | ||
| 823 | /// We use this to count from 0 so that arg instructions know | ||
| 824 | /// which parameter index they are, without having to store | ||
| 825 | /// a parameter index with each arg instruction. | ||
| 826 | param_index: usize, | ||
| 827 | casted_args: []*Inst, | ||
| 828 | merges: Merges, | ||
| 829 | }, | ||
| 830 | |||
| 831 | pub const Merges = struct { | ||
| 832 | results: ArrayListUnmanaged(*Inst), | ||
| 833 | block_inst: *Inst.Block, | ||
| 834 | }; | ||
| 783 | }; | 835 | }; |
| 784 | 836 | ||
| 785 | /// For debugging purposes. | 837 | /// For debugging purposes. |
| ... | @@ -1189,8 +1241,21 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { | ... | @@ -1189,8 +1241,21 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1189 | break :blk fn_zir; | 1241 | break :blk fn_zir; |
| 1190 | }; | 1242 | }; |
| 1191 | 1243 | ||
| 1244 | const is_inline = blk: { | ||
| 1245 | if (fn_proto.getExternExportInlineToken()) |maybe_inline_token| { | ||
| 1246 | if (tree.token_ids[maybe_inline_token] == .Keyword_inline) { | ||
| 1247 | break :blk true; | ||
| 1248 | } | ||
| 1249 | } | ||
| 1250 | break :blk false; | ||
| 1251 | }; | ||
| 1252 | |||
| 1192 | new_func.* = .{ | 1253 | new_func.* = .{ |
| 1193 | .analysis = .{ .queued = fn_zir }, | 1254 | .bits = .{ |
| 1255 | .state = .queued, | ||
| 1256 | .is_inline = is_inline, | ||
| 1257 | }, | ||
| 1258 | .data = .{ .zir = fn_zir }, | ||
| 1194 | .owner_decl = decl, | 1259 | .owner_decl = decl, |
| 1195 | }; | 1260 | }; |
| 1196 | fn_payload.* = .{ | 1261 | fn_payload.* = .{ |
| ... | @@ -1199,11 +1264,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { | ... | @@ -1199,11 +1264,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1199 | }; | 1264 | }; |
| 1200 | 1265 | ||
| 1201 | var prev_type_has_bits = false; | 1266 | var prev_type_has_bits = false; |
| 1267 | var prev_is_inline = false; | ||
| 1202 | var type_changed = true; | 1268 | var type_changed = true; |
| 1203 | 1269 | ||
| 1204 | if (decl.typedValueManaged()) |tvm| { | 1270 | if (decl.typedValueManaged()) |tvm| { |
| 1205 | prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); | 1271 | prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); |
| 1206 | type_changed = !tvm.typed_value.ty.eql(fn_type); | 1272 | type_changed = !tvm.typed_value.ty.eql(fn_type); |
| 1273 | if (tvm.typed_value.val.castTag(.function)) |payload| { | ||
| 1274 | const prev_func = payload.data; | ||
| 1275 | prev_is_inline = prev_func.bits.is_inline; | ||
| 1276 | } | ||
| 1207 | 1277 | ||
| 1208 | tvm.deinit(self.gpa); | 1278 | tvm.deinit(self.gpa); |
| 1209 | } | 1279 | } |
| ... | @@ -1221,18 +1291,26 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { | ... | @@ -1221,18 +1291,26 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1221 | decl.analysis = .complete; | 1291 | decl.analysis = .complete; |
| 1222 | decl.generation = self.generation; | 1292 | decl.generation = self.generation; |
| 1223 | 1293 | ||
| 1224 | if (fn_type.hasCodeGenBits()) { | 1294 | if (!is_inline and fn_type.hasCodeGenBits()) { |
| 1225 | // We don't fully codegen the decl until later, but we do need to reserve a global | 1295 | // We don't fully codegen the decl until later, but we do need to reserve a global |
| 1226 | // offset table index for it. This allows us to codegen decls out of dependency order, | 1296 | // offset table index for it. This allows us to codegen decls out of dependency order, |
| 1227 | // increasing how many computations can be done in parallel. | 1297 | // increasing how many computations can be done in parallel. |
| 1228 | try self.comp.bin_file.allocateDeclIndexes(decl); | 1298 | try self.comp.bin_file.allocateDeclIndexes(decl); |
| 1229 | try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); | 1299 | try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); |
| 1230 | } else if (prev_type_has_bits) { | 1300 | } else if (!prev_is_inline and prev_type_has_bits) { |
| 1231 | self.comp.bin_file.freeDecl(decl); | 1301 | self.comp.bin_file.freeDecl(decl); |
| 1232 | } | 1302 | } |
| 1233 | 1303 | ||
| 1234 | if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { | 1304 | if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { |
| 1235 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | 1305 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { |
| 1306 | if (is_inline) { | ||
| 1307 | return self.failTok( | ||
| 1308 | &block_scope.base, | ||
| 1309 | maybe_export_token, | ||
| 1310 | "export of inline function", | ||
| 1311 | .{}, | ||
| 1312 | ); | ||
| 1313 | } | ||
| 1236 | const export_src = tree.token_locs[maybe_export_token].start; | 1314 | const export_src = tree.token_locs[maybe_export_token].start; |
| 1237 | const name_loc = tree.token_locs[fn_proto.getNameToken().?]; | 1315 | const name_loc = tree.token_locs[fn_proto.getNameToken().?]; |
| 1238 | const name = tree.tokenSliceLoc(name_loc); | 1316 | const name = tree.tokenSliceLoc(name_loc); |
| ... | @@ -1240,7 +1318,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { | ... | @@ -1240,7 +1318,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1240 | try self.analyzeExport(&block_scope.base, export_src, name, decl); | 1318 | try self.analyzeExport(&block_scope.base, export_src, name, decl); |
| 1241 | } | 1319 | } |
| 1242 | } | 1320 | } |
| 1243 | return type_changed; | 1321 | return type_changed or is_inline != prev_is_inline; |
| 1244 | }, | 1322 | }, |
| 1245 | .VarDecl => { | 1323 | .VarDecl => { |
| 1246 | const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node); | 1324 | const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node); |
| ... | @@ -1824,15 +1902,15 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { | ... | @@ -1824,15 +1902,15 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { |
| 1824 | }; | 1902 | }; |
| 1825 | defer inner_block.instructions.deinit(self.gpa); | 1903 | defer inner_block.instructions.deinit(self.gpa); |
| 1826 | 1904 | ||
| 1827 | const fn_zir = func.analysis.queued; | 1905 | const fn_zir = func.data.zir; |
| 1828 | defer fn_zir.arena.promote(self.gpa).deinit(); | 1906 | defer fn_zir.arena.promote(self.gpa).deinit(); |
| 1829 | func.analysis = .{ .in_progress = {} }; | 1907 | func.setAnalysis(.in_progress); |
| 1830 | log.debug("set {s} to in_progress\n", .{decl.name}); | 1908 | log.debug("set {s} to in_progress\n", .{decl.name}); |
| 1831 | 1909 | ||
| 1832 | try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body); | 1910 | try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body); |
| 1833 | 1911 | ||
| 1834 | const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items); | 1912 | const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items); |
| 1835 | func.analysis = .{ .success = .{ .instructions = instructions } }; | 1913 | func.setAnalysis(.{ .success = .{ .instructions = instructions } }); |
| 1836 | log.debug("set {s} to success\n", .{decl.name}); | 1914 | log.debug("set {s} to success\n", .{decl.name}); |
| 1837 | } | 1915 | } |
| 1838 | 1916 | ||
| ... | @@ -2329,7 +2407,7 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn | ... | @@ -2329,7 +2407,7 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn |
| 2329 | self.ensureDeclAnalyzed(decl) catch |err| { | 2407 | self.ensureDeclAnalyzed(decl) catch |err| { |
| 2330 | if (scope.cast(Scope.Block)) |block| { | 2408 | if (scope.cast(Scope.Block)) |block| { |
| 2331 | if (block.func) |func| { | 2409 | if (block.func) |func| { |
| 2332 | func.analysis = .dependency_failure; | 2410 | func.setAnalysis(.dependency_failure); |
| 2333 | } else { | 2411 | } else { |
| 2334 | block.decl.analysis = .dependency_failure; | 2412 | block.decl.analysis = .dependency_failure; |
| 2335 | } | 2413 | } |
| ... | @@ -3029,7 +3107,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Com | ... | @@ -3029,7 +3107,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Com |
| 3029 | .block => { | 3107 | .block => { |
| 3030 | const block = scope.cast(Scope.Block).?; | 3108 | const block = scope.cast(Scope.Block).?; |
| 3031 | if (block.func) |func| { | 3109 | if (block.func) |func| { |
| 3032 | func.analysis = .sema_failure; | 3110 | func.setAnalysis(.sema_failure); |
| 3033 | } else { | 3111 | } else { |
| 3034 | block.decl.analysis = .sema_failure; | 3112 | block.decl.analysis = .sema_failure; |
| 3035 | block.decl.generation = self.generation; | 3113 | block.decl.generation = self.generation; |
src/codegen.zig+5-5| ... | @@ -532,7 +532,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -532,7 +532,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 532 | self.code.items.len += 4; | 532 | self.code.items.len += 4; |
| 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.data.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.data.body); |
| 580 | try self.dbgSetEpilogueBegin(); | 580 | try self.dbgSetEpilogueBegin(); |
| 581 | } | 581 | } |
| 582 | }, | 582 | }, |
| ... | @@ -593,7 +593,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -593,7 +593,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 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.data.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.data.body); |
| 642 | try self.dbgSetEpilogueBegin(); | 642 | try self.dbgSetEpilogueBegin(); |
| 643 | } | 643 | } |
| 644 | }, | 644 | }, |
| 645 | else => { | 645 | else => { |
| 646 | try self.dbgSetPrologueEnd(); | 646 | try self.dbgSetPrologueEnd(); |
| 647 | try self.genBody(self.mod_fn.analysis.success); | 647 | try self.genBody(self.mod_fn.data.body); |
| 648 | try self.dbgSetEpilogueBegin(); | 648 | try self.dbgSetEpilogueBegin(); |
| 649 | }, | 649 | }, |
| 650 | } | 650 | } |
src/codegen/c.zig+1-1| ... | @@ -275,7 +275,7 @@ pub fn generate(file: *C, module: *Module, decl: *Decl) !void { | ... | @@ -275,7 +275,7 @@ pub fn generate(file: *C, module: *Module, decl: *Decl) !void { |
| 275 | try writer.writeAll(" {"); | 275 | try writer.writeAll(" {"); |
| 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.data.body.instructions; |
| 279 | if (instructions.len > 0) { | 279 | if (instructions.len > 0) { |
| 280 | try writer.writeAll("\n"); | 280 | try writer.writeAll("\n"); |
| 281 | for (instructions) |inst| { | 281 | for (instructions) |inst| { |
src/codegen/wasm.zig+1-1| ... | @@ -63,7 +63,7 @@ pub fn genCode(buf: *ArrayList(u8), decl: *Decl) !void { | ... | @@ -63,7 +63,7 @@ pub fn genCode(buf: *ArrayList(u8), decl: *Decl) !void { |
| 63 | // TODO: check for and handle death of 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.data.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.data.body.instructions; |
| 298 | for (instructions) |inst| { | 298 | for (instructions) |inst| { |
| 299 | switch (inst.tag) { | 299 | switch (inst.tag) { |
| 300 | .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), | 300 | .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), |
src/zir.zig+249-100| ... | @@ -793,7 +793,9 @@ pub const Inst = struct { | ... | @@ -793,7 +793,9 @@ pub const Inst = struct { |
| 793 | fn_type: *Inst, | 793 | fn_type: *Inst, |
| 794 | body: Module.Body, | 794 | body: Module.Body, |
| 795 | }, | 795 | }, |
| 796 | kw_args: struct {}, | 796 | kw_args: struct { |
| 797 | is_inline: bool = false, | ||
| 798 | }, | ||
| 797 | }; | 799 | }; |
| 798 | 800 | ||
| 799 | pub const FnType = struct { | 801 | pub const FnType = struct { |
| ... | @@ -1847,83 +1849,258 @@ pub fn emit(allocator: *Allocator, old_module: *IrModule) !Module { | ... | @@ -1847,83 +1849,258 @@ pub fn emit(allocator: *Allocator, old_module: *IrModule) !Module { |
| 1847 | /// For debugging purposes, prints a function representation to stderr. | 1849 | /// For debugging purposes, prints a function representation to stderr. |
| 1848 | pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void { | 1850 | pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void { |
| 1849 | const allocator = old_module.gpa; | 1851 | const allocator = old_module.gpa; |
| 1850 | var ctx: EmitZIR = .{ | 1852 | var ctx: DumpTzir = .{ |
| 1851 | .allocator = allocator, | 1853 | .allocator = allocator, |
| 1852 | .decls = .{}, | ||
| 1853 | .arena = std.heap.ArenaAllocator.init(allocator), | 1854 | .arena = std.heap.ArenaAllocator.init(allocator), |
| 1854 | .old_module = &old_module, | 1855 | .old_module = &old_module, |
| 1855 | .next_auto_name = 0, | 1856 | .module_fn = module_fn, |
| 1856 | .names = std.StringArrayHashMap(void).init(allocator), | 1857 | .indent = 2, |
| 1857 | .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator), | 1858 | .inst_table = DumpTzir.InstTable.init(allocator), |
| 1858 | .indent = 0, | 1859 | .partial_inst_table = DumpTzir.InstTable.init(allocator), |
| 1859 | .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator), | 1860 | .const_table = DumpTzir.InstTable.init(allocator), |
| 1860 | .loop_table = std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop).init(allocator), | ||
| 1861 | .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator), | ||
| 1862 | .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator), | ||
| 1863 | }; | 1861 | }; |
| 1864 | defer ctx.metadata.deinit(); | 1862 | defer ctx.inst_table.deinit(); |
| 1865 | defer ctx.body_metadata.deinit(); | 1863 | defer ctx.partial_inst_table.deinit(); |
| 1866 | defer ctx.block_table.deinit(); | 1864 | defer ctx.const_table.deinit(); |
| 1867 | defer ctx.loop_table.deinit(); | ||
| 1868 | defer ctx.decls.deinit(allocator); | ||
| 1869 | defer ctx.names.deinit(); | ||
| 1870 | defer ctx.primitive_table.deinit(); | ||
| 1871 | defer ctx.arena.deinit(); | 1865 | defer ctx.arena.deinit(); |
| 1872 | 1866 | ||
| 1873 | const fn_ty = module_fn.owner_decl.typed_value.most_recent.typed_value.ty; | 1867 | switch (module_fn.analysis()) { |
| 1874 | _ = ctx.emitFn(module_fn, 0, fn_ty) catch |err| { | 1868 | .queued => std.debug.print("(queued)", .{}), |
| 1875 | std.debug.print("unable to dump function: {s}\n", .{@errorName(err)}); | 1869 | .in_progress => std.debug.print("(in_progress)", .{}), |
| 1876 | return; | 1870 | .sema_failure => std.debug.print("(sema_failure)", .{}), |
| 1877 | }; | 1871 | .dependency_failure => std.debug.print("(dependency_failure)", .{}), |
| 1878 | var module = Module{ | 1872 | .success => |body| { |
| 1879 | .decls = ctx.decls.items, | 1873 | ctx.dump(body, std.io.getStdErr().writer()) catch @panic("failed to dump TZIR"); |
| 1880 | .arena = ctx.arena, | 1874 | }, |
| 1881 | .metadata = ctx.metadata, | 1875 | } |
| 1882 | .body_metadata = ctx.body_metadata, | ||
| 1883 | }; | ||
| 1884 | |||
| 1885 | module.dump(); | ||
| 1886 | } | 1876 | } |
| 1887 | 1877 | ||
| 1888 | /// For debugging purposes, prints a function representation to stderr. | 1878 | const DumpTzir = struct { |
| 1889 | pub fn dumpBlock(old_module: IrModule, module_block: *IrModule.Scope.Block) void { | 1879 | allocator: *Allocator, |
| 1890 | const allocator = old_module.gpa; | 1880 | arena: std.heap.ArenaAllocator, |
| 1891 | var ctx: EmitZIR = .{ | 1881 | old_module: *const IrModule, |
| 1892 | .allocator = allocator, | 1882 | module_fn: *IrModule.Fn, |
| 1893 | .decls = .{}, | 1883 | indent: usize, |
| 1894 | .arena = std.heap.ArenaAllocator.init(allocator), | 1884 | inst_table: InstTable, |
| 1895 | .old_module = &old_module, | 1885 | partial_inst_table: InstTable, |
| 1896 | .next_auto_name = 0, | 1886 | const_table: InstTable, |
| 1897 | .names = std.StringArrayHashMap(void).init(allocator), | 1887 | next_index: usize = 0, |
| 1898 | .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator), | 1888 | next_partial_index: usize = 0, |
| 1899 | .indent = 0, | 1889 | next_const_index: usize = 0, |
| 1900 | .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator), | 1890 | |
| 1901 | .loop_table = std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop).init(allocator), | 1891 | const InstTable = std.AutoArrayHashMap(*ir.Inst, usize); |
| 1902 | .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator), | 1892 | |
| 1903 | .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator), | 1893 | fn dump(dtz: *DumpTzir, body: ir.Body, writer: std.fs.File.Writer) !void { |
| 1904 | }; | 1894 | // First pass to pre-populate the table so that we can show even invalid references. |
| 1905 | defer ctx.metadata.deinit(); | 1895 | // Must iterate the same order we iterate the second time. |
| 1906 | defer ctx.body_metadata.deinit(); | 1896 | // We also look for constants and put them in the const_table. |
| 1907 | defer ctx.block_table.deinit(); | 1897 | for (body.instructions) |inst| { |
| 1908 | defer ctx.loop_table.deinit(); | 1898 | try dtz.inst_table.put(inst, dtz.next_index); |
| 1909 | defer ctx.decls.deinit(allocator); | 1899 | dtz.next_index += 1; |
| 1910 | defer ctx.names.deinit(); | 1900 | switch (inst.tag) { |
| 1911 | defer ctx.primitive_table.deinit(); | 1901 | .alloc, |
| 1912 | defer ctx.arena.deinit(); | 1902 | .retvoid, |
| 1903 | .unreach, | ||
| 1904 | .breakpoint, | ||
| 1905 | .dbg_stmt, | ||
| 1906 | => {}, | ||
| 1913 | 1907 | ||
| 1914 | _ = ctx.emitBlock(module_block, 0) catch |err| { | 1908 | .ref, |
| 1915 | std.debug.print("unable to dump function: {}\n", .{err}); | 1909 | .ret, |
| 1916 | return; | 1910 | .bitcast, |
| 1917 | }; | 1911 | .not, |
| 1918 | var module = Module{ | 1912 | .isnonnull, |
| 1919 | .decls = ctx.decls.items, | 1913 | .isnull, |
| 1920 | .arena = ctx.arena, | 1914 | .iserr, |
| 1921 | .metadata = ctx.metadata, | 1915 | .ptrtoint, |
| 1922 | .body_metadata = ctx.body_metadata, | 1916 | .floatcast, |
| 1923 | }; | 1917 | .intcast, |
| 1918 | .load, | ||
| 1919 | .unwrap_optional, | ||
| 1920 | .wrap_optional, | ||
| 1921 | => { | ||
| 1922 | const un_op = inst.cast(ir.Inst.UnOp).?; | ||
| 1923 | try dtz.findConst(un_op.operand); | ||
| 1924 | }, | ||
| 1924 | 1925 | ||
| 1925 | module.dump(); | 1926 | .add, |
| 1926 | } | 1927 | .sub, |
| 1928 | .cmp_lt, | ||
| 1929 | .cmp_lte, | ||
| 1930 | .cmp_eq, | ||
| 1931 | .cmp_gte, | ||
| 1932 | .cmp_gt, | ||
| 1933 | .cmp_neq, | ||
| 1934 | .store, | ||
| 1935 | .booland, | ||
| 1936 | .boolor, | ||
| 1937 | .bitand, | ||
| 1938 | .bitor, | ||
| 1939 | .xor, | ||
| 1940 | => { | ||
| 1941 | const bin_op = inst.cast(ir.Inst.BinOp).?; | ||
| 1942 | try dtz.findConst(bin_op.lhs); | ||
| 1943 | try dtz.findConst(bin_op.rhs); | ||
| 1944 | }, | ||
| 1945 | |||
| 1946 | .arg => {}, | ||
| 1947 | |||
| 1948 | // TODO fill out this debug printing | ||
| 1949 | .assembly, | ||
| 1950 | .block, | ||
| 1951 | .br, | ||
| 1952 | .brvoid, | ||
| 1953 | .call, | ||
| 1954 | .condbr, | ||
| 1955 | .constant, | ||
| 1956 | .loop, | ||
| 1957 | .varptr, | ||
| 1958 | .switchbr, | ||
| 1959 | => {}, | ||
| 1960 | } | ||
| 1961 | } | ||
| 1962 | |||
| 1963 | std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name}); | ||
| 1964 | |||
| 1965 | for (dtz.const_table.items()) |entry| { | ||
| 1966 | const constant = entry.key.castTag(.constant).?; | ||
| 1967 | try writer.print(" @{d}: {} = {};\n", .{ | ||
| 1968 | entry.value, constant.base.ty, constant.val, | ||
| 1969 | }); | ||
| 1970 | } | ||
| 1971 | |||
| 1972 | return dtz.dumpBody(body, writer); | ||
| 1973 | } | ||
| 1974 | |||
| 1975 | fn dumpBody(dtz: *DumpTzir, body: ir.Body, writer: std.fs.File.Writer) !void { | ||
| 1976 | for (body.instructions) |inst| { | ||
| 1977 | const my_index = dtz.next_partial_index; | ||
| 1978 | try dtz.partial_inst_table.put(inst, my_index); | ||
| 1979 | dtz.next_partial_index += 1; | ||
| 1980 | |||
| 1981 | try writer.writeByteNTimes(' ', dtz.indent); | ||
| 1982 | try writer.print("%{d}: {} = {s}(", .{ | ||
| 1983 | my_index, inst.ty, @tagName(inst.tag), | ||
| 1984 | }); | ||
| 1985 | switch (inst.tag) { | ||
| 1986 | .alloc, | ||
| 1987 | .retvoid, | ||
| 1988 | .unreach, | ||
| 1989 | .breakpoint, | ||
| 1990 | .dbg_stmt, | ||
| 1991 | => try writer.writeAll(")\n"), | ||
| 1992 | |||
| 1993 | .ref, | ||
| 1994 | .ret, | ||
| 1995 | .bitcast, | ||
| 1996 | .not, | ||
| 1997 | .isnonnull, | ||
| 1998 | .isnull, | ||
| 1999 | .iserr, | ||
| 2000 | .ptrtoint, | ||
| 2001 | .floatcast, | ||
| 2002 | .intcast, | ||
| 2003 | .load, | ||
| 2004 | .unwrap_optional, | ||
| 2005 | .wrap_optional, | ||
| 2006 | => { | ||
| 2007 | const un_op = inst.cast(ir.Inst.UnOp).?; | ||
| 2008 | if (dtz.partial_inst_table.get(un_op.operand)) |operand_index| { | ||
| 2009 | try writer.print("%{d})\n", .{operand_index}); | ||
| 2010 | } else if (dtz.const_table.get(un_op.operand)) |operand_index| { | ||
| 2011 | try writer.print("@{d})\n", .{operand_index}); | ||
| 2012 | } else if (dtz.inst_table.get(un_op.operand)) |operand_index| { | ||
| 2013 | try writer.print("%{d}) // Instruction does not dominate all uses!\n", .{ | ||
| 2014 | operand_index, | ||
| 2015 | }); | ||
| 2016 | } else { | ||
| 2017 | try writer.writeAll("!BADREF!)\n"); | ||
| 2018 | } | ||
| 2019 | }, | ||
| 2020 | |||
| 2021 | .add, | ||
| 2022 | .sub, | ||
| 2023 | .cmp_lt, | ||
| 2024 | .cmp_lte, | ||
| 2025 | .cmp_eq, | ||
| 2026 | .cmp_gte, | ||
| 2027 | .cmp_gt, | ||
| 2028 | .cmp_neq, | ||
| 2029 | .store, | ||
| 2030 | .booland, | ||
| 2031 | .boolor, | ||
| 2032 | .bitand, | ||
| 2033 | .bitor, | ||
| 2034 | .xor, | ||
| 2035 | => { | ||
| 2036 | var lhs_kinky: ?usize = null; | ||
| 2037 | var rhs_kinky: ?usize = null; | ||
| 2038 | |||
| 2039 | const bin_op = inst.cast(ir.Inst.BinOp).?; | ||
| 2040 | if (dtz.partial_inst_table.get(bin_op.lhs)) |operand_index| { | ||
| 2041 | try writer.print("%{d}, ", .{operand_index}); | ||
| 2042 | } else if (dtz.const_table.get(bin_op.lhs)) |operand_index| { | ||
| 2043 | try writer.print("@{d}, ", .{operand_index}); | ||
| 2044 | } else if (dtz.inst_table.get(bin_op.lhs)) |operand_index| { | ||
| 2045 | lhs_kinky = operand_index; | ||
| 2046 | try writer.print("%{d}, ", .{operand_index}); | ||
| 2047 | } else { | ||
| 2048 | try writer.writeAll("!BADREF!, "); | ||
| 2049 | } | ||
| 2050 | if (dtz.partial_inst_table.get(bin_op.rhs)) |operand_index| { | ||
| 2051 | try writer.print("%{d}", .{operand_index}); | ||
| 2052 | } else if (dtz.const_table.get(bin_op.rhs)) |operand_index| { | ||
| 2053 | try writer.print("@{d}", .{operand_index}); | ||
| 2054 | } else if (dtz.inst_table.get(bin_op.rhs)) |operand_index| { | ||
| 2055 | rhs_kinky = operand_index; | ||
| 2056 | try writer.print("%{d}", .{operand_index}); | ||
| 2057 | } else { | ||
| 2058 | try writer.writeAll("!BADREF!"); | ||
| 2059 | } | ||
| 2060 | if (lhs_kinky != null or rhs_kinky != null) { | ||
| 2061 | try writer.writeAll(") // Instruction does not dominate all uses!"); | ||
| 2062 | if (lhs_kinky) |lhs| { | ||
| 2063 | try writer.print(" %{d}", .{lhs}); | ||
| 2064 | } | ||
| 2065 | if (rhs_kinky) |rhs| { | ||
| 2066 | try writer.print(" %{d}", .{rhs}); | ||
| 2067 | } | ||
| 2068 | try writer.writeAll("\n"); | ||
| 2069 | } else { | ||
| 2070 | try writer.writeAll(")\n"); | ||
| 2071 | } | ||
| 2072 | }, | ||
| 2073 | |||
| 2074 | .arg => { | ||
| 2075 | const arg = inst.castTag(.arg).?; | ||
| 2076 | try writer.print("{s})\n", .{arg.name}); | ||
| 2077 | }, | ||
| 2078 | |||
| 2079 | // TODO fill out this debug printing | ||
| 2080 | .assembly, | ||
| 2081 | .block, | ||
| 2082 | .br, | ||
| 2083 | .brvoid, | ||
| 2084 | .call, | ||
| 2085 | .condbr, | ||
| 2086 | .constant, | ||
| 2087 | .loop, | ||
| 2088 | .varptr, | ||
| 2089 | .switchbr, | ||
| 2090 | => { | ||
| 2091 | try writer.writeAll("!TODO!)\n"); | ||
| 2092 | }, | ||
| 2093 | } | ||
| 2094 | } | ||
| 2095 | } | ||
| 2096 | |||
| 2097 | fn findConst(dtz: *DumpTzir, operand: *ir.Inst) !void { | ||
| 2098 | if (operand.tag == .constant) { | ||
| 2099 | try dtz.const_table.put(operand, dtz.next_const_index); | ||
| 2100 | dtz.next_const_index += 1; | ||
| 2101 | } | ||
| 2102 | } | ||
| 2103 | }; | ||
| 1927 | 2104 | ||
| 1928 | const EmitZIR = struct { | 2105 | const EmitZIR = struct { |
| 1929 | allocator: *Allocator, | 2106 | allocator: *Allocator, |
| ... | @@ -2105,36 +2282,6 @@ const EmitZIR = struct { | ... | @@ -2105,36 +2282,6 @@ const EmitZIR = struct { |
| 2105 | return &declref_inst.base; | 2282 | return &declref_inst.base; |
| 2106 | } | 2283 | } |
| 2107 | 2284 | ||
| 2108 | fn emitBlock(self: *EmitZIR, module_block: *IrModule.Scope.Block, src: usize) Allocator.Error!*Decl { | ||
| 2109 | var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator); | ||
| 2110 | defer inst_table.deinit(); | ||
| 2111 | |||
| 2112 | var instructions = std.ArrayList(*Inst).init(self.allocator); | ||
| 2113 | defer instructions.deinit(); | ||
| 2114 | |||
| 2115 | const body: ir.Body = .{ .instructions = module_block.instructions.items }; | ||
| 2116 | try self.emitBody(body, &inst_table, &instructions); | ||
| 2117 | |||
| 2118 | const fn_type = try self.emitType(src, Type.initTag(.void)); | ||
| 2119 | |||
| 2120 | const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len); | ||
| 2121 | mem.copy(*Inst, arena_instrs, instructions.items); | ||
| 2122 | |||
| 2123 | const fn_inst = try self.arena.allocator.create(Inst.Fn); | ||
| 2124 | fn_inst.* = .{ | ||
| 2125 | .base = .{ | ||
| 2126 | .src = src, | ||
| 2127 | .tag = Inst.Fn.base_tag, | ||
| 2128 | }, | ||
| 2129 | .positionals = .{ | ||
| 2130 | .fn_type = fn_type.inst, | ||
| 2131 | .body = .{ .instructions = arena_instrs }, | ||
| 2132 | }, | ||
| 2133 | .kw_args = .{}, | ||
| 2134 | }; | ||
| 2135 | return self.emitUnnamedDecl(&fn_inst.base); | ||
| 2136 | } | ||
| 2137 | |||
| 2138 | fn emitFn(self: *EmitZIR, module_fn: *IrModule.Fn, src: usize, ty: Type) Allocator.Error!*Decl { | 2285 | fn emitFn(self: *EmitZIR, module_fn: *IrModule.Fn, src: usize, ty: Type) Allocator.Error!*Decl { |
| 2139 | var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator); | 2286 | var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator); |
| 2140 | defer inst_table.deinit(); | 2287 | defer inst_table.deinit(); |
| ... | @@ -2142,7 +2289,7 @@ const EmitZIR = struct { | ... | @@ -2142,7 +2289,7 @@ const EmitZIR = struct { |
| 2142 | var instructions = std.ArrayList(*Inst).init(self.allocator); | 2289 | var instructions = std.ArrayList(*Inst).init(self.allocator); |
| 2143 | defer instructions.deinit(); | 2290 | defer instructions.deinit(); |
| 2144 | 2291 | ||
| 2145 | switch (module_fn.analysis) { | 2292 | switch (module_fn.analysis()) { |
| 2146 | .queued => unreachable, | 2293 | .queued => unreachable, |
| 2147 | .in_progress => unreachable, | 2294 | .in_progress => unreachable, |
| 2148 | .success => |body| { | 2295 | .success => |body| { |
| ... | @@ -2224,7 +2371,9 @@ const EmitZIR = struct { | ... | @@ -2224,7 +2371,9 @@ const EmitZIR = struct { |
| 2224 | .fn_type = fn_type.inst, | 2371 | .fn_type = fn_type.inst, |
| 2225 | .body = .{ .instructions = arena_instrs }, | 2372 | .body = .{ .instructions = arena_instrs }, |
| 2226 | }, | 2373 | }, |
| 2227 | .kw_args = .{}, | 2374 | .kw_args = .{ |
| 2375 | .is_inline = module_fn.bits.is_inline, | ||
| 2376 | }, | ||
| 2228 | }; | 2377 | }; |
| 2229 | return self.emitUnnamedDecl(&fn_inst.base); | 2378 | return self.emitUnnamedDecl(&fn_inst.base); |
| 2230 | } | 2379 | } |
src/zir_sema.zig+163-76| ... | @@ -577,7 +577,15 @@ fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) In | ... | @@ -577,7 +577,15 @@ fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) In |
| 577 | } | 577 | } |
| 578 | 578 | ||
| 579 | fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst { | 579 | fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst { |
| 580 | const b = try mod.requireRuntimeBlock(scope, inst.base.src); | 580 | const b = try mod.requireFunctionBlock(scope, inst.base.src); |
| 581 | switch (b.label) { | ||
| 582 | .none, .breaking => {}, | ||
| 583 | .inlining => |*inlining| { | ||
| 584 | const param_index = inlining.param_index; | ||
| 585 | inlining.param_index += 1; | ||
| 586 | return inlining.casted_args[param_index]; | ||
| 587 | }, | ||
| 588 | } | ||
| 581 | const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty; | 589 | const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty; |
| 582 | const param_index = b.instructions.items.len; | 590 | const param_index = b.instructions.items.len; |
| 583 | const param_count = fn_ty.fnParamLen(); | 591 | const param_count = fn_ty.fnParamLen(); |
| ... | @@ -636,7 +644,7 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c | ... | @@ -636,7 +644,7 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c |
| 636 | .decl = parent_block.decl, | 644 | .decl = parent_block.decl, |
| 637 | .instructions = .{}, | 645 | .instructions = .{}, |
| 638 | .arena = parent_block.arena, | 646 | .arena = parent_block.arena, |
| 639 | .label = null, | 647 | .label = .none, |
| 640 | .is_comptime = parent_block.is_comptime or is_comptime, | 648 | .is_comptime = parent_block.is_comptime or is_comptime, |
| 641 | }; | 649 | }; |
| 642 | defer child_block.instructions.deinit(mod.gpa); | 650 | defer child_block.instructions.deinit(mod.gpa); |
| ... | @@ -674,41 +682,56 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt | ... | @@ -674,41 +682,56 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt |
| 674 | .decl = parent_block.decl, | 682 | .decl = parent_block.decl, |
| 675 | .instructions = .{}, | 683 | .instructions = .{}, |
| 676 | .arena = parent_block.arena, | 684 | .arena = parent_block.arena, |
| 677 | // TODO @as here is working around a stage1 miscompilation bug :( | 685 | .label = Scope.Block.Label{ |
| 678 | .label = @as(?Scope.Block.Label, Scope.Block.Label{ | 686 | .breaking = .{ |
| 679 | .zir_block = inst, | 687 | .zir_block = inst, |
| 680 | .results = .{}, | 688 | .merges = .{ |
| 681 | .block_inst = block_inst, | 689 | .results = .{}, |
| 682 | }), | 690 | .block_inst = block_inst, |
| 691 | }, | ||
| 692 | }, | ||
| 693 | }, | ||
| 683 | .is_comptime = is_comptime or parent_block.is_comptime, | 694 | .is_comptime = is_comptime or parent_block.is_comptime, |
| 684 | }; | 695 | }; |
| 685 | const label = &child_block.label.?; | 696 | const merges = &child_block.label.breaking.merges; |
| 686 | 697 | ||
| 687 | defer child_block.instructions.deinit(mod.gpa); | 698 | defer child_block.instructions.deinit(mod.gpa); |
| 688 | defer label.results.deinit(mod.gpa); | 699 | defer merges.results.deinit(mod.gpa); |
| 689 | 700 | ||
| 690 | try analyzeBody(mod, &child_block.base, inst.positionals.body); | 701 | try analyzeBody(mod, &child_block.base, inst.positionals.body); |
| 691 | 702 | ||
| 703 | return analyzeBlockBody(mod, scope, &child_block, merges); | ||
| 704 | } | ||
| 705 | |||
| 706 | fn analyzeBlockBody( | ||
| 707 | mod: *Module, | ||
| 708 | scope: *Scope, | ||
| 709 | child_block: *Scope.Block, | ||
| 710 | merges: *Scope.Block.Label.Merges, | ||
| 711 | ) InnerError!*Inst { | ||
| 712 | const parent_block = scope.cast(Scope.Block).?; | ||
| 713 | |||
| 692 | // Blocks must terminate with noreturn instruction. | 714 | // Blocks must terminate with noreturn instruction. |
| 693 | assert(child_block.instructions.items.len != 0); | 715 | assert(child_block.instructions.items.len != 0); |
| 694 | assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn()); | 716 | assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn()); |
| 695 | 717 | ||
| 696 | if (label.results.items.len == 0) { | 718 | if (merges.results.items.len == 0) { |
| 697 | // No need for a block instruction. We can put the new instructions directly into the parent block. | 719 | // No need for a block instruction. We can put the new instructions |
| 720 | // directly into the parent block. | ||
| 698 | const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items); | 721 | const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items); |
| 699 | try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); | 722 | try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); |
| 700 | return copied_instructions[copied_instructions.len - 1]; | 723 | return copied_instructions[copied_instructions.len - 1]; |
| 701 | } | 724 | } |
| 702 | if (label.results.items.len == 1) { | 725 | if (merges.results.items.len == 1) { |
| 703 | const last_inst_index = child_block.instructions.items.len - 1; | 726 | const last_inst_index = child_block.instructions.items.len - 1; |
| 704 | const last_inst = child_block.instructions.items[last_inst_index]; | 727 | const last_inst = child_block.instructions.items[last_inst_index]; |
| 705 | if (last_inst.breakBlock()) |br_block| { | 728 | if (last_inst.breakBlock()) |br_block| { |
| 706 | if (br_block == block_inst) { | 729 | if (br_block == merges.block_inst) { |
| 707 | // No need for a block instruction. We can put the new instructions directly into the parent block. | 730 | // No need for a block instruction. We can put the new instructions directly into the parent block. |
| 708 | // Here we omit the break instruction. | 731 | // Here we omit the break instruction. |
| 709 | const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]); | 732 | const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]); |
| 710 | try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); | 733 | try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); |
| 711 | return label.results.items[0]; | 734 | return merges.results.items[0]; |
| 712 | } | 735 | } |
| 713 | } | 736 | } |
| 714 | } | 737 | } |
| ... | @@ -717,10 +740,10 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt | ... | @@ -717,10 +740,10 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt |
| 717 | 740 | ||
| 718 | // Need to set the type and emit the Block instruction. This allows machine code generation | 741 | // Need to set the type and emit the Block instruction. This allows machine code generation |
| 719 | // to emit a jump instruction to after the block when it encounters the break. | 742 | // to emit a jump instruction to after the block when it encounters the break. |
| 720 | try parent_block.instructions.append(mod.gpa, &block_inst.base); | 743 | try parent_block.instructions.append(mod.gpa, &merges.block_inst.base); |
| 721 | block_inst.base.ty = try mod.resolvePeerTypes(scope, label.results.items); | 744 | merges.block_inst.base.ty = try mod.resolvePeerTypes(scope, merges.results.items); |
| 722 | block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) }; | 745 | merges.block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) }; |
| 723 | return &block_inst.base; | 746 | return &merges.block_inst.base; |
| 724 | } | 747 | } |
| 725 | 748 | ||
| 726 | fn analyzeInstBreakpoint(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | 749 | fn analyzeInstBreakpoint(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { |
| ... | @@ -829,14 +852,32 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError | ... | @@ -829,14 +852,32 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError |
| 829 | const ret_type = func.ty.fnReturnType(); | 852 | const ret_type = func.ty.fnReturnType(); |
| 830 | 853 | ||
| 831 | const b = try mod.requireFunctionBlock(scope, inst.base.src); | 854 | const b = try mod.requireFunctionBlock(scope, inst.base.src); |
| 832 | if (b.is_comptime) { | 855 | const is_comptime_call = b.is_comptime or inst.kw_args.modifier == .compile_time; |
| 833 | const fn_val = try mod.resolveConstValue(scope, func); | 856 | const is_inline_call = is_comptime_call or inst.kw_args.modifier == .always_inline or blk: { |
| 834 | const module_fn = switch (fn_val.tag()) { | 857 | // This logic will get simplified by |
| 835 | .function => fn_val.castTag(.function).?.data, | 858 | // https://github.com/ziglang/zig/issues/6429 |
| 836 | .extern_fn => return mod.fail(scope, inst.base.src, "comptime call of extern function", .{}), | 859 | if (try mod.resolveDefinedValue(scope, func)) |func_val| { |
| 860 | const module_fn = switch (func_val.tag()) { | ||
| 861 | .function => func_val.castTag(.function).?.data, | ||
| 862 | else => break :blk false, | ||
| 863 | }; | ||
| 864 | break :blk module_fn.bits.is_inline; | ||
| 865 | } | ||
| 866 | break :blk false; | ||
| 867 | }; | ||
| 868 | if (is_inline_call) { | ||
| 869 | const func_val = try mod.resolveConstValue(scope, func); | ||
| 870 | const module_fn = switch (func_val.tag()) { | ||
| 871 | .function => func_val.castTag(.function).?.data, | ||
| 872 | .extern_fn => return mod.fail(scope, inst.base.src, "{s} call of extern function", .{ | ||
| 873 | @as([]const u8, if (is_comptime_call) "comptime" else "inline"), | ||
| 874 | }), | ||
| 837 | else => unreachable, | 875 | else => unreachable, |
| 838 | }; | 876 | }; |
| 839 | const callee_decl = module_fn.owner_decl; | 877 | const callee_decl = module_fn.owner_decl; |
| 878 | // TODO: De-duplicate this with the code in Module.zig that generates | ||
| 879 | // ZIR for the same function and re-use the same ZIR for runtime function | ||
| 880 | // generation and for inline/comptime calls. | ||
| 840 | const callee_file_scope = callee_decl.getFileScope(); | 881 | const callee_file_scope = callee_decl.getFileScope(); |
| 841 | const tree = mod.getAstTree(callee_file_scope) catch |err| switch (err) { | 882 | const tree = mod.getAstTree(callee_file_scope) catch |err| switch (err) { |
| 842 | error.OutOfMemory => return error.OutOfMemory, | 883 | error.OutOfMemory => return error.OutOfMemory, |
| ... | @@ -859,23 +900,31 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError | ... | @@ -859,23 +900,31 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError |
| 859 | }; | 900 | }; |
| 860 | defer gen_scope.instructions.deinit(mod.gpa); | 901 | defer gen_scope.instructions.deinit(mod.gpa); |
| 861 | 902 | ||
| 862 | // Add a const instruction for each parameter. | 903 | // We need an instruction for each parameter, and they must be first in the body. |
| 904 | try gen_scope.instructions.resize(mod.gpa, fn_proto.params_len); | ||
| 863 | var params_scope = &gen_scope.base; | 905 | var params_scope = &gen_scope.base; |
| 864 | for (fn_proto.params()) |param, i| { | 906 | for (fn_proto.params()) |param, i| { |
| 865 | const name_token = param.name_token.?; | 907 | const name_token = param.name_token.?; |
| 866 | const src = tree.token_locs[name_token].start; | 908 | const src = tree.token_locs[name_token].start; |
| 867 | const param_name = try mod.identifierTokenString(scope, name_token); | 909 | const param_name = try mod.identifierTokenString(scope, name_token); |
| 868 | const arg_val = try mod.resolveConstValue(scope, casted_args[i]); | 910 | const arg = try call_arena.allocator.create(zir.Inst.Arg); |
| 869 | const arg = try astgen.addZIRInstConst(mod, params_scope, src, .{ | 911 | arg.* = .{ |
| 870 | .ty = casted_args[i].ty, | 912 | .base = .{ |
| 871 | .val = arg_val, | 913 | .tag = .arg, |
| 872 | }); | 914 | .src = src, |
| 915 | }, | ||
| 916 | .positionals = .{ | ||
| 917 | .name = param_name, | ||
| 918 | }, | ||
| 919 | .kw_args = .{}, | ||
| 920 | }; | ||
| 921 | gen_scope.instructions.items[i] = &arg.base; | ||
| 873 | const sub_scope = try call_arena.allocator.create(Scope.LocalVal); | 922 | const sub_scope = try call_arena.allocator.create(Scope.LocalVal); |
| 874 | sub_scope.* = .{ | 923 | sub_scope.* = .{ |
| 875 | .parent = params_scope, | 924 | .parent = params_scope, |
| 876 | .gen_zir = &gen_scope, | 925 | .gen_zir = &gen_scope, |
| 877 | .name = param_name, | 926 | .name = param_name, |
| 878 | .inst = arg, | 927 | .inst = &arg.base, |
| 879 | }; | 928 | }; |
| 880 | params_scope = &sub_scope.base; | 929 | params_scope = &sub_scope.base; |
| 881 | } | 930 | } |
| ... | @@ -896,42 +945,52 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError | ... | @@ -896,42 +945,52 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError |
| 896 | zir.dumpZir(mod.gpa, "fn_body_callee", callee_decl.name, gen_scope.instructions.items) catch {}; | 945 | zir.dumpZir(mod.gpa, "fn_body_callee", callee_decl.name, gen_scope.instructions.items) catch {}; |
| 897 | } | 946 | } |
| 898 | 947 | ||
| 899 | // Analyze the ZIR. | 948 | // Analyze the ZIR. The same ZIR gets analyzed into a runtime function |
| 900 | var inner_block: Scope.Block = .{ | 949 | // or an inlined call depending on what union tag the `label` field is |
| 950 | // set to in the `Scope.Block`. | ||
| 951 | // This block instruction will be used to capture the return value from the | ||
| 952 | // inlined function. | ||
| 953 | const block_inst = try scope.arena().create(Inst.Block); | ||
| 954 | block_inst.* = .{ | ||
| 955 | .base = .{ | ||
| 956 | .tag = Inst.Block.base_tag, | ||
| 957 | .ty = ret_type, | ||
| 958 | .src = inst.base.src, | ||
| 959 | }, | ||
| 960 | .body = undefined, | ||
| 961 | }; | ||
| 962 | var child_block: Scope.Block = .{ | ||
| 901 | .parent = null, | 963 | .parent = null, |
| 902 | .func = module_fn, | 964 | .func = module_fn, |
| 903 | .decl = callee_decl, | 965 | // Note that we pass the caller's Decl, not the callee. This causes |
| 966 | // compile errors to be attached (correctly) to the caller's Decl. | ||
| 967 | .decl = scope.decl().?, | ||
| 904 | .instructions = .{}, | 968 | .instructions = .{}, |
| 905 | .arena = &call_arena.allocator, | 969 | .arena = scope.arena(), |
| 906 | .is_comptime = true, | 970 | .label = Scope.Block.Label{ |
| 971 | .inlining = .{ | ||
| 972 | .param_index = 0, | ||
| 973 | .casted_args = casted_args, | ||
| 974 | .merges = .{ | ||
| 975 | .results = .{}, | ||
| 976 | .block_inst = block_inst, | ||
| 977 | }, | ||
| 978 | }, | ||
| 979 | }, | ||
| 980 | .is_comptime = is_comptime_call, | ||
| 907 | }; | 981 | }; |
| 908 | defer inner_block.instructions.deinit(mod.gpa); | 982 | const merges = &child_block.label.inlining.merges; |
| 983 | |||
| 984 | defer child_block.instructions.deinit(mod.gpa); | ||
| 985 | defer merges.results.deinit(mod.gpa); | ||
| 909 | 986 | ||
| 910 | // TODO make sure compile errors that happen from this analyzeBody are reported correctly | 987 | // This will have return instructions analyzed as break instructions to |
| 911 | // and attach to the caller Decl not the callee. | 988 | // the block_inst above. |
| 912 | try analyzeBody(mod, &inner_block.base, .{ | 989 | try analyzeBody(mod, &child_block.base, .{ |
| 913 | .instructions = gen_scope.instructions.items, | 990 | .instructions = gen_scope.instructions.items, |
| 914 | }); | 991 | }); |
| 915 | 992 | ||
| 916 | if (mod.comp.verbose_ir) { | 993 | return analyzeBlockBody(mod, scope, &child_block, merges); |
| 917 | inner_block.dump(mod.*); | ||
| 918 | } | ||
| 919 | |||
| 920 | assert(inner_block.instructions.items.len == 1); | ||
| 921 | const only_inst = inner_block.instructions.items[0]; | ||
| 922 | switch (only_inst.tag) { | ||
| 923 | .ret => { | ||
| 924 | const ret_inst = only_inst.castTag(.ret).?; | ||
| 925 | const operand = ret_inst.operand; | ||
| 926 | const callee_arena = scope.arena(); | ||
| 927 | return mod.constInst(scope, inst.base.src, .{ | ||
| 928 | .ty = try operand.ty.copy(callee_arena), | ||
| 929 | .val = try operand.value().?.copy(callee_arena), | ||
| 930 | }); | ||
| 931 | }, | ||
| 932 | .retvoid => return mod.constVoid(scope, inst.base.src), | ||
| 933 | else => unreachable, | ||
| 934 | } | ||
| 935 | } | 994 | } |
| 936 | 995 | ||
| 937 | return mod.addCall(b, inst.base.src, ret_type, func, casted_args); | 996 | return mod.addCall(b, inst.base.src, ret_type, func, casted_args); |
| ... | @@ -954,7 +1013,11 @@ fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError! | ... | @@ -954,7 +1013,11 @@ fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError! |
| 954 | }; | 1013 | }; |
| 955 | const new_func = try scope.arena().create(Module.Fn); | 1014 | const new_func = try scope.arena().create(Module.Fn); |
| 956 | new_func.* = .{ | 1015 | new_func.* = .{ |
| 957 | .analysis = .{ .queued = fn_zir }, | 1016 | .bits = .{ |
| 1017 | .state = .queued, | ||
| 1018 | .is_inline = fn_inst.kw_args.is_inline, | ||
| 1019 | }, | ||
| 1020 | .data = .{ .zir = fn_zir }, | ||
| 958 | .owner_decl = scope.decl().?, | 1021 | .owner_decl = scope.decl().?, |
| 959 | }; | 1022 | }; |
| 960 | return mod.constInst(scope, fn_inst.base.src, .{ | 1023 | return mod.constInst(scope, fn_inst.base.src, .{ |
| ... | @@ -2020,21 +2083,41 @@ fn analyzeInstUnreachable( | ... | @@ -2020,21 +2083,41 @@ fn analyzeInstUnreachable( |
| 2020 | fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | 2083 | fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { |
| 2021 | const operand = try resolveInst(mod, scope, inst.positionals.operand); | 2084 | const operand = try resolveInst(mod, scope, inst.positionals.operand); |
| 2022 | const b = try mod.requireFunctionBlock(scope, inst.base.src); | 2085 | const b = try mod.requireFunctionBlock(scope, inst.base.src); |
| 2023 | return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand); | 2086 | |
| 2087 | switch (b.label) { | ||
| 2088 | .inlining => |*inlining| { | ||
| 2089 | // We are inlining a function call; rewrite the `ret` as a `break`. | ||
| 2090 | try inlining.merges.results.append(mod.gpa, operand); | ||
| 2091 | return mod.addBr(b, inst.base.src, inlining.merges.block_inst, operand); | ||
| 2092 | }, | ||
| 2093 | .none, .breaking => { | ||
| 2094 | return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand); | ||
| 2095 | }, | ||
| 2096 | } | ||
| 2024 | } | 2097 | } |
| 2025 | 2098 | ||
| 2026 | fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | 2099 | fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { |
| 2027 | const b = try mod.requireFunctionBlock(scope, inst.base.src); | 2100 | const b = try mod.requireFunctionBlock(scope, inst.base.src); |
| 2028 | if (b.func) |func| { | 2101 | switch (b.label) { |
| 2029 | // Need to emit a compile error if returning void is not allowed. | 2102 | .inlining => |*inlining| { |
| 2030 | const void_inst = try mod.constVoid(scope, inst.base.src); | 2103 | // We are inlining a function call; rewrite the `retvoid` as a `breakvoid`. |
| 2031 | const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty; | 2104 | const void_inst = try mod.constVoid(scope, inst.base.src); |
| 2032 | const casted_void = try mod.coerce(scope, fn_ty.fnReturnType(), void_inst); | 2105 | try inlining.merges.results.append(mod.gpa, void_inst); |
| 2033 | if (casted_void.ty.zigTypeTag() != .Void) { | 2106 | return mod.addBr(b, inst.base.src, inlining.merges.block_inst, void_inst); |
| 2034 | return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, casted_void); | 2107 | }, |
| 2035 | } | 2108 | .none, .breaking => { |
| 2109 | if (b.func) |func| { | ||
| 2110 | // Need to emit a compile error if returning void is not allowed. | ||
| 2111 | const void_inst = try mod.constVoid(scope, inst.base.src); | ||
| 2112 | const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty; | ||
| 2113 | const casted_void = try mod.coerce(scope, fn_ty.fnReturnType(), void_inst); | ||
| 2114 | if (casted_void.ty.zigTypeTag() != .Void) { | ||
| 2115 | return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, casted_void); | ||
| 2116 | } | ||
| 2117 | } | ||
| 2118 | return mod.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid); | ||
| 2119 | }, | ||
| 2036 | } | 2120 | } |
| 2037 | return mod.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid); | ||
| 2038 | } | 2121 | } |
| 2039 | 2122 | ||
| 2040 | fn floatOpAllowed(tag: zir.Inst.Tag) bool { | 2123 | fn floatOpAllowed(tag: zir.Inst.Tag) bool { |
| ... | @@ -2054,12 +2137,16 @@ fn analyzeBreak( | ... | @@ -2054,12 +2137,16 @@ fn analyzeBreak( |
| 2054 | ) InnerError!*Inst { | 2137 | ) InnerError!*Inst { |
| 2055 | var opt_block = scope.cast(Scope.Block); | 2138 | var opt_block = scope.cast(Scope.Block); |
| 2056 | while (opt_block) |block| { | 2139 | while (opt_block) |block| { |
| 2057 | if (block.label) |*label| { | 2140 | switch (block.label) { |
| 2058 | if (label.zir_block == zir_block) { | 2141 | .none => {}, |
| 2059 | try label.results.append(mod.gpa, operand); | 2142 | .breaking => |*label| { |
| 2060 | const b = try mod.requireRuntimeBlock(scope, src); | 2143 | if (label.zir_block == zir_block) { |
| 2061 | return mod.addBr(b, src, label.block_inst, operand); | 2144 | try label.merges.results.append(mod.gpa, operand); |
| 2062 | } | 2145 | const b = try mod.requireFunctionBlock(scope, src); |
| 2146 | return mod.addBr(b, src, label.merges.block_inst, operand); | ||
| 2147 | } | ||
| 2148 | }, | ||
| 2149 | .inlining => unreachable, // Invalid `break` ZIR inside inline function call. | ||
| 2063 | } | 2150 | } |
| 2064 | opt_block = block.parent; | 2151 | opt_block = block.parent; |
| 2065 | } else unreachable; | 2152 | } else unreachable; |
test/stage2/zir.zig+6-6| ... | @@ -30,7 +30,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -30,7 +30,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 30 | \\@unnamed$7 = fntype([], @void, cc=C) | 30 | \\@unnamed$7 = fntype([], @void, cc=C) |
| 31 | \\@entry = fn(@unnamed$7, { | 31 | \\@entry = fn(@unnamed$7, { |
| 32 | \\ %0 = returnvoid() ; deaths=0b1000000000000000 | 32 | \\ %0 = returnvoid() ; deaths=0b1000000000000000 |
| 33 | \\}) | 33 | \\}, is_inline=0) |
| 34 | \\ | 34 | \\ |
| 35 | ); | 35 | ); |
| 36 | ctx.transformZIR("elemptr, add, cmp, condbr, return, breakpoint", linux_x64, | 36 | ctx.transformZIR("elemptr, add, cmp, condbr, return, breakpoint", linux_x64, |
| ... | @@ -78,7 +78,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -78,7 +78,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 78 | \\@unnamed$6 = fntype([], @void, cc=C) | 78 | \\@unnamed$6 = fntype([], @void, cc=C) |
| 79 | \\@entry = fn(@unnamed$6, { | 79 | \\@entry = fn(@unnamed$6, { |
| 80 | \\ %0 = returnvoid() ; deaths=0b1000000000000000 | 80 | \\ %0 = returnvoid() ; deaths=0b1000000000000000 |
| 81 | \\}) | 81 | \\}, is_inline=0) |
| 82 | \\@entry__anon_1 = str("2\x08\x01\n") | 82 | \\@entry__anon_1 = str("2\x08\x01\n") |
| 83 | \\@9 = declref("9__anon_0") | 83 | \\@9 = declref("9__anon_0") |
| 84 | \\@9__anon_0 = str("entry") | 84 | \\@9__anon_0 = str("entry") |
| ... | @@ -123,17 +123,17 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -123,17 +123,17 @@ pub fn addCases(ctx: *TestContext) !void { |
| 123 | \\@entry = fn(@unnamed$7, { | 123 | \\@entry = fn(@unnamed$7, { |
| 124 | \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001 | 124 | \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001 |
| 125 | \\ %1 = returnvoid() ; deaths=0b1000000000000000 | 125 | \\ %1 = returnvoid() ; deaths=0b1000000000000000 |
| 126 | \\}) | 126 | \\}, is_inline=0) |
| 127 | \\@unnamed$9 = fntype([], @void, cc=C) | 127 | \\@unnamed$9 = fntype([], @void, cc=C) |
| 128 | \\@a = fn(@unnamed$9, { | 128 | \\@a = fn(@unnamed$9, { |
| 129 | \\ %0 = call(@b, [], modifier=auto) ; deaths=0b1000000000000001 | 129 | \\ %0 = call(@b, [], modifier=auto) ; deaths=0b1000000000000001 |
| 130 | \\ %1 = returnvoid() ; deaths=0b1000000000000000 | 130 | \\ %1 = returnvoid() ; deaths=0b1000000000000000 |
| 131 | \\}) | 131 | \\}, is_inline=0) |
| 132 | \\@unnamed$11 = fntype([], @void, cc=C) | 132 | \\@unnamed$11 = fntype([], @void, cc=C) |
| 133 | \\@b = fn(@unnamed$11, { | 133 | \\@b = fn(@unnamed$11, { |
| 134 | \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001 | 134 | \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001 |
| 135 | \\ %1 = returnvoid() ; deaths=0b1000000000000000 | 135 | \\ %1 = returnvoid() ; deaths=0b1000000000000000 |
| 136 | \\}) | 136 | \\}, is_inline=0) |
| 137 | \\ | 137 | \\ |
| 138 | ); | 138 | ); |
| 139 | // Now we introduce a compile error | 139 | // Now we introduce a compile error |
| ... | @@ -203,7 +203,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -203,7 +203,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 203 | \\@unnamed$7 = fntype([], @void, cc=C) | 203 | \\@unnamed$7 = fntype([], @void, cc=C) |
| 204 | \\@entry = fn(@unnamed$7, { | 204 | \\@entry = fn(@unnamed$7, { |
| 205 | \\ %0 = returnvoid() ; deaths=0b1000000000000000 | 205 | \\ %0 = returnvoid() ; deaths=0b1000000000000000 |
| 206 | \\}) | 206 | \\}, is_inline=0) |
| 207 | \\ | 207 | \\ |
| 208 | ); | 208 | ); |
| 209 | } | 209 | } |