diff options
| author | Jacob Young <jacobly0@users.noreply.github.com> | 2023-05-28 02:41:22 -0400 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2023-06-10 20:47:56 -0700 |
| commit | 3b6ca1d35b950d67fff5964f0063dadf01f30e2d (patch) | |
| tree | 953815632535b965c7d32318ca515d8926cf4c4b /src/Sema.zig | |
| parent | d40b83de45db27c8c3e7a1f2ccf892563df43637 (diff) | |
| download | zig-3b6ca1d35b950d67fff5964f0063dadf01f30e2d.tar.gz zig-3b6ca1d35b950d67fff5964f0063dadf01f30e2d.zip | |
Module: move memoized data to the intern pool
This avoids memory management bugs with the previous implementation.
Diffstat (limited to 'src/Sema.zig')
| -rw-r--r-- | src/Sema.zig | 173 |
1 files changed, 95 insertions, 78 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index c1bcf53ab2..7562794d25 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -734,6 +734,7 @@ pub const Block = struct { errdefer sema.mod.abortAnonDecl(new_decl_index); try new_decl.finalizeNewArena(&wad.new_decl_arena); wad.finished = true; + try sema.mod.finalizeAnonDecl(new_decl_index); return new_decl_index; } }; @@ -2292,7 +2293,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { defer reference_stack.deinit(); // Avoid infinite loops. - var seen = std.AutoHashMap(Module.Decl.Index, void).init(gpa); + var seen = std.AutoHashMap(Decl.Index, void).init(gpa); defer seen.deinit(); var cur_reference_trace: u32 = 0; @@ -2742,7 +2743,9 @@ fn zirStructDecl( try sema.analyzeStructDecl(new_decl, inst, struct_index); try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl_index); + const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); + try mod.finalizeAnonDecl(new_decl_index); + return decl_val; } fn createAnonymousDeclTypeNamed( @@ -2941,6 +2944,7 @@ fn zirEnumDecl( new_namespace.ty = incomplete_enum.index.toType(); const decl_val = try sema.analyzeDeclVal(block, src, new_decl_index); + try mod.finalizeAnonDecl(new_decl_index); done = true; const int_tag_ty = ty: { @@ -3193,7 +3197,9 @@ fn zirUnionDecl( _ = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl); try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl_index); + const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); + try mod.finalizeAnonDecl(new_decl_index); + return decl_val; } fn zirOpaqueDecl( @@ -3257,7 +3263,9 @@ fn zirOpaqueDecl( extra_index = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl); try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl_index); + const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); + try mod.finalizeAnonDecl(new_decl_index); + return decl_val; } fn zirErrorSetDecl( @@ -3298,7 +3306,9 @@ fn zirErrorSetDecl( new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); - return sema.analyzeDeclVal(block, src, new_decl_index); + const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); + try mod.finalizeAnonDecl(new_decl_index); + return decl_val; } fn zirRetPtr(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { @@ -5133,32 +5143,35 @@ fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins return sema.addStrLit(block, bytes); } -fn addStrLit(sema: *Sema, block: *Block, zir_bytes: []const u8) CompileError!Air.Inst.Ref { - // `zir_bytes` references memory inside the ZIR module, which can get deallocated - // after semantic analysis is complete, for example in the case of the initialization - // expression of a variable declaration. +fn addStrLit(sema: *Sema, block: *Block, bytes: []const u8) CompileError!Air.Inst.Ref { const mod = sema.mod; - const gpa = sema.gpa; - const ty = try mod.arrayType(.{ - .len = zir_bytes.len, - .child = .u8_type, - .sentinel = .zero_u8, - }); - const val = try mod.intern(.{ .aggregate = .{ - .ty = ty.toIntern(), - .storage = .{ .bytes = zir_bytes }, - } }); - const gop = try mod.memoized_decls.getOrPut(gpa, val); - if (!gop.found_existing) { - var anon_decl = try block.startAnonDecl(); - defer anon_decl.deinit(); + const memoized_decl_index = memoized: { + const ty = try mod.arrayType(.{ + .len = bytes.len, + .child = .u8_type, + .sentinel = .zero_u8, + }); + const val = try mod.intern(.{ .aggregate = .{ + .ty = ty.toIntern(), + .storage = .{ .bytes = bytes }, + } }); - const decl_index = try anon_decl.finish(ty, val.toValue(), 0); + _ = try sema.typeHasRuntimeBits(ty); + const new_decl_index = try mod.createAnonymousDecl(block, .{ .ty = ty, .val = val.toValue() }); + errdefer mod.abortAnonDecl(new_decl_index); - gop.key_ptr.* = val; - gop.value_ptr.* = decl_index; - } - return sema.analyzeDeclRef(gop.value_ptr.*); + const memoized_index = try mod.intern(.{ .memoized_decl = .{ + .val = val, + .decl = new_decl_index, + } }); + const memoized_decl_index = mod.intern_pool.indexToKey(memoized_index).memoized_decl.decl; + if (memoized_decl_index != new_decl_index) + mod.abortAnonDecl(new_decl_index) + else + try mod.finalizeAnonDecl(new_decl_index); + break :memoized memoized_decl_index; + }; + return sema.analyzeDeclRef(memoized_decl_index); } fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -6868,30 +6881,15 @@ fn analyzeCall( defer child_block.instructions.deinit(gpa); defer merges.deinit(gpa); - // If it's a comptime function call, we need to memoize it as long as no external - // comptime memory is mutated. - var memoized_call_key = Module.MemoizedCall.Key{ - .func = module_fn_index, - .args_index = @intCast(u32, mod.memoized_call_args.items.len), - .args_count = @intCast(u32, func_ty_info.param_types.len), - }; - var delete_memoized_call_key = false; - defer if (delete_memoized_call_key) { - assert(mod.memoized_call_args.items.len >= memoized_call_key.args_index and - mod.memoized_call_args.items.len < memoized_call_key.args_index + memoized_call_key.args_count); - mod.memoized_call_args.shrinkRetainingCapacity(memoized_call_key.args_index); - }; - if (is_comptime_call) { - try mod.memoized_call_args.ensureUnusedCapacity(gpa, memoized_call_key.args_count); - delete_memoized_call_key = true; - } - try sema.emitBackwardBranch(block, call_src); - // Whether this call should be memoized, set to false if the call can mutate - // comptime state. + // Whether this call should be memoized, set to false if the call can mutate comptime state. var should_memoize = true; + // If it's a comptime function call, we need to memoize it as long as no external + // comptime memory is mutated. + const memoized_arg_values = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len); + var new_fn_info = mod.typeToFunc(fn_owner_decl.ty).?; new_fn_info.param_types = try sema.arena.alloc(InternPool.Index, new_fn_info.param_types.len); new_fn_info.comptime_bits = 0; @@ -6918,6 +6916,7 @@ fn analyzeCall( uncasted_args, is_comptime_call, &should_memoize, + memoized_arg_values, mod.typeToFunc(func_ty).?.param_types, func, &has_comptime_args, @@ -6935,6 +6934,7 @@ fn analyzeCall( uncasted_args, is_comptime_call, &should_memoize, + memoized_arg_values, mod.typeToFunc(func_ty).?.param_types, func, &has_comptime_args, @@ -6988,28 +6988,18 @@ fn analyzeCall( // bug generating invalid LLVM IR. const res2: Air.Inst.Ref = res2: { if (should_memoize and is_comptime_call) { - const gop = try mod.memoized_calls.getOrPutContext( - gpa, - memoized_call_key, - .{ .args = &mod.memoized_call_args }, - ); - if (gop.found_existing) { - assert(mod.memoized_call_args.items.len == memoized_call_key.args_index + memoized_call_key.args_count); - mod.memoized_call_args.shrinkRetainingCapacity(memoized_call_key.args_index); - delete_memoized_call_key = false; - - // We need to use the original memoized error set instead of fn_ret_ty. - const result = gop.value_ptr.*; - assert(result != .none); // recursive memoization? - - break :res2 try sema.addConstant(mod.intern_pool.typeOf(result).toType(), result.toValue()); + if (mod.intern_pool.getIfExists(.{ .memoized_call = .{ + .func = module_fn_index, + .arg_values = memoized_arg_values, + .result = .none, + } })) |memoized_call_index| { + const memoized_call = mod.intern_pool.indexToKey(memoized_call_index).memoized_call; + break :res2 try sema.addConstant( + mod.intern_pool.typeOf(memoized_call.result).toType(), + memoized_call.result.toValue(), + ); } - gop.value_ptr.* = .none; - } else if (delete_memoized_call_key) { - assert(mod.memoized_call_args.items.len == memoized_call_key.args_index + memoized_call_key.args_count); - mod.memoized_call_args.shrinkRetainingCapacity(memoized_call_key.args_index); } - delete_memoized_call_key = false; const new_func_resolved_ty = try mod.funcType(new_fn_info); if (!is_comptime_call and !block.is_typeof) { @@ -7067,10 +7057,14 @@ fn analyzeCall( if (should_memoize and is_comptime_call) { const result_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, result, ""); - mod.memoized_calls.getPtrContext( - memoized_call_key, - .{ .args = &mod.memoized_call_args }, - ).?.* = try result_val.intern(fn_ret_ty, mod); + + // TODO: check whether any external comptime memory was mutated by the + // comptime function call. If so, then do not memoize the call here. + _ = try mod.intern(.{ .memoized_call = .{ + .func = module_fn_index, + .arg_values = memoized_arg_values, + .result = try result_val.intern(fn_ret_ty, mod), + } }); } break :res2 result; @@ -7216,6 +7210,7 @@ fn analyzeInlineCallArg( uncasted_args: []const Air.Inst.Ref, is_comptime_call: bool, should_memoize: *bool, + memoized_arg_values: []InternPool.Index, raw_param_types: []const InternPool.Index, func_inst: Air.Inst.Ref, has_comptime_args: *bool, @@ -7279,7 +7274,7 @@ fn analyzeInlineCallArg( }, } should_memoize.* = should_memoize.* and !arg_val.canMutateComptimeVarState(mod); - mod.memoized_call_args.appendAssumeCapacity(try arg_val.intern(param_ty.toType(), mod)); + memoized_arg_values[arg_i.*] = try arg_val.intern(param_ty.toType(), mod); } else { sema.inst_map.putAssumeCapacityNoClobber(inst, casted_arg); } @@ -7315,7 +7310,7 @@ fn analyzeInlineCallArg( }, } should_memoize.* = should_memoize.* and !arg_val.canMutateComptimeVarState(mod); - mod.memoized_call_args.appendAssumeCapacity(try arg_val.intern(sema.typeOf(uncasted_arg), mod)); + memoized_arg_values[arg_i.*] = try arg_val.intern(sema.typeOf(uncasted_arg), mod); } else { if (zir_tags[inst] == .param_anytype_comptime) { _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime"); @@ -19363,7 +19358,9 @@ fn zirReify( } } - return sema.analyzeDeclVal(block, src, new_decl_index); + const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); + try mod.finalizeAnonDecl(new_decl_index); + return decl_val; }, .Opaque => { const fields = ip.typeOf(union_val.val).toType().structFields(mod); @@ -19407,7 +19404,9 @@ fn zirReify( new_namespace.ty = opaque_ty.toType(); try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl_index); + const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); + try mod.finalizeAnonDecl(new_decl_index); + return decl_val; }, .Union => { const fields = ip.typeOf(union_val.val).toType().structFields(mod); @@ -19604,7 +19603,9 @@ fn zirReify( } try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl_index); + const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); + try mod.finalizeAnonDecl(new_decl_index); + return decl_val; }, .Fn => { const fields = ip.typeOf(union_val.val).toType().structFields(mod); @@ -19902,7 +19903,9 @@ fn reifyStruct( } try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl_index); + const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); + try mod.finalizeAnonDecl(new_decl_index); + return decl_val; } fn zirAddrSpaceCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { @@ -31865,6 +31868,9 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { .opt, .aggregate, .un, + // memoization, not types + .memoized_decl, + .memoized_call, => unreachable, }, }; @@ -32997,6 +33003,8 @@ fn generateUnionTagTypeNumbered( .ty = Type.type, .val = undefined, }, name); + errdefer mod.abortAnonDecl(new_decl_index); + const new_decl = mod.declPtr(new_decl_index); new_decl.name_fully_qualified = true; new_decl.owns_tv = true; @@ -33016,6 +33024,7 @@ fn generateUnionTagTypeNumbered( new_decl.val = enum_ty.toValue(); + try mod.finalizeAnonDecl(new_decl_index); return enum_ty.toType(); } @@ -33049,6 +33058,7 @@ fn generateUnionTagTypeSimple( mod.declPtr(new_decl_index).name_fully_qualified = true; break :new_decl_index new_decl_index; }; + errdefer mod.abortAnonDecl(new_decl_index); const enum_ty = try mod.intern(.{ .enum_type = .{ .decl = new_decl_index, @@ -33066,6 +33076,7 @@ fn generateUnionTagTypeSimple( new_decl.owns_tv = true; new_decl.val = enum_ty.toValue(); + try mod.finalizeAnonDecl(new_decl_index); return enum_ty.toType(); } @@ -33358,6 +33369,9 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { .opt, .aggregate, .un, + // memoization, not types + .memoized_decl, + .memoized_call, => unreachable, }, }; @@ -33843,6 +33857,9 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { .opt, .aggregate, .un, + // memoization, not types + .memoized_decl, + .memoized_call, => unreachable, }, }; |
