diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-03-20 22:40:08 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-03-20 22:40:08 -0700 |
| commit | 7598a00f34e91375bc8d4f57e8f5ecbc0d1b4d14 (patch) | |
| tree | 75338d2cf30fb9092692d0b447d8e2cd610d3023 /src | |
| parent | d8692b8bdb4630f2bb2763cdbe609de7d73b28d8 (diff) | |
| download | zig-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.zig | 33 | ||||
| -rw-r--r-- | src/Sema.zig | 15 | ||||
| -rw-r--r-- | src/astgen.zig | 71 | ||||
| -rw-r--r-- | src/main.zig | 33 | ||||
| -rw-r--r-- | src/zir.zig | 46 |
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, |
