Commit 006e7f6805
Changed files (9)
test
stage2
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| {
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);
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();
},
}
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.*);
}
}
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).?),
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;
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);
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, .{
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;
+ \\}
+ ,
+ "",
+ );
}
{