authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-07-09 14:41:36-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-07-18 19:02:05-07:00
log4a55fc6c53b27ed9be0bd316969fbdfbef98ad1f
tree682bb4663ac7fc4337676bf005cfd6d75bd93138
parentf3dc53f6b53e8493b341f82cb06a56e33e80e6b7

InternPool: avoid false negatives for functions with inferred error sets

There is one case where function types may be inequal but we still want to find the same function body instance in InternPool. In the case of the functions having an inferred error set, the key used to find an existing function body will necessarily have a unique inferred error set type, because it refers to the function body InternPool Index. To make this case work we omit the inferred error set from the equality and hashing functions.

1 files changed, 128 insertions(+), 77 deletions(-)

src/InternPool.zig+128-77
......@@ -537,6 +537,32 @@ pub const Key = union(enum) {
537537 assert(i < self.param_types.len);
538538 return @as(u1, @truncate(self.noalias_bits >> i)) != 0;
539539 }
540
541 pub fn eql(a: FuncType, b: FuncType, ip: *const InternPool) bool {
542 return std.mem.eql(Index, a.param_types.get(ip), b.param_types.get(ip)) and
543 a.return_type == b.return_type and
544 a.comptime_bits == b.comptime_bits and
545 a.noalias_bits == b.noalias_bits and
546 a.alignment == b.alignment and
547 a.cc == b.cc and
548 a.is_var_args == b.is_var_args and
549 a.is_generic == b.is_generic and
550 a.is_noinline == b.is_noinline;
551 }
552
553 pub fn hash(self: FuncType, hasher: *Hash, ip: *const InternPool) void {
554 for (self.param_types.get(ip)) |param_type| {
555 std.hash.autoHash(hasher, param_type);
556 }
557 std.hash.autoHash(hasher, self.return_type);
558 std.hash.autoHash(hasher, self.comptime_bits);
559 std.hash.autoHash(hasher, self.noalias_bits);
560 std.hash.autoHash(hasher, self.alignment);
561 std.hash.autoHash(hasher, self.cc);
562 std.hash.autoHash(hasher, self.is_var_args);
563 std.hash.autoHash(hasher, self.is_generic);
564 std.hash.autoHash(hasher, self.is_noinline);
565 }
540566 };
541567
542568 pub const Variable = struct {
......@@ -572,6 +598,7 @@ pub const Key = union(enum) {
572598 zir_body_inst_extra_index: u32,
573599 /// Index into extra array of the resolved inferred error set for this function.
574600 /// Used for mutating that data.
601 /// 0 when the function does not have an inferred error set.
575602 resolved_error_set_extra_index: u32,
576603 /// When a generic function is instantiated, branch_quota is inherited from the
577604 /// active Sema context. Importantly, this value is also updated when an existing
......@@ -942,17 +969,7 @@ pub const Key = union(enum) {
942969
943970 .func_type => |func_type| {
944971 var hasher = Hash.init(seed);
945 for (func_type.param_types.get(ip)) |param_type| {
946 std.hash.autoHash(&hasher, param_type);
947 }
948 std.hash.autoHash(&hasher, func_type.return_type);
949 std.hash.autoHash(&hasher, func_type.comptime_bits);
950 std.hash.autoHash(&hasher, func_type.noalias_bits);
951 std.hash.autoHash(&hasher, func_type.alignment);
952 std.hash.autoHash(&hasher, func_type.cc);
953 std.hash.autoHash(&hasher, func_type.is_var_args);
954 std.hash.autoHash(&hasher, func_type.is_generic);
955 std.hash.autoHash(&hasher, func_type.is_noinline);
972 func_type.hash(&hasher, ip);
956973 return hasher.final();
957974 },
958975
......@@ -964,13 +981,24 @@ pub const Key = union(enum) {
964981 },
965982
966983 .func => |func| {
967 if (func.generic_owner == .none)
984 // In the case of a function with an inferred error set, we
985 // must not include the inferred error set type in the hash,
986 // otherwise we would get false negatives for interning generic
987 // function instances which have inferred error sets.
988
989 if (func.generic_owner == .none and func.resolved_error_set_extra_index == 0)
968990 return Hash.hash(seed, asBytes(&func.owner_decl) ++ asBytes(&func.ty));
969991
970992 var hasher = Hash.init(seed);
971993 std.hash.autoHash(&hasher, func.generic_owner);
972994 for (func.comptime_args.get(ip)) |arg| std.hash.autoHash(&hasher, arg);
973 std.hash.autoHash(&hasher, func.ty);
995 if (func.resolved_error_set_extra_index == 0) {
996 std.hash.autoHash(&hasher, func.ty);
997 } else {
998 var ty_info = ip.indexToFuncType(func.ty).?;
999 ty_info.return_type = ip.errorUnionPayload(ty_info.return_type);
1000 ty_info.hash(&hasher, ip);
1001 }
9741002 return hasher.final();
9751003 },
9761004
......@@ -1079,13 +1107,37 @@ pub const Key = union(enum) {
10791107 if (a_info.generic_owner != b_info.generic_owner)
10801108 return false;
10811109
1082 if (a_info.ty != b_info.ty)
1083 return false;
1110 if (a_info.generic_owner == .none) {
1111 if (a_info.owner_decl != b_info.owner_decl)
1112 return false;
1113 } else {
1114 if (!std.mem.eql(
1115 Index,
1116 a_info.comptime_args.get(ip),
1117 b_info.comptime_args.get(ip),
1118 )) return false;
1119 }
10841120
1085 if (a_info.generic_owner == .none)
1086 return a_info.owner_decl == b_info.owner_decl;
1121 if (a_info.ty == b_info.ty)
1122 return true;
10871123
1088 return std.mem.eql(Index, a_info.comptime_args.get(ip), b_info.comptime_args.get(ip));
1124 // There is one case where the types may be inequal but we
1125 // still want to find the same function body instance. In the
1126 // case of the functions having an inferred error set, the key
1127 // used to find an existing function body will necessarily have
1128 // a unique inferred error set type, because it refers to the
1129 // function body InternPool Index. To make this case work we
1130 // omit the inferred error set from the equality check.
1131 if (a_info.resolved_error_set_extra_index == 0 or
1132 b_info.resolved_error_set_extra_index == 0)
1133 {
1134 return false;
1135 }
1136 var a_ty_info = ip.indexToFuncType(a_info.ty).?;
1137 a_ty_info.return_type = ip.errorUnionPayload(a_ty_info.return_type);
1138 var b_ty_info = ip.indexToFuncType(b_info.ty).?;
1139 b_ty_info.return_type = ip.errorUnionPayload(a_ty_info.return_type);
1140 return a_ty_info.eql(b_ty_info, ip);
10891141 },
10901142
10911143 .ptr => |a_info| {
......@@ -1246,16 +1298,7 @@ pub const Key = union(enum) {
12461298
12471299 .func_type => |a_info| {
12481300 const b_info = b.func_type;
1249
1250 return std.mem.eql(Index, a_info.param_types.get(ip), b_info.param_types.get(ip)) and
1251 a_info.return_type == b_info.return_type and
1252 a_info.comptime_bits == b_info.comptime_bits and
1253 a_info.noalias_bits == b_info.noalias_bits and
1254 a_info.alignment == b_info.alignment and
1255 a_info.cc == b_info.cc and
1256 a_info.is_var_args == b_info.is_var_args and
1257 a_info.is_generic == b_info.is_generic and
1258 a_info.is_noinline == b_info.is_noinline;
1301 return Key.FuncType.eql(a_info, b_info, ip);
12591302 },
12601303
12611304 .memoized_call => |a_info| {
......@@ -2156,7 +2199,6 @@ pub const Tag = enum(u8) {
21562199 const Error = Key.Error;
21572200 const EnumTag = Key.EnumTag;
21582201 const ExternFunc = Key.ExternFunc;
2159 const Func = Key.Func;
21602202 const Union = Key.Union;
21612203 const TypePointer = Key.PtrType;
21622204
......@@ -3137,7 +3179,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
31373179 },
31383180 .extern_func => .{ .extern_func = ip.extraData(Tag.ExternFunc, data) },
31393181 .func_instance => .{ .func = ip.indexToKeyFuncInstance(data) },
3140 .func_decl => .{ .func = ip.indexToKeyFuncDecl(data) },
3182 .func_decl => .{ .func = ip.extraIndexToFuncDecl(data) },
31413183 .only_possible_value => {
31423184 const ty = @as(Index, @enumFromInt(data));
31433185 const ty_item = ip.items.get(@intFromEnum(ty));
......@@ -3286,9 +3328,9 @@ fn extraFuncType(ip: *const InternPool, extra_index: u32) Key.FuncType {
32863328 };
32873329}
32883330
3289fn indexToKeyFuncDecl(ip: *const InternPool, data: u32) Key.Func {
3331fn extraIndexToFuncDecl(ip: *const InternPool, extra_index: u32) Key.Func {
32903332 _ = ip;
3291 _ = data;
3333 _ = extra_index;
32923334 @panic("TODO");
32933335}
32943336
......@@ -4357,38 +4399,41 @@ pub fn getFuncDecl(ip: *InternPool, gpa: Allocator, key: GetFuncDeclKey) Allocat
43574399
43584400 try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Tag.FuncDecl).Struct.fields.len);
43594401 try ip.items.ensureUnusedCapacity(gpa, 1);
4402 try ip.map.ensureUnusedCapacity(gpa, 1);
43604403
4361 ip.items.appendAssumeCapacity(.{
4362 .tag = .func_decl,
4363 .data = ip.addExtraAssumeCapacity(Tag.FuncDecl{
4364 .analysis = .{
4365 .state = if (key.cc == .Inline) .inline_only else .none,
4366 .is_cold = false,
4367 .is_noinline = key.is_noinline,
4368 .calls_or_awaits_errorable_fn = false,
4369 .stack_alignment = .none,
4370 .inferred_error_set = false,
4371 },
4372 .owner_decl = key.owner_decl,
4373 .ty = key.ty,
4374 .zir_body_inst = key.zir_body_inst,
4375 .lbrace_line = key.lbrace_line,
4376 .rbrace_line = key.rbrace_line,
4377 .lbrace_column = key.lbrace_column,
4378 .rbrace_column = key.rbrace_column,
4379 }),
4404 const func_decl_extra_index = ip.addExtraAssumeCapacity(Tag.FuncDecl{
4405 .analysis = .{
4406 .state = if (key.cc == .Inline) .inline_only else .none,
4407 .is_cold = false,
4408 .is_noinline = key.is_noinline,
4409 .calls_or_awaits_errorable_fn = false,
4410 .stack_alignment = .none,
4411 .inferred_error_set = false,
4412 },
4413 .owner_decl = key.owner_decl,
4414 .ty = key.ty,
4415 .zir_body_inst = key.zir_body_inst,
4416 .lbrace_line = key.lbrace_line,
4417 .rbrace_line = key.rbrace_line,
4418 .lbrace_column = key.lbrace_column,
4419 .rbrace_column = key.rbrace_column,
43804420 });
43814421
43824422 const adapter: KeyAdapter = .{ .intern_pool = ip };
4383 const gop = try ip.map.getOrPutAdapted(gpa, Key{
4384 .func = indexToKeyFuncDecl(ip, @intCast(ip.items.len - 1)),
4423 const gop = ip.map.getOrPutAssumeCapacityAdapted(Key{
4424 .func = extraIndexToFuncDecl(ip, func_decl_extra_index),
43854425 }, adapter);
4386 if (!gop.found_existing) return @enumFromInt(ip.items.len - 1);
43874426
4388 // An existing function type was found; undo the additions to our two arrays.
4389 ip.items.len -= 1;
4390 ip.extra.items.len = prev_extra_len;
4391 return @enumFromInt(gop.index);
4427 if (gop.found_existing) {
4428 ip.extra.items.len = prev_extra_len;
4429 return @enumFromInt(gop.index);
4430 }
4431
4432 ip.items.appendAssumeCapacity(.{
4433 .tag = .func_decl,
4434 .data = func_decl_extra_index,
4435 });
4436 return @enumFromInt(ip.items.len - 1);
43924437}
43934438
43944439pub const GetFuncDeclIesKey = struct {
......@@ -4434,25 +4479,27 @@ pub fn getFuncDeclIes(ip: *InternPool, gpa: Allocator, key: GetFuncDeclIesKey) A
44344479 params_len);
44354480 try ip.items.ensureUnusedCapacity(gpa, 4);
44364481
4482 const func_decl_extra_index = ip.addExtraAssumeCapacity(Tag.FuncDecl{
4483 .analysis = .{
4484 .state = if (key.cc == .Inline) .inline_only else .none,
4485 .is_cold = false,
4486 .is_noinline = key.is_noinline,
4487 .calls_or_awaits_errorable_fn = false,
4488 .stack_alignment = .none,
4489 .inferred_error_set = true,
4490 },
4491 .owner_decl = key.owner_decl,
4492 .ty = @enumFromInt(ip.items.len + 1),
4493 .zir_body_inst = key.zir_body_inst,
4494 .lbrace_line = key.lbrace_line,
4495 .rbrace_line = key.rbrace_line,
4496 .lbrace_column = key.lbrace_column,
4497 .rbrace_column = key.rbrace_column,
4498 });
4499
44374500 ip.items.appendAssumeCapacity(.{
44384501 .tag = .func_decl,
4439 .data = ip.addExtraAssumeCapacity(Tag.FuncDecl{
4440 .analysis = .{
4441 .state = if (key.cc == .Inline) .inline_only else .none,
4442 .is_cold = false,
4443 .is_noinline = key.is_noinline,
4444 .calls_or_awaits_errorable_fn = false,
4445 .stack_alignment = .none,
4446 .inferred_error_set = true,
4447 },
4448 .owner_decl = key.owner_decl,
4449 .ty = @enumFromInt(ip.items.len + 1),
4450 .zir_body_inst = key.zir_body_inst,
4451 .lbrace_line = key.lbrace_line,
4452 .rbrace_line = key.rbrace_line,
4453 .lbrace_column = key.lbrace_column,
4454 .rbrace_column = key.rbrace_column,
4455 }),
4502 .data = func_decl_extra_index,
44564503 });
44574504 ip.extra.appendAssumeCapacity(@intFromEnum(Index.none));
44584505
......@@ -4497,7 +4544,7 @@ pub fn getFuncDeclIes(ip: *InternPool, gpa: Allocator, key: GetFuncDeclIesKey) A
44974544
44984545 const adapter: KeyAdapter = .{ .intern_pool = ip };
44994546 const gop = ip.map.getOrPutAssumeCapacityAdapted(Key{
4500 .func = indexToKeyFuncDecl(ip, @intCast(ip.items.len - 4)),
4547 .func = extraIndexToFuncDecl(ip, func_decl_extra_index),
45014548 }, adapter);
45024549 if (!gop.found_existing) {
45034550 assert(!ip.map.getOrPutAssumeCapacityAdapted(Key{ .error_union_type = .{
......@@ -5570,6 +5617,10 @@ pub fn errorUnionSet(ip: *const InternPool, ty: Index) Index {
55705617 return ip.indexToKey(ty).error_union_type.error_set_type;
55715618}
55725619
5620pub fn errorUnionPayload(ip: *const InternPool, ty: Index) Index {
5621 return ip.indexToKey(ty).error_union_type.payload_type;
5622}
5623
55735624/// The is only legal because the initializer is not part of the hash.
55745625pub fn mutateVarInit(ip: *InternPool, index: Index, init_index: Index) void {
55755626 const item = ip.items.get(@intFromEnum(index));
......@@ -5738,7 +5789,7 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void {
57385789 .float_comptime_float => @sizeOf(Float128),
57395790 .variable => @sizeOf(Tag.Variable) + @sizeOf(Module.Decl),
57405791 .extern_func => @sizeOf(Tag.ExternFunc) + @sizeOf(Module.Decl),
5741 .func_decl => @sizeOf(Tag.Func) + @sizeOf(Module.Decl),
5792 .func_decl => @sizeOf(Tag.FuncDecl) + @sizeOf(Module.Decl),
57425793 .func_instance => b: {
57435794 const info = ip.extraData(Tag.FuncInstance, data);
57445795 const ty = ip.typeOf(info.generic_owner);