mirror of
https://github.com/ziglang/zig.git
synced 2026-02-15 13:58:27 +00:00
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.
This commit is contained in:
parent
9362f382ab
commit
006e7f6805
@ -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.*);
|
||||
}
|
||||
}
|
||||
|
||||
140
src/Module.zig
140
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,
|
||||
in_progress,
|
||||
sema_failure,
|
||||
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,
|
||||
};
|
||||
};
|
||||
|
||||
/// Contains un-analyzed ZIR instructions generated from Zig source AST.
|
||||
pub const ZIR = struct {
|
||||
body: zir.Module.Body,
|
||||
arena: std.heap.ArenaAllocator.State,
|
||||
/// 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,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
@ -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();
|
||||
},
|
||||
}
|
||||
|
||||
@ -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| {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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).?),
|
||||
|
||||
17
src/zir.zig
17
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);
|
||||
|
||||
@ -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, .{
|
||||
|
||||
@ -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;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user