From 9362f382ab7023592cc1d71044217b847b122406 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 2 Jan 2021 12:32:30 -0700 Subject: stage2: implement function call inlining in the frontend * 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. --- src/Compilation.zig | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src/Compilation.zig') diff --git a/src/Compilation.zig b/src/Compilation.zig index de115b9b40..a6f39a3154 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1459,10 +1459,10 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor const module = self.bin_file.options.module.?; if (decl.typed_value.most_recent.typed_value.val.castTag(.function)) |payload| { const func = payload.data; - switch (func.analysis) { + switch (func.bits.state) { .queued => module.analyzeFnBody(decl, func) catch |err| switch (err) { error.AnalysisFail => { - assert(func.analysis != .in_progress); + assert(func.bits.state != .in_progress); continue; }, error.OutOfMemory => return error.OutOfMemory, @@ -1471,12 +1471,16 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor .sema_failure, .dependency_failure => continue, .success => {}, } - // Here we tack on additional allocations to the Decl's arena. The allocations are - // lifetime annotations in the ZIR. + // Here we tack on additional allocations to the Decl's arena. The allocations + // are lifetime annotations in the ZIR. var decl_arena = decl.typed_value.most_recent.arena.?.promote(module.gpa); defer decl.typed_value.most_recent.arena.?.* = decl_arena.state; log.debug("analyze liveness of {s}\n", .{decl.name}); - try liveness.analyze(module.gpa, &decl_arena.allocator, func.analysis.success); + try liveness.analyze(module.gpa, &decl_arena.allocator, func.data.body); + + if (self.verbose_ir) { + func.dump(module.*); + } } assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); -- cgit v1.2.3 From 006e7f68056af62ae7713d7ef228841d11874735 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 2 Jan 2021 13:40:23 -0700 Subject: stage2: re-use ZIR for comptime and inline calls Instead of freeing ZIR after semantic analysis, we keep it around so that it can be used for comptime calls, inline calls, and generic function calls. ZIR memory is now managed by the Decl arena. Debug dump() functions are conditionally compiled; only available in Debug builds of the compiler. Add a test for an inline function call. --- src/Compilation.zig | 9 ++-- src/Module.zig | 136 +++++++++++++++------------------------------------ src/codegen.zig | 10 ++-- src/codegen/c.zig | 2 +- src/codegen/wasm.zig | 2 +- src/llvm_backend.zig | 2 +- src/zir.zig | 17 ++++--- src/zir_sema.zig | 99 ++----------------------------------- test/stage2/test.zig | 25 ++++++++++ 9 files changed, 93 insertions(+), 209 deletions(-) (limited to 'src/Compilation.zig') diff --git a/src/Compilation.zig b/src/Compilation.zig index a6f39a3154..9a06aee561 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1459,15 +1459,16 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor const module = self.bin_file.options.module.?; if (decl.typed_value.most_recent.typed_value.val.castTag(.function)) |payload| { const func = payload.data; - switch (func.bits.state) { + switch (func.state) { .queued => module.analyzeFnBody(decl, func) catch |err| switch (err) { error.AnalysisFail => { - assert(func.bits.state != .in_progress); + assert(func.state != .in_progress); continue; }, error.OutOfMemory => return error.OutOfMemory, }, .in_progress => unreachable, + .inline_only => unreachable, // don't queue work for this .sema_failure, .dependency_failure => continue, .success => {}, } @@ -1476,9 +1477,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor var decl_arena = decl.typed_value.most_recent.arena.?.promote(module.gpa); defer decl.typed_value.most_recent.arena.?.* = decl_arena.state; log.debug("analyze liveness of {s}\n", .{decl.name}); - try liveness.analyze(module.gpa, &decl_arena.allocator, func.data.body); + try liveness.analyze(module.gpa, &decl_arena.allocator, func.body); - if (self.verbose_ir) { + if (std.builtin.mode == .Debug and self.verbose_ir) { func.dump(module.*); } } diff --git a/src/Module.zig b/src/Module.zig index db76ecd5db..be6ca0df63 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -286,75 +286,29 @@ pub const Decl = struct { /// Extern functions do not have this data structure; they are represented by /// the `Decl` only, with a `Value` tag of `extern_fn`. pub const Fn = struct { - bits: packed struct { - /// Get and set this field via `analysis` and `setAnalysis`. - state: Analysis.Tag, - /// We carry this state into `Fn` instead of leaving it in the AST so that - /// analysis of function calls can happen even on functions whose AST has - /// been unloaded from memory. - is_inline: bool, - unused_bits: u4 = 0, - }, - /// Get and set this data via `analysis` and `setAnalysis`. - data: union { - none: void, - zir: *ZIR, - body: Body, - }, owner_decl: *Decl, - - pub const Analysis = union(Tag) { - queued: *ZIR, + /// Contains un-analyzed ZIR instructions generated from Zig source AST. + /// Even after we finish analysis, the ZIR is kept in memory, so that + /// comptime and inline function calls can happen. + zir: zir.Module.Body, + /// undefined unless analysis state is `success`. + body: Body, + state: Analysis, + + pub const Analysis = enum { + queued, + /// This function intentionally only has ZIR generated because it is marked + /// inline, which means no runtime version of the function will be generated. + inline_only, in_progress, + /// There will be a corresponding ErrorMsg in Module.failed_decls sema_failure, + /// This Fn might be OK but it depends on another Decl which did not + /// successfully complete semantic analysis. dependency_failure, - success: Body, - - pub const Tag = enum(u3) { - queued, - in_progress, - /// There will be a corresponding ErrorMsg in Module.failed_decls - sema_failure, - /// This Fn might be OK but it depends on another Decl which did not - /// successfully complete semantic analysis. - dependency_failure, - success, - }; + success, }; - /// Contains un-analyzed ZIR instructions generated from Zig source AST. - pub const ZIR = struct { - body: zir.Module.Body, - arena: std.heap.ArenaAllocator.State, - }; - - pub fn analysis(self: Fn) Analysis { - return switch (self.bits.state) { - .queued => .{ .queued = self.data.zir }, - .success => .{ .success = self.data.body }, - .in_progress => .in_progress, - .sema_failure => .sema_failure, - .dependency_failure => .dependency_failure, - }; - } - - pub fn setAnalysis(self: *Fn, anal: Analysis) void { - switch (anal) { - .queued => |zir_ptr| { - self.bits.state = .queued; - self.data = .{ .zir = zir_ptr }; - }, - .success => |body| { - self.bits.state = .success; - self.data = .{ .body = body }; - }, - .in_progress, .sema_failure, .dependency_failure => { - self.bits.state = anal; - self.data = .{ .none = {} }; - }, - } - } - /// For debugging purposes. pub fn dump(self: *Fn, mod: Module) void { zir.dumpFn(mod, self); @@ -1124,7 +1078,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .param_types = param_types, }, .{}); - if (self.comp.verbose_ir) { + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { zir.dumpZir(self.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {}; } @@ -1175,14 +1129,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { const new_func = try decl_arena.allocator.create(Fn); const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); - const fn_zir = blk: { - // This scope's arena memory is discarded after the ZIR generation - // pass completes, and semantic analysis of it completes. - var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa); - errdefer gen_scope_arena.deinit(); + const fn_zir: zir.Module.Body = blk: { + // We put the ZIR inside the Decl arena. var gen_scope: Scope.GenZIR = .{ .decl = decl, - .arena = &gen_scope_arena.allocator, + .arena = &decl_arena.allocator, .parent = decl.scope, }; defer gen_scope.instructions.deinit(self.gpa); @@ -1194,7 +1145,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { const name_token = param.name_token.?; const src = tree.token_locs[name_token].start; const param_name = try self.identifierTokenString(&gen_scope.base, name_token); - const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg); + const arg = try decl_arena.allocator.create(zir.Inst.Arg); arg.* = .{ .base = .{ .tag = .arg, @@ -1206,7 +1157,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .kw_args = .{}, }; gen_scope.instructions.items[i] = &arg.base; - const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVal); + const sub_scope = try decl_arena.allocator.create(Scope.LocalVal); sub_scope.* = .{ .parent = params_scope, .gen_zir = &gen_scope, @@ -1227,18 +1178,13 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid); } - if (self.comp.verbose_ir) { + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { zir.dumpZir(self.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {}; } - const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR); - fn_zir.* = .{ - .body = .{ - .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items), - }, - .arena = gen_scope_arena.state, + break :blk .{ + .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items), }; - break :blk fn_zir; }; const is_inline = blk: { @@ -1249,13 +1195,12 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { } break :blk false; }; + const anal_state = ([2]Fn.Analysis{ .queued, .inline_only })[@boolToInt(is_inline)]; new_func.* = .{ - .bits = .{ - .state = .queued, - .is_inline = is_inline, - }, - .data = .{ .zir = fn_zir }, + .state = anal_state, + .zir = fn_zir, + .body = undefined, .owner_decl = decl, }; fn_payload.* = .{ @@ -1272,7 +1217,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { type_changed = !tvm.typed_value.ty.eql(fn_type); if (tvm.typed_value.val.castTag(.function)) |payload| { const prev_func = payload.data; - prev_is_inline = prev_func.bits.is_inline; + prev_is_inline = prev_func.state == .inline_only; } tvm.deinit(self.gpa); @@ -1391,7 +1336,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { const src = tree.token_locs[init_node.firstToken()].start; const init_inst = try astgen.expr(self, &gen_scope.base, init_result_loc, init_node); - if (self.comp.verbose_ir) { + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { zir.dumpZir(self.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {}; } @@ -1435,7 +1380,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .val = Value.initTag(.type_type), }); const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node); - if (self.comp.verbose_ir) { + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { zir.dumpZir(self.gpa, "var_type", decl.name, type_scope.instructions.items) catch {}; } @@ -1511,7 +1456,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { defer gen_scope.instructions.deinit(self.gpa); _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr); - if (self.comp.verbose_ir) { + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { zir.dumpZir(self.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {}; } @@ -1902,15 +1847,14 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { }; defer inner_block.instructions.deinit(self.gpa); - const fn_zir = func.data.zir; - defer fn_zir.arena.promote(self.gpa).deinit(); - func.setAnalysis(.in_progress); + func.state = .in_progress; log.debug("set {s} to in_progress\n", .{decl.name}); - try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body); + try zir_sema.analyzeBody(self, &inner_block.base, func.zir); const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items); - func.setAnalysis(.{ .success = .{ .instructions = instructions } }); + func.state = .success; + func.body = .{ .instructions = instructions }; log.debug("set {s} to success\n", .{decl.name}); } @@ -2407,7 +2351,7 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn self.ensureDeclAnalyzed(decl) catch |err| { if (scope.cast(Scope.Block)) |block| { if (block.func) |func| { - func.setAnalysis(.dependency_failure); + func.state = .dependency_failure; } else { block.decl.analysis = .dependency_failure; } @@ -3107,7 +3051,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Com .block => { const block = scope.cast(Scope.Block).?; if (block.func) |func| { - func.setAnalysis(.sema_failure); + func.state = .sema_failure; } else { block.decl.analysis = .sema_failure; block.decl.generation = self.generation; diff --git a/src/codegen.zig b/src/codegen.zig index 588c3dec4c..58be73a31c 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -532,7 +532,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { self.code.items.len += 4; try self.dbgSetPrologueEnd(); - try self.genBody(self.mod_fn.data.body); + try self.genBody(self.mod_fn.body); const stack_end = self.max_end_stack; if (stack_end > math.maxInt(i32)) @@ -576,7 +576,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }); } else { try self.dbgSetPrologueEnd(); - try self.genBody(self.mod_fn.data.body); + try self.genBody(self.mod_fn.body); try self.dbgSetEpilogueBegin(); } }, @@ -593,7 +593,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { try self.dbgSetPrologueEnd(); - try self.genBody(self.mod_fn.data.body); + try self.genBody(self.mod_fn.body); // Backpatch stack offset const stack_end = self.max_end_stack; @@ -638,13 +638,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { writeInt(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32()); } else { try self.dbgSetPrologueEnd(); - try self.genBody(self.mod_fn.data.body); + try self.genBody(self.mod_fn.body); try self.dbgSetEpilogueBegin(); } }, else => { try self.dbgSetPrologueEnd(); - try self.genBody(self.mod_fn.data.body); + try self.genBody(self.mod_fn.body); try self.dbgSetEpilogueBegin(); }, } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 712d663af0..1a89e22d48 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -275,7 +275,7 @@ pub fn generate(file: *C, module: *Module, decl: *Decl) !void { try writer.writeAll(" {"); const func: *Module.Fn = func_payload.data; - const instructions = func.data.body.instructions; + const instructions = func.body.instructions; if (instructions.len > 0) { try writer.writeAll("\n"); for (instructions) |inst| { diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index 1eb4f5bc29..036243dcca 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -63,7 +63,7 @@ pub fn genCode(buf: *ArrayList(u8), decl: *Decl) !void { // TODO: check for and handle death of instructions const tv = decl.typed_value.most_recent.typed_value; const mod_fn = tv.val.castTag(.function).?.data; - for (mod_fn.data.body.instructions) |inst| try genInst(buf, decl, inst); + for (mod_fn.body.instructions) |inst| try genInst(buf, decl, inst); // Write 'end' opcode try writer.writeByte(0x0B); diff --git a/src/llvm_backend.zig b/src/llvm_backend.zig index 5814aa7e7e..97406797b6 100644 --- a/src/llvm_backend.zig +++ b/src/llvm_backend.zig @@ -294,7 +294,7 @@ pub const LLVMIRModule = struct { const entry_block = llvm_func.appendBasicBlock("Entry"); self.builder.positionBuilderAtEnd(entry_block); - const instructions = func.data.body.instructions; + const instructions = func.body.instructions; for (instructions) |inst| { switch (inst.tag) { .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), diff --git a/src/zir.zig b/src/zir.zig index 64b74f24d9..56ddee919c 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -1864,13 +1864,15 @@ pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void { defer ctx.const_table.deinit(); defer ctx.arena.deinit(); - switch (module_fn.analysis()) { + switch (module_fn.state) { .queued => std.debug.print("(queued)", .{}), + .inline_only => std.debug.print("(inline_only)", .{}), .in_progress => std.debug.print("(in_progress)", .{}), .sema_failure => std.debug.print("(sema_failure)", .{}), .dependency_failure => std.debug.print("(dependency_failure)", .{}), - .success => |body| { - ctx.dump(body, std.io.getStdErr().writer()) catch @panic("failed to dump TZIR"); + .success => { + const writer = std.io.getStdErr().writer(); + ctx.dump(module_fn.body, writer) catch @panic("failed to dump TZIR"); }, } } @@ -2289,11 +2291,12 @@ const EmitZIR = struct { var instructions = std.ArrayList(*Inst).init(self.allocator); defer instructions.deinit(); - switch (module_fn.analysis()) { + switch (module_fn.state) { .queued => unreachable, .in_progress => unreachable, - .success => |body| { - try self.emitBody(body, &inst_table, &instructions); + .inline_only => unreachable, + .success => { + try self.emitBody(module_fn.body, &inst_table, &instructions); }, .sema_failure => { const err_msg = self.old_module.failed_decls.get(module_fn.owner_decl).?; @@ -2372,7 +2375,7 @@ const EmitZIR = struct { .body = .{ .instructions = arena_instrs }, }, .kw_args = .{ - .is_inline = module_fn.bits.is_inline, + .is_inline = module_fn.state == .inline_only, }, }; return self.emitUnnamedDecl(&fn_inst.base); diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 9365996bb6..e8d995dd5e 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -25,8 +25,6 @@ const trace = @import("tracy.zig").trace; const Scope = Module.Scope; const InnerError = Module.InnerError; const Decl = Module.Decl; -const astgen = @import("astgen.zig"); -const ast = std.zig.ast; pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { switch (old_inst.tag) { @@ -861,7 +859,7 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError .function => func_val.castTag(.function).?.data, else => break :blk false, }; - break :blk module_fn.bits.is_inline; + break :blk module_fn.state == .inline_only; } break :blk false; }; @@ -874,76 +872,6 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError }), else => unreachable, }; - const callee_decl = module_fn.owner_decl; - // TODO: De-duplicate this with the code in Module.zig that generates - // ZIR for the same function and re-use the same ZIR for runtime function - // generation and for inline/comptime calls. - const callee_file_scope = callee_decl.getFileScope(); - const tree = mod.getAstTree(callee_file_scope) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.AnalysisFail => return error.AnalysisFail, - // TODO: make sure this gets retried and not cached - else => return mod.fail(scope, inst.base.src, "failed to load {s}: {s}", .{ - callee_file_scope.sub_file_path, @errorName(err), - }), - }; - const ast_node = tree.root_node.decls()[callee_decl.src_index]; - const fn_proto = ast_node.castTag(.FnProto).?; - - var call_arena = std.heap.ArenaAllocator.init(mod.gpa); - defer call_arena.deinit(); - - var gen_scope: Scope.GenZIR = .{ - .decl = callee_decl, - .arena = &call_arena.allocator, - .parent = callee_decl.scope, - }; - defer gen_scope.instructions.deinit(mod.gpa); - - // We need an instruction for each parameter, and they must be first in the body. - try gen_scope.instructions.resize(mod.gpa, fn_proto.params_len); - var params_scope = &gen_scope.base; - for (fn_proto.params()) |param, i| { - const name_token = param.name_token.?; - const src = tree.token_locs[name_token].start; - const param_name = try mod.identifierTokenString(scope, name_token); - const arg = try call_arena.allocator.create(zir.Inst.Arg); - arg.* = .{ - .base = .{ - .tag = .arg, - .src = src, - }, - .positionals = .{ - .name = param_name, - }, - .kw_args = .{}, - }; - gen_scope.instructions.items[i] = &arg.base; - const sub_scope = try call_arena.allocator.create(Scope.LocalVal); - sub_scope.* = .{ - .parent = params_scope, - .gen_zir = &gen_scope, - .name = param_name, - .inst = &arg.base, - }; - params_scope = &sub_scope.base; - } - - const body_node = fn_proto.getBodyNode().?; // We handle extern functions above. - const body_block = body_node.cast(ast.Node.Block).?; - - try astgen.blockExpr(mod, params_scope, body_block); - - if (gen_scope.instructions.items.len == 0 or - !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()) - { - const src = tree.token_locs[body_block.rbrace].start; - _ = try astgen.addZIRNoOp(mod, &gen_scope.base, src, .returnvoid); - } - - if (mod.comp.verbose_ir) { - zir.dumpZir(mod.gpa, "fn_body_callee", callee_decl.name, gen_scope.instructions.items) catch {}; - } // Analyze the ZIR. The same ZIR gets analyzed into a runtime function // or an inlined call depending on what union tag the `label` field is @@ -986,9 +914,7 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError // This will have return instructions analyzed as break instructions to // the block_inst above. - try analyzeBody(mod, &child_block.base, .{ - .instructions = gen_scope.instructions.items, - }); + try analyzeBody(mod, &child_block.base, module_fn.zir); return analyzeBlockBody(mod, scope, &child_block, merges); } @@ -998,26 +924,11 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst { const fn_type = try resolveType(mod, scope, fn_inst.positionals.fn_type); - const fn_zir = blk: { - var fn_arena = std.heap.ArenaAllocator.init(mod.gpa); - errdefer fn_arena.deinit(); - - const fn_zir = try scope.arena().create(Module.Fn.ZIR); - fn_zir.* = .{ - .body = .{ - .instructions = fn_inst.positionals.body.instructions, - }, - .arena = fn_arena.state, - }; - break :blk fn_zir; - }; const new_func = try scope.arena().create(Module.Fn); new_func.* = .{ - .bits = .{ - .state = .queued, - .is_inline = fn_inst.kw_args.is_inline, - }, - .data = .{ .zir = fn_zir }, + .state = if (fn_inst.kw_args.is_inline) .inline_only else .queued, + .zir = fn_inst.positionals.body, + .body = undefined, .owner_decl = scope.decl().?, }; return mod.constInst(scope, fn_inst.base.src, .{ diff --git a/test/stage2/test.zig b/test/stage2/test.zig index 18c54b367e..79f5c3a73e 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -342,6 +342,7 @@ pub fn addCases(ctx: *TestContext) !void { , "", ); + // comptime function call case.addCompareOutput( \\export fn _start() noreturn { \\ exit(); @@ -365,6 +366,30 @@ pub fn addCases(ctx: *TestContext) !void { , "", ); + // Inline function call + case.addCompareOutput( + \\export fn _start() noreturn { + \\ var x: usize = 3; + \\ const y = add(1, 2, x); + \\ exit(y - 6); + \\} + \\ + \\inline fn add(a: usize, b: usize, c: usize) usize { + \\ return a + b + c; + \\} + \\ + \\fn exit(code: usize) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); } { -- cgit v1.2.3