aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-03-20 22:40:08 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-03-20 22:40:08 -0700
commit7598a00f34e91375bc8d4f57e8f5ecbc0d1b4d14 (patch)
tree75338d2cf30fb9092692d0b447d8e2cd610d3023 /src
parentd8692b8bdb4630f2bb2763cdbe609de7d73b28d8 (diff)
downloadzig-7598a00f34e91375bc8d4f57e8f5ecbc0d1b4d14.tar.gz
zig-7598a00f34e91375bc8d4f57e8f5ecbc0d1b4d14.zip
stage2: fix memory management of ZIR code
* free Module.Fn ZIR code when destroying the owner Decl * unreachable_safe and unreachable_unsafe are collapsed into one ZIR instruction with a safety flag. * astgen: emit an unreachable instruction for unreachable literals * don't forget to call deinit on ZIR code * astgen: implement some builtin functions
Diffstat (limited to 'src')
-rw-r--r--src/Module.zig33
-rw-r--r--src/Sema.zig15
-rw-r--r--src/astgen.zig71
-rw-r--r--src/main.zig33
-rw-r--r--src/zir.zig46
5 files changed, 114 insertions, 84 deletions
diff --git a/src/Module.zig b/src/Module.zig
index 9830e43d4a..dcf57bb709 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -224,6 +224,10 @@ pub const Decl = struct {
const gpa = module.gpa;
gpa.free(mem.spanZ(decl.name));
if (decl.typedValueManaged()) |tvm| {
+ if (tvm.typed_value.val.castTag(.function)) |payload| {
+ const func = payload.data;
+ func.deinit(gpa);
+ }
tvm.deinit(gpa);
}
decl.dependants.deinit(gpa);
@@ -334,7 +338,7 @@ pub const EmitH = struct {
fwd_decl: std.ArrayListUnmanaged(u8) = .{},
};
-/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
+/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
/// 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 {
@@ -347,6 +351,7 @@ pub const Fn = struct {
/// The number of parameters is determined by referring to the type.
/// The first N elements of `extra` are indexes into `string_bytes` to
/// a null-terminated string.
+ /// This memory is managed with gpa, must be freed when the function is freed.
zir: zir.Code,
/// undefined unless analysis state is `success`.
body: ir.Body,
@@ -370,6 +375,10 @@ pub const Fn = struct {
pub fn dump(func: *Fn, mod: Module) void {
ir.dumpFn(mod, func);
}
+
+ pub fn deinit(func: *Fn, gpa: *Allocator) void {
+ func.zir.deinit(gpa);
+ }
};
pub const Var = struct {
@@ -1502,8 +1511,7 @@ pub const WipZirCode = struct {
.ret_node,
.ret_tok,
.ret_coerce,
- .unreachable_unsafe,
- .unreachable_safe,
+ .@"unreachable",
.loop,
.suspend_block,
.suspend_block_one,
@@ -1521,6 +1529,7 @@ pub const WipZirCode = struct {
pub fn deinit(wzc: *WipZirCode) void {
wzc.instructions.deinit(wzc.gpa);
wzc.extra.deinit(wzc.gpa);
+ wzc.string_bytes.deinit(wzc.gpa);
}
};
@@ -2078,7 +2087,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool {
var analysis_arena = std.heap.ArenaAllocator.init(mod.gpa);
defer analysis_arena.deinit();
- const code: zir.Code = blk: {
+ var code: zir.Code = blk: {
var wip_zir_code: WipZirCode = .{
.decl = decl,
.arena = &analysis_arena.allocator,
@@ -2102,6 +2111,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool {
}
break :blk code;
};
+ defer code.deinit(mod.gpa);
var sema: Sema = .{
.mod = mod,
@@ -2154,17 +2164,17 @@ fn astgenAndSemaFn(
var fn_type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
defer fn_type_scope_arena.deinit();
- var fn_type_wip_zir_exec: WipZirCode = .{
+ var fn_type_wip_zir_code: WipZirCode = .{
.decl = decl,
.arena = &fn_type_scope_arena.allocator,
.gpa = mod.gpa,
};
- defer fn_type_wip_zir_exec.deinit();
+ defer fn_type_wip_zir_code.deinit();
var fn_type_scope: Scope.GenZir = .{
.force_comptime = true,
.parent = &decl.container.base,
- .zir_code = &fn_type_wip_zir_exec,
+ .zir_code = &fn_type_wip_zir_code,
};
defer fn_type_scope.instructions.deinit(mod.gpa);
@@ -2317,7 +2327,8 @@ fn astgenAndSemaFn(
errdefer decl_arena.deinit();
const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
- const fn_type_code = try fn_type_scope.finish();
+ var fn_type_code = try fn_type_scope.finish();
+ defer fn_type_code.deinit(mod.gpa);
if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
fn_type_code.dump(mod.gpa, "fn_type", &fn_type_scope.base, 0) catch {};
}
@@ -2621,7 +2632,8 @@ fn astgenAndSemaVarDecl(
init_result_loc,
var_decl.ast.init_node,
);
- const code = try gen_scope.finish();
+ var code = try gen_scope.finish();
+ defer code.deinit(mod.gpa);
if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
code.dump(mod.gpa, "var_init", &gen_scope.base, 0) catch {};
}
@@ -2683,7 +2695,8 @@ fn astgenAndSemaVarDecl(
defer type_scope.instructions.deinit(mod.gpa);
const var_type = try astgen.typeExpr(mod, &type_scope.base, var_decl.ast.type_node);
- const code = try type_scope.finish();
+ var code = try type_scope.finish();
+ defer code.deinit(mod.gpa);
if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
code.dump(mod.gpa, "var_type", &type_scope.base, 0) catch {};
}
diff --git a/src/Sema.zig b/src/Sema.zig
index d039b5abd4..807160eec0 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -137,8 +137,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde
.as_node => try sema.zirAsNode(block, zir_inst),
.@"asm" => try sema.zirAsm(block, zir_inst, false),
.asm_volatile => try sema.zirAsm(block, zir_inst, true),
- .unreachable_safe => try sema.zirUnreachable(block, zir_inst, true),
- .unreachable_unsafe => try sema.zirUnreachable(block, zir_inst, false),
+ .@"unreachable" => try sema.zirUnreachable(block, zir_inst),
.ret_coerce => try sema.zirRetTok(block, zir_inst, true),
.ret_tok => try sema.zirRetTok(block, zir_inst, false),
.ret_node => try sema.zirRetNode(block, zir_inst),
@@ -2852,17 +2851,13 @@ fn zirCondbr(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) Inne
return parent_block.addCondBr(src, cond, tzir_then_body, tzir_else_body);
}
-fn zirUnreachable(
- sema: *Sema,
- block: *Scope.Block,
- inst: zir.Inst.Index,
- safety_check: bool,
-) InnerError!*Inst {
+fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
- const src_node = sema.code.instructions.items(.data)[inst].node;
- const src: LazySrcLoc = .{ .node_offset = src_node };
+ const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable";
+ const src = inst_data.src();
+ const safety_check = inst_data.safety;
try sema.requireRuntimeBlock(block, src);
// TODO Add compile error for @optimizeFor occurring too late in a scope.
if (safety_check and block.wantSafety()) {
diff --git a/src/astgen.zig b/src/astgen.zig
index 342c6be916..f9d06e9475 100644
--- a/src/astgen.zig
+++ b/src/astgen.zig
@@ -415,10 +415,13 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In
return callExpr(mod, scope, rl, node, tree.callFull(node));
},
- .unreachable_literal => {
- const result = @enumToInt(zir.Const.unreachable_value);
- return rvalue(mod, scope, rl, result, node);
- },
+ .unreachable_literal => return gz.add(.{
+ .tag = .@"unreachable",
+ .data = .{ .@"unreachable" = .{
+ .safety = true,
+ .src_node = gz.zir_code.decl.nodeIndexToRelative(node),
+ } },
+ }),
.@"return" => return ret(mod, scope, node),
.field_access => return fieldAccess(mod, scope, rl, node),
.float_literal => return floatLiteral(mod, scope, rl, node),
@@ -3012,10 +3015,11 @@ fn as(
scope: *Scope,
rl: ResultLoc,
builtin_token: ast.TokenIndex,
- src: usize,
+ node: ast.Node.Index,
lhs: ast.Node.Index,
rhs: ast.Node.Index,
) InnerError!zir.Inst.Ref {
+ if (true) @panic("TODO update for zir-memory-layout");
const dest_type = try typeExpr(mod, scope, lhs);
switch (rl) {
.none, .discard, .ref, .ty => {
@@ -3090,10 +3094,11 @@ fn bitCast(
scope: *Scope,
rl: ResultLoc,
builtin_token: ast.TokenIndex,
- src: usize,
+ node: ast.Node.Index,
lhs: ast.Node.Index,
rhs: ast.Node.Index,
) InnerError!zir.Inst.Ref {
+ if (true) @panic("TODO update for zir-memory-layout");
const dest_type = try typeExpr(mod, scope, lhs);
switch (rl) {
.none => {
@@ -3138,9 +3143,10 @@ fn typeOf(
scope: *Scope,
rl: ResultLoc,
builtin_token: ast.TokenIndex,
- src: usize,
+ node: ast.Node.Index,
params: []const ast.Node.Index,
) InnerError!zir.Inst.Ref {
+ if (true) @panic("TODO update for zir-memory-layout");
if (params.len < 1) {
return mod.failTok(scope, builtin_token, "expected at least 1 argument, found 0", .{});
}
@@ -3158,14 +3164,13 @@ fn builtinCall(
mod: *Module,
scope: *Scope,
rl: ResultLoc,
- call: ast.Node.Index,
+ node: ast.Node.Index,
params: []const ast.Node.Index,
) InnerError!zir.Inst.Ref {
- if (true) @panic("TODO update for zir-memory-layout");
const tree = scope.tree();
const main_tokens = tree.nodes.items(.main_token);
- const builtin_token = main_tokens[call];
+ const builtin_token = main_tokens[node];
const builtin_name = tree.tokenSlice(builtin_token);
// We handle the different builtins manually because they have different semantics depending
@@ -3187,56 +3192,60 @@ fn builtinCall(
}
}
+ const gz = scope.getGenZir();
+
switch (info.tag) {
.ptr_to_int => {
const operand = try expr(mod, scope, .none, params[0]);
- const result = try addZIRUnOp(mod, scope, src, .ptrtoint, operand);
- return rvalue(mod, scope, rl, result);
+ const result = try gz.addUnNode(.ptrtoint, operand, node);
+ return rvalue(mod, scope, rl, result, node);
},
.float_cast => {
+ if (true) @panic("TODO update for zir-memory-layout");
const dest_type = try typeExpr(mod, scope, params[0]);
const rhs = try expr(mod, scope, .none, params[1]);
const result = try addZIRBinOp(mod, scope, src, .floatcast, dest_type, rhs);
- return rvalue(mod, scope, rl, result);
+ return rvalue(mod, scope, rl, result, node);
},
.int_cast => {
+ if (true) @panic("TODO update for zir-memory-layout");
const dest_type = try typeExpr(mod, scope, params[0]);
const rhs = try expr(mod, scope, .none, params[1]);
const result = try addZIRBinOp(mod, scope, src, .intcast, dest_type, rhs);
- return rvalue(mod, scope, rl, result);
+ return rvalue(mod, scope, rl, result, node);
},
.breakpoint => {
+ if (true) @panic("TODO update for zir-memory-layout");
const result = try addZIRNoOp(mod, scope, src, .breakpoint);
- return rvalue(mod, scope, rl, result);
+ return rvalue(mod, scope, rl, result, node);
},
.import => {
const target = try expr(mod, scope, .none, params[0]);
- const result = try addZIRUnOp(mod, scope, src, .import, target);
- return rvalue(mod, scope, rl, result);
+ const result = try gz.addUnNode(.import, target, node);
+ return rvalue(mod, scope, rl, result, node);
},
.compile_error => {
const target = try expr(mod, scope, .none, params[0]);
- const result = try addZIRUnOp(mod, scope, src, .compile_error, target);
- return rvalue(mod, scope, rl, result);
+ const result = try gz.addUnNode(.compile_error, target, node);
+ return rvalue(mod, scope, rl, result, node);
},
.set_eval_branch_quota => {
- const u32_type = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.u32_type),
- });
- const quota = try expr(mod, scope, .{ .ty = u32_type }, params[0]);
- const result = try addZIRUnOp(mod, scope, src, .set_eval_branch_quota, quota);
- return rvalue(mod, scope, rl, result);
+ const u32_rl: ResultLoc = .{ .ty = @enumToInt(zir.Const.u32_type) };
+ const quota = try expr(mod, scope, u32_rl, params[0]);
+ const result = try gz.addUnNode(.set_eval_branch_quota, quota, node);
+ return rvalue(mod, scope, rl, result, node);
},
.compile_log => {
+ if (true) @panic("TODO update for zir-memory-layout");
const arena = scope.arena();
var targets = try arena.alloc(zir.Inst.Ref, params.len);
for (params) |param, param_i|
targets[param_i] = try expr(mod, scope, .none, param);
const result = try addZIRInst(mod, scope, src, zir.Inst.CompileLog, .{ .to_log = targets }, .{});
- return rvalue(mod, scope, rl, result);
+ return rvalue(mod, scope, rl, result, node);
},
.field => {
+ if (true) @panic("TODO update for zir-memory-layout");
const string_type = try addZIRInstConst(mod, scope, src, .{
.ty = Type.initTag(.type),
.val = Value.initTag(.const_slice_u8_type),
@@ -3252,11 +3261,11 @@ fn builtinCall(
return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val_named, .{
.object = try expr(mod, scope, .none, params[0]),
.field_name = try comptimeExpr(mod, scope, string_rl, params[1]),
- }));
+ }), node);
},
- .as => return as(mod, scope, rl, builtin_token, src, params[0], params[1]),
- .bit_cast => return bitCast(mod, scope, rl, builtin_token, src, params[0], params[1]),
- .TypeOf => return typeOf(mod, scope, rl, builtin_token, src, params),
+ .as => return as(mod, scope, rl, builtin_token, node, params[0], params[1]),
+ .bit_cast => return bitCast(mod, scope, rl, builtin_token, node, params[0], params[1]),
+ .TypeOf => return typeOf(mod, scope, rl, builtin_token, node, params),
.add_with_overflow,
.align_cast,
diff --git a/src/main.zig b/src/main.zig
index 76f957456a..272187a9ed 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -1750,15 +1750,12 @@ fn buildOutputType(
}
const self_exe_path = try fs.selfExePathAlloc(arena);
- var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir|
- .{
- .path = lib_dir,
- .handle = try fs.cwd().openDir(lib_dir, .{}),
- }
- else
- introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
- fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
- };
+ var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{
+ .path = lib_dir,
+ .handle = try fs.cwd().openDir(lib_dir, .{}),
+ } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
+ fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
+ };
defer zig_lib_directory.handle.close();
var thread_pool: ThreadPool = undefined;
@@ -2461,15 +2458,12 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
}
}
- var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir|
- .{
- .path = lib_dir,
- .handle = try fs.cwd().openDir(lib_dir, .{}),
- }
- else
- introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
- fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
- };
+ var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{
+ .path = lib_dir,
+ .handle = try fs.cwd().openDir(lib_dir, .{}),
+ } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
+ fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
+ };
defer zig_lib_directory.handle.close();
const std_special = "std" ++ fs.path.sep_str ++ "special";
@@ -3281,8 +3275,7 @@ pub const ClangArgIterator = struct {
self.zig_equivalent = clang_arg.zig_equivalent;
break :find_clang_arg;
},
- }
- else {
+ } else {
fatal("Unknown Clang option: '{s}'", .{arg});
}
}
diff --git a/src/zir.zig b/src/zir.zig
index 5ddbcd659c..286d5585df 100644
--- a/src/zir.zig
+++ b/src/zir.zig
@@ -67,6 +67,13 @@ pub const Code = struct {
return code.string_bytes[index..end :0];
}
+ pub fn deinit(code: *Code, gpa: *Allocator) void {
+ code.instructions.deinit(gpa);
+ gpa.free(code.string_bytes);
+ gpa.free(code.extra);
+ code.* = undefined;
+ }
+
/// For debugging purposes, like dumpFn but for unanalyzed zir blocks
pub fn dump(
code: Code,
@@ -737,15 +744,9 @@ pub const Inst = struct {
/// of one or more params.
/// Uses the `pl_node` field. AST node is the `@TypeOf` call. Payload is `MultiOp`.
typeof_peer,
- /// Asserts control-flow will not reach this instruction. Not safety checked - the compiler
- /// will assume the correctness of this instruction.
- /// Uses the `node` union field.
- unreachable_unsafe,
- /// Asserts control-flow will not reach this instruction. In safety-checked modes,
- /// this will generate a call to the panic function unless it can be proven unreachable
- /// by the compiler.
- /// Uses the `node` union field.
- unreachable_safe,
+ /// Asserts control-flow will not reach this instruction (`unreachable`).
+ /// Uses the `unreachable` union field.
+ @"unreachable",
/// Bitwise XOR. `^`
xor,
/// Create an optional type '?T'
@@ -989,8 +990,7 @@ pub const Inst = struct {
.ret_node,
.ret_tok,
.ret_coerce,
- .unreachable_unsafe,
- .unreachable_safe,
+ .@"unreachable",
.loop,
.suspend_block,
.suspend_block_one,
@@ -1131,6 +1131,20 @@ pub const Inst = struct {
callee: Ref,
param_index: u32,
},
+ @"unreachable": struct {
+ /// Offset from Decl AST node index.
+ /// `Tag` determines which kind of AST node this points to.
+ src_node: i32,
+ /// `false`: Not safety checked - the compiler will assume the
+ /// correctness of this instruction.
+ /// `true`: In safety-checked modes, this will generate a call
+ /// to the panic function unless it can be proven unreachable by the compiler.
+ safety: bool,
+
+ pub fn src(self: @This()) LazySrcLoc {
+ return .{ .node_offset = self.src_node };
+ }
+ },
// Make sure we don't accidentally add a field to make this union
// bigger than expected. Note that in Debug builds, Zig is allowed
@@ -1408,8 +1422,6 @@ const Writer = struct {
.dbg_stmt_node,
.ret_ptr,
.ret_type,
- .unreachable_unsafe,
- .unreachable_safe,
=> try self.writeNode(stream, inst),
.decl_ref,
@@ -1424,6 +1436,7 @@ const Writer = struct {
.fn_type_cc => try self.writeFnTypeCc(stream, inst, false),
.fn_type_var_args => try self.writeFnType(stream, inst, true),
.fn_type_cc_var_args => try self.writeFnTypeCc(stream, inst, true),
+ .@"unreachable" => try self.writeUnreachable(stream, inst),
.enum_literal_small => try self.writeSmallStr(stream, inst),
@@ -1612,6 +1625,13 @@ const Writer = struct {
return self.writeFnTypeCommon(stream, param_types, inst_data.return_type, var_args, cc);
}
+ fn writeUnreachable(self: *Writer, stream: anytype, inst: Inst.Index) !void {
+ const inst_data = self.code.instructions.items(.data)[inst].@"unreachable";
+ const safety_str = if (inst_data.safety) "safe" else "unsafe";
+ try stream.print("{s}) ", .{safety_str});
+ try self.writeSrc(stream, inst_data.src());
+ }
+
fn writeFnTypeCommon(
self: *Writer,
stream: anytype,