authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-06-02 04:24:25-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-06-10 20:47:59-07:00
logda24ea7f36d056cb49e8e91064f06cb724e46f67
tree0b3920d68166cf664d4c72731d1260ea34a82d72
parent04e66e6b4deb67aef9a4064decd82a678cb7ec82

Sema: rewrite `monomorphed_funcs` usage

In an effort to delete `Value.hashUncoerced`, generic instantiation has been redesigned. Instead of just storing instantiations in `monomorphed_funcs`, partially instantiated generic argument types are also cached. This isn't quite the single `getOrPut` that it used to be, but one `get` per generic argument plus one get for the instantiation, with an equal number of `put`s per unique instantiation isn't bad.

3 files changed, 126 insertions(+), 224 deletions(-)

src/Module.zig+27-13
...@@ -99,6 +99,7 @@ tmp_hack_arena: std.heap.ArenaAllocator,...@@ -99,6 +99,7 @@ tmp_hack_arena: std.heap.ArenaAllocator,
99/// This is currently only used for string literals.99/// This is currently only used for string literals.
100memoized_decls: std.AutoHashMapUnmanaged(InternPool.Index, Decl.Index) = .{},100memoized_decls: std.AutoHashMapUnmanaged(InternPool.Index, Decl.Index) = .{},
101101
102monomorphed_func_keys: std.ArrayListUnmanaged(InternPool.Index) = .{},
102/// The set of all the generic function instantiations. This is used so that when a generic103/// The set of all the generic function instantiations. This is used so that when a generic
103/// function is called twice with the same comptime parameter arguments, both calls dispatch104/// function is called twice with the same comptime parameter arguments, both calls dispatch
104/// to the same function.105/// to the same function.
...@@ -202,24 +203,40 @@ pub const CImportError = struct {...@@ -202,24 +203,40 @@ pub const CImportError = struct {
202 }203 }
203};204};
204205
205const MonomorphedFuncsSet = std.HashMapUnmanaged(206pub const MonomorphedFuncKey = struct { func: Fn.Index, args_index: u32, args_len: u32 };
206 Fn.Index,207
207 void,208pub const MonomorphedFuncAdaptedKey = struct { func: Fn.Index, args: []const InternPool.Index };
209
210pub const MonomorphedFuncsSet = std.HashMapUnmanaged(
211 MonomorphedFuncKey,
212 InternPool.Index,
208 MonomorphedFuncsContext,213 MonomorphedFuncsContext,
209 std.hash_map.default_max_load_percentage,214 std.hash_map.default_max_load_percentage,
210);215);
211216
212const MonomorphedFuncsContext = struct {217pub const MonomorphedFuncsContext = struct {
218 mod: *Module,
219
220 pub fn eql(_: @This(), a: MonomorphedFuncKey, b: MonomorphedFuncKey) bool {
221 return std.meta.eql(a, b);
222 }
223
224 pub fn hash(ctx: @This(), key: MonomorphedFuncKey) u64 {
225 const key_args = ctx.mod.monomorphed_func_keys.items[key.args_index..][0..key.args_len];
226 return std.hash.Wyhash.hash(@enumToInt(key.func), std.mem.sliceAsBytes(key_args));
227 }
228};
229
230pub const MonomorphedFuncsAdaptedContext = struct {
213 mod: *Module,231 mod: *Module,
214232
215 pub fn eql(ctx: @This(), a: Fn.Index, b: Fn.Index) bool {233 pub fn eql(ctx: @This(), adapted_key: MonomorphedFuncAdaptedKey, other_key: MonomorphedFuncKey) bool {
216 _ = ctx;234 const other_key_args = ctx.mod.monomorphed_func_keys.items[other_key.args_index..][0..other_key.args_len];
217 return a == b;235 return adapted_key.func == other_key.func and std.mem.eql(InternPool.Index, adapted_key.args, other_key_args);
218 }236 }
219237
220 /// Must match `Sema.GenericCallAdapter.hash`.238 pub fn hash(_: @This(), adapted_key: MonomorphedFuncAdaptedKey) u64 {
221 pub fn hash(ctx: @This(), key: Fn.Index) u64 {239 return std.hash.Wyhash.hash(@enumToInt(adapted_key.func), std.mem.sliceAsBytes(adapted_key.args));
222 return ctx.mod.funcPtr(key).hash;
223 }240 }
224};241};
225242
...@@ -571,9 +588,6 @@ pub const Decl = struct {...@@ -571,9 +588,6 @@ pub const Decl = struct {
571 pub fn clearValues(decl: *Decl, mod: *Module) void {588 pub fn clearValues(decl: *Decl, mod: *Module) void {
572 if (decl.getOwnedFunctionIndex(mod).unwrap()) |func| {589 if (decl.getOwnedFunctionIndex(mod).unwrap()) |func| {
573 _ = mod.align_stack_fns.remove(func);590 _ = mod.align_stack_fns.remove(func);
574 if (mod.funcPtr(func).comptime_args != null) {
575 _ = mod.monomorphed_funcs.removeContext(func, .{ .mod = mod });
576 }
577 mod.destroyFunc(func);591 mod.destroyFunc(func);
578 }592 }
579 }593 }
src/Sema.zig+99-140
...@@ -6679,78 +6679,6 @@ fn callBuiltin(...@@ -6679,78 +6679,6 @@ fn callBuiltin(
6679 _ = try sema.analyzeCall(block, builtin_fn, func_ty, sema.src, sema.src, modifier, false, args, null, null);6679 _ = try sema.analyzeCall(block, builtin_fn, func_ty, sema.src, sema.src, modifier, false, args, null, null);
6680}6680}
66816681
6682const GenericCallAdapter = struct {
6683 generic_fn: *Module.Fn,
6684 precomputed_hash: u64,
6685 func_ty_info: InternPool.Key.FuncType,
6686 args: []const Arg,
6687 module: *Module,
6688
6689 const Arg = struct {
6690 ty: Type,
6691 val: Value,
6692 is_anytype: bool,
6693 };
6694
6695 pub fn eql(ctx: @This(), adapted_key: void, other_key: Module.Fn.Index) bool {
6696 _ = adapted_key;
6697 const other_func = ctx.module.funcPtr(other_key);
6698
6699 // Checking for equality may happen on an item that has been inserted
6700 // into the map but is not yet fully initialized. In such case, the
6701 // two initialized fields are `hash` and `generic_owner_decl`.
6702 if (ctx.generic_fn.owner_decl != other_func.generic_owner_decl.unwrap().?) return false;
6703
6704 const other_comptime_args = other_func.comptime_args.?;
6705 for (other_comptime_args[0..ctx.func_ty_info.param_types.len], 0..) |other_arg, i| {
6706 const this_arg = ctx.args[i];
6707 const this_is_comptime = !this_arg.val.isGenericPoison();
6708 const other_is_comptime = !other_arg.val.isGenericPoison();
6709 const this_is_anytype = this_arg.is_anytype;
6710 const other_is_anytype = other_func.isAnytypeParam(ctx.module, @intCast(u32, i));
6711
6712 if (other_is_anytype != this_is_anytype) return false;
6713 if (other_is_comptime != this_is_comptime) return false;
6714
6715 if (this_is_anytype) {
6716 // Both are anytype parameters.
6717 if (!this_arg.ty.eql(other_arg.ty, ctx.module)) {
6718 return false;
6719 }
6720 if (this_is_comptime) {
6721 // Both are comptime and anytype parameters with matching types.
6722 if (!this_arg.val.eql(other_arg.val, other_arg.ty, ctx.module)) {
6723 return false;
6724 }
6725 }
6726 } else if (this_is_comptime) {
6727 // Both are comptime parameters but not anytype parameters.
6728 // We assert no error is possible here because any lazy values must be resolved
6729 // before inserting into the generic function hash map.
6730 const is_eql = Value.eqlAdvanced(
6731 this_arg.val,
6732 this_arg.ty,
6733 other_arg.val,
6734 other_arg.ty,
6735 ctx.module,
6736 null,
6737 ) catch unreachable;
6738 if (!is_eql) {
6739 return false;
6740 }
6741 }
6742 }
6743 return true;
6744 }
6745
6746 /// The implementation of the hash is in semantic analysis of function calls, so
6747 /// that any errors when computing the hash can be properly reported.
6748 pub fn hash(ctx: @This(), adapted_key: void) u64 {
6749 _ = adapted_key;
6750 return ctx.precomputed_hash;
6751 }
6752};
6753
6754fn analyzeCall(6682fn analyzeCall(
6755 sema: *Sema,6683 sema: *Sema,
6756 block: *Block,6684 block: *Block,
...@@ -7480,11 +7408,12 @@ fn instantiateGenericCall(...@@ -7480,11 +7408,12 @@ fn instantiateGenericCall(
7480 const ip = &mod.intern_pool;7408 const ip = &mod.intern_pool;
74817409
7482 const func_val = try sema.resolveConstValue(block, func_src, func, "generic function being called must be comptime-known");7410 const func_val = try sema.resolveConstValue(block, func_src, func, "generic function being called must be comptime-known");
7483 const module_fn = mod.funcPtr(switch (ip.indexToKey(func_val.toIntern())) {7411 const module_fn_index = switch (ip.indexToKey(func_val.toIntern())) {
7484 .func => |function| function.index,7412 .func => |function| function.index,
7485 .ptr => |ptr| mod.declPtr(ptr.addr.decl).val.getFunctionIndex(mod).unwrap().?,7413 .ptr => |ptr| mod.declPtr(ptr.addr.decl).val.getFunctionIndex(mod).unwrap().?,
7486 else => unreachable,7414 else => unreachable,
7487 });7415 };
7416 const module_fn = mod.funcPtr(module_fn_index);
7488 // Check the Module's generic function map with an adapted context, so that we7417 // Check the Module's generic function map with an adapted context, so that we
7489 // can match against `uncasted_args` rather than doing the work below to create a7418 // can match against `uncasted_args` rather than doing the work below to create a
7490 // generic Scope only to junk it if it matches an existing instantiation.7419 // generic Scope only to junk it if it matches an existing instantiation.
...@@ -7495,32 +7424,24 @@ fn instantiateGenericCall(...@@ -7495,32 +7424,24 @@ fn instantiateGenericCall(
7495 const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst);7424 const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst);
7496 const zir_tags = fn_zir.instructions.items(.tag);7425 const zir_tags = fn_zir.instructions.items(.tag);
74977426
7498 // This hash must match `Module.MonomorphedFuncsContext.hash`.7427 const generic_args = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len);
7499 // For parameters explicitly marked comptime and simple parameter type expressions,7428 const callee_index = callee: {
7500 // we know whether a parameter is elided from a monomorphed function, and can7429 var arg_i: usize = 0;
7501 // use it in the hash here. However, for parameter type expressions that are not7430 var generic_arg_i: u32 = 0;
7502 // explicitly marked comptime and rely on previous parameter comptime values, we7431 var known_unique = false;
7503 // don't find out until after generating a monomorphed function whether the parameter
7504 // type ended up being a "must-be-comptime-known" type.
7505 var hasher = std.hash.Wyhash.init(0);
7506 std.hash.autoHash(&hasher, module_fn.owner_decl);
7507
7508 const generic_args = try sema.arena.alloc(GenericCallAdapter.Arg, func_ty_info.param_types.len);
7509 {
7510 var i: usize = 0;
7511 for (fn_info.param_body) |inst| {7432 for (fn_info.param_body) |inst| {
7512 var is_comptime = false;7433 var is_comptime = false;
7513 var is_anytype = false;7434 var is_anytype = false;
7514 switch (zir_tags[inst]) {7435 switch (zir_tags[inst]) {
7515 .param => {7436 .param => {
7516 is_comptime = func_ty_info.paramIsComptime(@intCast(u5, i));7437 is_comptime = func_ty_info.paramIsComptime(@intCast(u5, arg_i));
7517 },7438 },
7518 .param_comptime => {7439 .param_comptime => {
7519 is_comptime = true;7440 is_comptime = true;
7520 },7441 },
7521 .param_anytype => {7442 .param_anytype => {
7522 is_anytype = true;7443 is_anytype = true;
7523 is_comptime = func_ty_info.paramIsComptime(@intCast(u5, i));7444 is_comptime = func_ty_info.paramIsComptime(@intCast(u5, arg_i));
7524 },7445 },
7525 .param_anytype_comptime => {7446 .param_anytype_comptime => {
7526 is_anytype = true;7447 is_anytype = true;
...@@ -7529,7 +7450,15 @@ fn instantiateGenericCall(...@@ -7529,7 +7450,15 @@ fn instantiateGenericCall(
7529 else => continue,7450 else => continue,
7530 }7451 }
75317452
7532 const arg_ty = sema.typeOf(uncasted_args[i]);7453 defer arg_i += 1;
7454 if (known_unique) {
7455 if (is_comptime or is_anytype) {
7456 generic_arg_i += 1;
7457 }
7458 continue;
7459 }
7460
7461 const arg_ty = sema.typeOf(uncasted_args[arg_i]);
7533 if (is_comptime or is_anytype) {7462 if (is_comptime or is_anytype) {
7534 // Tuple default values are a part of the type and need to be7463 // Tuple default values are a part of the type and need to be
7535 // resolved to hash the type.7464 // resolved to hash the type.
...@@ -7537,69 +7466,72 @@ fn instantiateGenericCall(...@@ -7537,69 +7466,72 @@ fn instantiateGenericCall(
7537 }7466 }
75387467
7539 if (is_comptime) {7468 if (is_comptime) {
7540 const arg_val = sema.analyzeGenericCallArgVal(block, .unneeded, uncasted_args[i]) catch |err| switch (err) {7469 const arg_val = sema.analyzeGenericCallArgVal(block, .unneeded, uncasted_args[arg_i]) catch |err| switch (err) {
7541 error.NeededSourceLocation => {7470 error.NeededSourceLocation => {
7542 const decl = sema.mod.declPtr(block.src_decl);7471 const decl = sema.mod.declPtr(block.src_decl);
7543 const arg_src = mod.argSrc(call_src.node_offset.x, decl, i, bound_arg_src);7472 const arg_src = mod.argSrc(call_src.node_offset.x, decl, arg_i, bound_arg_src);
7544 _ = try sema.analyzeGenericCallArgVal(block, arg_src, uncasted_args[i]);7473 _ = try sema.analyzeGenericCallArgVal(block, arg_src, uncasted_args[arg_i]);
7545 unreachable;7474 unreachable;
7546 },7475 },
7547 else => |e| return e,7476 else => |e| return e,
7548 };7477 };
7549 arg_val.hashUncoerced(arg_ty, &hasher, mod);7478
7550 if (is_anytype) {7479 if (is_anytype) {
7551 std.hash.autoHash(&hasher, arg_ty.toIntern());7480 generic_args[generic_arg_i] = arg_val.toIntern();
7552 generic_args[i] = .{
7553 .ty = arg_ty,
7554 .val = arg_val,
7555 .is_anytype = true,
7556 };
7557 } else {7481 } else {
7558 generic_args[i] = .{7482 const final_arg_ty = mod.monomorphed_funcs.getAdapted(
7559 .ty = arg_ty,7483 Module.MonomorphedFuncAdaptedKey{
7560 .val = arg_val,7484 .func = module_fn_index,
7561 .is_anytype = false,7485 .args = generic_args[0..generic_arg_i],
7486 },
7487 Module.MonomorphedFuncsAdaptedContext{ .mod = mod },
7488 ) orelse {
7489 known_unique = true;
7490 generic_arg_i += 1;
7491 continue;
7492 };
7493 const casted_arg = sema.coerce(block, final_arg_ty.toType(), uncasted_args[arg_i], .unneeded) catch |err| switch (err) {
7494 error.NeededSourceLocation => {
7495 const decl = sema.mod.declPtr(block.src_decl);
7496 const arg_src = mod.argSrc(call_src.node_offset.x, decl, arg_i, bound_arg_src);
7497 _ = try sema.coerce(block, final_arg_ty.toType(), uncasted_args[arg_i], arg_src);
7498 unreachable;
7499 },
7500 else => |e| return e,
7562 };7501 };
7502 const casted_arg_val = sema.analyzeGenericCallArgVal(block, .unneeded, casted_arg) catch |err| switch (err) {
7503 error.NeededSourceLocation => {
7504 const decl = sema.mod.declPtr(block.src_decl);
7505 const arg_src = mod.argSrc(call_src.node_offset.x, decl, arg_i, bound_arg_src);
7506 _ = try sema.analyzeGenericCallArgVal(block, arg_src, casted_arg);
7507 unreachable;
7508 },
7509 else => |e| return e,
7510 };
7511 generic_args[generic_arg_i] = casted_arg_val.toIntern();
7563 }7512 }
7513 generic_arg_i += 1;
7564 } else if (is_anytype) {7514 } else if (is_anytype) {
7565 std.hash.autoHash(&hasher, arg_ty.toIntern());7515 generic_args[generic_arg_i] = arg_ty.toIntern();
7566 generic_args[i] = .{7516 generic_arg_i += 1;
7567 .ty = arg_ty,
7568 .val = Value.generic_poison,
7569 .is_anytype = true,
7570 };
7571 } else {
7572 generic_args[i] = .{
7573 .ty = arg_ty,
7574 .val = Value.generic_poison,
7575 .is_anytype = false,
7576 };
7577 }7517 }
7578
7579 i += 1;
7580 }7518 }
7581 }
75827519
7583 const precomputed_hash = hasher.final();7520 if (!known_unique) {
7521 if (mod.monomorphed_funcs.getAdapted(
7522 Module.MonomorphedFuncAdaptedKey{
7523 .func = module_fn_index,
7524 .args = generic_args[0..generic_arg_i],
7525 },
7526 Module.MonomorphedFuncsAdaptedContext{ .mod = mod },
7527 )) |callee_func| break :callee mod.intern_pool.indexToKey(callee_func).func.index;
7528 }
75847529
7585 const adapter: GenericCallAdapter = .{
7586 .generic_fn = module_fn,
7587 .precomputed_hash = precomputed_hash,
7588 .func_ty_info = func_ty_info,
7589 .args = generic_args,
7590 .module = mod,
7591 };
7592 const gop = try mod.monomorphed_funcs.getOrPutContextAdapted(gpa, {}, adapter, .{ .mod = mod });
7593 const callee_index = if (!gop.found_existing) callee: {
7594 const new_module_func_index = try mod.createFunc(undefined);7530 const new_module_func_index = try mod.createFunc(undefined);
7595 const new_module_func = mod.funcPtr(new_module_func_index);7531 const new_module_func = mod.funcPtr(new_module_func_index);
75967532
7597 // This ensures that we can operate on the hash map before the Module.Fn
7598 // struct is fully initialized.
7599 new_module_func.hash = precomputed_hash;
7600 new_module_func.generic_owner_decl = module_fn.owner_decl.toOptional();7533 new_module_func.generic_owner_decl = module_fn.owner_decl.toOptional();
7601 new_module_func.comptime_args = null;7534 new_module_func.comptime_args = null;
7602 gop.key_ptr.* = new_module_func_index;
76037535
7604 try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);7536 try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);
76057537
...@@ -7641,7 +7573,8 @@ fn instantiateGenericCall(...@@ -7641,7 +7573,8 @@ fn instantiateGenericCall(
7641 new_decl,7573 new_decl,
7642 new_decl_index,7574 new_decl_index,
7643 uncasted_args,7575 uncasted_args,
7644 module_fn,7576 generic_arg_i,
7577 module_fn_index,
7645 new_module_func_index,7578 new_module_func_index,
7646 namespace_index,7579 namespace_index,
7647 func_ty_info,7580 func_ty_info,
...@@ -7657,12 +7590,10 @@ fn instantiateGenericCall(...@@ -7657,12 +7590,10 @@ fn instantiateGenericCall(
7657 }7590 }
7658 assert(namespace.anon_decls.orderedRemove(new_decl_index));7591 assert(namespace.anon_decls.orderedRemove(new_decl_index));
7659 mod.destroyDecl(new_decl_index);7592 mod.destroyDecl(new_decl_index);
7660 assert(mod.monomorphed_funcs.removeContext(new_module_func_index, .{ .mod = mod }));
7661 mod.destroyFunc(new_module_func_index);7593 mod.destroyFunc(new_module_func_index);
7662 return err;7594 return err;
7663 },7595 },
7664 else => {7596 else => {
7665 assert(mod.monomorphed_funcs.removeContext(new_module_func_index, .{ .mod = mod }));
7666 // TODO look up the compile error that happened here and attach a note to it7597 // TODO look up the compile error that happened here and attach a note to it
7667 // pointing here, at the generic instantiation callsite.7598 // pointing here, at the generic instantiation callsite.
7668 if (sema.owner_func) |owner_func| {7599 if (sema.owner_func) |owner_func| {
...@@ -7675,9 +7606,8 @@ fn instantiateGenericCall(...@@ -7675,9 +7606,8 @@ fn instantiateGenericCall(
7675 };7606 };
76767607
7677 break :callee new_func;7608 break :callee new_func;
7678 } else gop.key_ptr.*;7609 };
7679 const callee = mod.funcPtr(callee_index);7610 const callee = mod.funcPtr(callee_index);
7680
7681 callee.branch_quota = @max(callee.branch_quota, sema.branch_quota);7611 callee.branch_quota = @max(callee.branch_quota, sema.branch_quota);
76827612
7683 const callee_inst = try sema.analyzeDeclVal(block, func_src, callee.owner_decl);7613 const callee_inst = try sema.analyzeDeclVal(block, func_src, callee.owner_decl);
...@@ -7752,7 +7682,7 @@ fn instantiateGenericCall(...@@ -7752,7 +7682,7 @@ fn instantiateGenericCall(
7752 if (call_tag == .call_always_tail) {7682 if (call_tag == .call_always_tail) {
7753 return sema.handleTailCall(block, call_src, func_ty, result);7683 return sema.handleTailCall(block, call_src, func_ty, result);
7754 }7684 }
7755 if (new_fn_info.return_type == .noreturn_type) {7685 if (func_ty.fnReturnType(mod).isNoReturn(mod)) {
7756 _ = try block.addNoOp(.unreach);7686 _ = try block.addNoOp(.unreach);
7757 return Air.Inst.Ref.unreachable_value;7687 return Air.Inst.Ref.unreachable_value;
7758 }7688 }
...@@ -7766,7 +7696,8 @@ fn resolveGenericInstantiationType(...@@ -7766,7 +7696,8 @@ fn resolveGenericInstantiationType(
7766 new_decl: *Decl,7696 new_decl: *Decl,
7767 new_decl_index: Decl.Index,7697 new_decl_index: Decl.Index,
7768 uncasted_args: []const Air.Inst.Ref,7698 uncasted_args: []const Air.Inst.Ref,
7769 module_fn: *Module.Fn,7699 generic_args_len: u32,
7700 module_fn_index: Module.Fn.Index,
7770 new_module_func: Module.Fn.Index,7701 new_module_func: Module.Fn.Index,
7771 namespace: Namespace.Index,7702 namespace: Namespace.Index,
7772 func_ty_info: InternPool.Key.FuncType,7703 func_ty_info: InternPool.Key.FuncType,
...@@ -7777,6 +7708,7 @@ fn resolveGenericInstantiationType(...@@ -7777,6 +7708,7 @@ fn resolveGenericInstantiationType(
7777 const gpa = sema.gpa;7708 const gpa = sema.gpa;
77787709
7779 const zir_tags = fn_zir.instructions.items(.tag);7710 const zir_tags = fn_zir.instructions.items(.tag);
7711 const module_fn = mod.funcPtr(module_fn_index);
7780 const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst);7712 const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst);
77817713
7782 // Re-run the block that creates the function, with the comptime parameters7714 // Re-run the block that creates the function, with the comptime parameters
...@@ -7893,9 +7825,15 @@ fn resolveGenericInstantiationType(...@@ -7893,9 +7825,15 @@ fn resolveGenericInstantiationType(
7893 const new_func = new_func_val.getFunctionIndex(mod).unwrap().?;7825 const new_func = new_func_val.getFunctionIndex(mod).unwrap().?;
7894 assert(new_func == new_module_func);7826 assert(new_func == new_module_func);
78957827
7828 const generic_args_index = @intCast(u32, mod.monomorphed_func_keys.items.len);
7829 const generic_args = try mod.monomorphed_func_keys.addManyAsSlice(gpa, generic_args_len);
7830 var generic_arg_i: u32 = 0;
7831 try mod.monomorphed_funcs.ensureUnusedCapacityContext(gpa, generic_args_len + 1, .{ .mod = mod });
7832
7896 arg_i = 0;7833 arg_i = 0;
7897 for (fn_info.param_body) |inst| {7834 for (fn_info.param_body) |inst| {
7898 var is_comptime = false;7835 var is_comptime = false;
7836 var is_anytype = false;
7899 switch (zir_tags[inst]) {7837 switch (zir_tags[inst]) {
7900 .param => {7838 .param => {
7901 is_comptime = func_ty_info.paramIsComptime(@intCast(u5, arg_i));7839 is_comptime = func_ty_info.paramIsComptime(@intCast(u5, arg_i));
...@@ -7904,9 +7842,11 @@ fn resolveGenericInstantiationType(...@@ -7904,9 +7842,11 @@ fn resolveGenericInstantiationType(
7904 is_comptime = true;7842 is_comptime = true;
7905 },7843 },
7906 .param_anytype => {7844 .param_anytype => {
7845 is_anytype = true;
7907 is_comptime = func_ty_info.paramIsComptime(@intCast(u5, arg_i));7846 is_comptime = func_ty_info.paramIsComptime(@intCast(u5, arg_i));
7908 },7847 },
7909 .param_anytype_comptime => {7848 .param_anytype_comptime => {
7849 is_anytype = true;
7910 is_comptime = true;7850 is_comptime = true;
7911 },7851 },
7912 else => continue,7852 else => continue,
...@@ -7924,11 +7864,24 @@ fn resolveGenericInstantiationType(...@@ -7924,11 +7864,24 @@ fn resolveGenericInstantiationType(
79247864
7925 if (is_comptime) {7865 if (is_comptime) {
7926 const arg_val = (child_sema.resolveMaybeUndefValAllowVariables(arg) catch unreachable).?;7866 const arg_val = (child_sema.resolveMaybeUndefValAllowVariables(arg) catch unreachable).?;
7867 if (!is_anytype) {
7868 if (mod.monomorphed_funcs.fetchPutAssumeCapacityContext(.{
7869 .func = module_fn_index,
7870 .args_index = generic_args_index,
7871 .args_len = generic_arg_i,
7872 }, arg_ty.toIntern(), .{ .mod = mod })) |kv| assert(kv.value == arg_ty.toIntern());
7873 }
7874 generic_args[generic_arg_i] = arg_val.toIntern();
7875 generic_arg_i += 1;
7927 child_sema.comptime_args[arg_i] = .{7876 child_sema.comptime_args[arg_i] = .{
7928 .ty = arg_ty,7877 .ty = arg_ty,
7929 .val = (try arg_val.intern(arg_ty, mod)).toValue(),7878 .val = (try arg_val.intern(arg_ty, mod)).toValue(),
7930 };7879 };
7931 } else {7880 } else {
7881 if (is_anytype) {
7882 generic_args[generic_arg_i] = arg_ty.toIntern();
7883 generic_arg_i += 1;
7884 }
7932 child_sema.comptime_args[arg_i] = .{7885 child_sema.comptime_args[arg_i] = .{
7933 .ty = arg_ty,7886 .ty = arg_ty,
7934 .val = Value.generic_poison,7887 .val = Value.generic_poison,
...@@ -7963,6 +7916,12 @@ fn resolveGenericInstantiationType(...@@ -7963,6 +7916,12 @@ fn resolveGenericInstantiationType(
7963 new_decl.owns_tv = true;7916 new_decl.owns_tv = true;
7964 new_decl.analysis = .complete;7917 new_decl.analysis = .complete;
79657918
7919 mod.monomorphed_funcs.putAssumeCapacityNoClobberContext(.{
7920 .func = module_fn_index,
7921 .args_index = generic_args_index,
7922 .args_len = generic_arg_i,
7923 }, new_decl.val.toIntern(), .{ .mod = mod });
7924
7966 // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field7925 // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field
7967 // will be populated, ensuring it will have `analyzeBody` called with the ZIR7926 // will be populated, ensuring it will have `analyzeBody` called with the ZIR
7968 // parameters mapped appropriately.7927 // parameters mapped appropriately.
src/value.zig-71
...@@ -1691,77 +1691,6 @@ pub const Value = struct {...@@ -1691,77 +1691,6 @@ pub const Value = struct {
1691 return (try orderAdvanced(a, b, mod, opt_sema)).compare(.eq);1691 return (try orderAdvanced(a, b, mod, opt_sema)).compare(.eq);
1692 }1692 }
16931693
1694 /// This is a more conservative hash function that produces equal hashes for values
1695 /// that can coerce into each other.
1696 /// This function is used by hash maps and so treats floating-point NaNs as equal
1697 /// to each other, and not equal to other floating-point values.
1698 pub fn hashUncoerced(val: Value, ty: Type, hasher: *std.hash.Wyhash, mod: *Module) void {
1699 if (val.isUndef(mod)) return;
1700 // The value is runtime-known and shouldn't affect the hash.
1701 if (val.isRuntimeValue(mod)) return;
1702
1703 if (val.ip_index != .none) {
1704 // The InternPool data structure hashes based on Key to make interned objects
1705 // unique. An Index can be treated simply as u32 value for the
1706 // purpose of Type/Value hashing and equality.
1707 std.hash.autoHash(hasher, val.toIntern());
1708 return;
1709 }
1710
1711 switch (ty.zigTypeTag(mod)) {
1712 .Opaque => unreachable, // Cannot hash opaque types
1713 .Void,
1714 .NoReturn,
1715 .Undefined,
1716 .Null,
1717 .Struct, // It sure would be nice to do something clever with structs.
1718 => |zig_type_tag| std.hash.autoHash(hasher, zig_type_tag),
1719 .Pointer => {
1720 assert(ty.isSlice(mod));
1721 const slice = val.castTag(.slice).?.data;
1722 const ptr_ty = ty.slicePtrFieldType(mod);
1723 slice.ptr.hashUncoerced(ptr_ty, hasher, mod);
1724 },
1725 .Type,
1726 .Float,
1727 .ComptimeFloat,
1728 .Bool,
1729 .Int,
1730 .ComptimeInt,
1731 .Fn,
1732 .Optional,
1733 .ErrorSet,
1734 .ErrorUnion,
1735 .Enum,
1736 .EnumLiteral,
1737 => unreachable, // handled above with the ip_index check
1738 .Array, .Vector => {
1739 const len = ty.arrayLen(mod);
1740 const elem_ty = ty.childType(mod);
1741 var index: usize = 0;
1742 while (index < len) : (index += 1) {
1743 const elem_val = val.elemValue(mod, index) catch |err| switch (err) {
1744 // Will be solved when arrays and vectors get migrated to the intern pool.
1745 error.OutOfMemory => @panic("OOM"),
1746 };
1747 elem_val.hashUncoerced(elem_ty, hasher, mod);
1748 }
1749 },
1750 .Union => {
1751 hasher.update(val.tagName(mod));
1752 switch (mod.intern_pool.indexToKey(val.toIntern())) {
1753 .un => |un| {
1754 const active_field_ty = ty.unionFieldType(un.tag.toValue(), mod);
1755 un.val.toValue().hashUncoerced(active_field_ty, hasher, mod);
1756 },
1757 else => std.hash.autoHash(hasher, std.builtin.TypeId.Void),
1758 }
1759 },
1760 .Frame => @panic("TODO implement hashing frame values"),
1761 .AnyFrame => @panic("TODO implement hashing anyframe values"),
1762 }
1763 }
1764
1765 pub fn isComptimeMutablePtr(val: Value, mod: *Module) bool {1694 pub fn isComptimeMutablePtr(val: Value, mod: *Module) bool {
1766 return switch (mod.intern_pool.indexToKey(val.toIntern())) {1695 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1767 .ptr => |ptr| switch (ptr.addr) {1696 .ptr => |ptr| switch (ptr.addr) {