authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-08-21 05:28:24+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-08-21 05:28:24+01:00
log61919fe63d1eb7134a3c85fe0a4cf279744de3e9
treead8a93f55ed7c4cff101789aedea0b5cc930d0f6
parent16d74809d44d6bb8db1a32923ef8db43d956e24d
parenta99ad52b362d966f772f29ad14ae1714218bc033
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #21135 from mlugg/incremental

incremental: more progress

18 files changed, 297 insertions(+), 132 deletions(-)

lib/compiler/aro/aro/Builtins/Builtin.zig+1-1
......@@ -5165,7 +5165,7 @@ const dafsa = [_]Node{
51655165 .{ .char = 'e', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 4913 },
51665166};
51675167pub const data = blk: {
5168 @setEvalBranchQuota(3986);
5168 @setEvalBranchQuota(30_000);
51695169 break :blk [_]@This(){
51705170 // _Block_object_assign
51715171 .{ .tag = @enumFromInt(0), .properties = .{ .param_str = "vv*vC*iC", .header = .blocks, .attributes = .{ .lib_function_without_prefix = true } } },
lib/compiler/aro/aro/Parser.zig+2
......@@ -4802,6 +4802,7 @@ const CallExpr = union(enum) {
48024802 }
48034803
48044804 fn shouldPromoteVarArg(self: CallExpr, arg_idx: u32) bool {
4805 @setEvalBranchQuota(2000);
48054806 return switch (self) {
48064807 .standard => true,
48074808 .builtin => |builtin| switch (builtin.tag) {
......@@ -4902,6 +4903,7 @@ const CallExpr = union(enum) {
49024903 }
49034904
49044905 fn returnType(self: CallExpr, p: *Parser, callable_ty: Type) Type {
4906 @setEvalBranchQuota(6000);
49054907 return switch (self) {
49064908 .standard => callable_ty.returnType(),
49074909 .builtin => |builtin| switch (builtin.tag) {
lib/std/crypto/aes/soft.zig+2
......@@ -629,6 +629,8 @@ fn generateSbox(invert: bool) [256]u8 {
629629
630630// Generate lookup tables.
631631fn generateTable(invert: bool) [4][256]u32 {
632 @setEvalBranchQuota(50000);
633
632634 var table: [4][256]u32 = undefined;
633635
634636 for (generateSbox(invert), 0..) |value, index| {
lib/std/crypto/blake2.zig+2-2
......@@ -786,7 +786,7 @@ test "blake2b384 streaming" {
786786
787787test "comptime blake2b384" {
788788 comptime {
789 @setEvalBranchQuota(10000);
789 @setEvalBranchQuota(20000);
790790 var block = [_]u8{0} ** Blake2b384.block_length;
791791 var out: [Blake2b384.digest_length]u8 = undefined;
792792
......@@ -878,7 +878,7 @@ test "blake2b512 keyed" {
878878
879879test "comptime blake2b512" {
880880 comptime {
881 @setEvalBranchQuota(10000);
881 @setEvalBranchQuota(12000);
882882 var block = [_]u8{0} ** Blake2b512.block_length;
883883 var out: [Blake2b512.digest_length]u8 = undefined;
884884
lib/std/crypto/pcurves/p384.zig+1-1
......@@ -393,7 +393,7 @@ pub const P384 = struct {
393393 }
394394
395395 const basePointPc = pc: {
396 @setEvalBranchQuota(50000);
396 @setEvalBranchQuota(70000);
397397 break :pc precompute(P384.basePoint, 15);
398398 };
399399
lib/std/enums.zig+1-1
......@@ -6,7 +6,7 @@ const testing = std.testing;
66const EnumField = std.builtin.Type.EnumField;
77
88/// Increment this value when adding APIs that add single backwards branches.
9const eval_branch_quota_cushion = 5;
9const eval_branch_quota_cushion = 10;
1010
1111/// Returns a struct with a field matching each unique named enum element.
1212/// If the enum is extern and has multiple names for the same value, only
lib/std/hash/xxhash.zig+1-1
......@@ -890,7 +890,7 @@ test "xxhash32 smhasher" {
890890 }
891891 };
892892 try Test.do();
893 @setEvalBranchQuota(75000);
893 @setEvalBranchQuota(85000);
894894 comptime try Test.do();
895895}
896896
lib/std/math.zig+8-16
......@@ -749,31 +749,23 @@ test rotl {
749749 try testing.expect(rotl(@Vector(1, u32), @Vector(1, u32){1 << 31}, @as(isize, -1))[0] == @as(u32, 1) << 30);
750750}
751751
752/// Returns an unsigned int type that can hold the number of bits in T
753/// - 1. Suitable for 0-based bit indices of T.
752/// Returns an unsigned int type that can hold the number of bits in T - 1.
753/// Suitable for 0-based bit indices of T.
754754pub fn Log2Int(comptime T: type) type {
755755 // comptime ceil log2
756756 if (T == comptime_int) return comptime_int;
757 comptime var count = 0;
758 comptime var s = @typeInfo(T).Int.bits - 1;
759 inline while (s != 0) : (s >>= 1) {
760 count += 1;
761 }
762
763 return std.meta.Int(.unsigned, count);
757 const bits: u16 = @typeInfo(T).Int.bits;
758 const log2_bits = 16 - @clz(bits - 1);
759 return std.meta.Int(.unsigned, log2_bits);
764760}
765761
766762/// Returns an unsigned int type that can hold the number of bits in T.
767763pub fn Log2IntCeil(comptime T: type) type {
768764 // comptime ceil log2
769765 if (T == comptime_int) return comptime_int;
770 comptime var count = 0;
771 comptime var s = @typeInfo(T).Int.bits;
772 inline while (s != 0) : (s >>= 1) {
773 count += 1;
774 }
775
776 return std.meta.Int(.unsigned, count);
766 const bits: u16 = @typeInfo(T).Int.bits;
767 const log2_bits = 16 - @clz(bits);
768 return std.meta.Int(.unsigned, log2_bits);
777769}
778770
779771/// Returns the smallest integer type that can hold both from and to.
lib/std/math/hypot.zig+1
......@@ -114,6 +114,7 @@ test "hypot.precise" {
114114}
115115
116116test "hypot.special" {
117 @setEvalBranchQuota(2000);
117118 inline for (.{ f16, f32, f64, f128 }) |T| {
118119 try expect(math.isNan(hypot(nan(T), 0.0)));
119120 try expect(math.isNan(hypot(0.0, nan(T))));
lib/std/math/nextafter.zig+1-1
......@@ -144,7 +144,7 @@ test "int" {
144144}
145145
146146test "float" {
147 @setEvalBranchQuota(3000);
147 @setEvalBranchQuota(4000);
148148
149149 // normal -> normal
150150 try expect(nextAfter(f16, 0x1.234p0, 2.0) == 0x1.238p0);
lib/std/unicode.zig+1
......@@ -535,6 +535,7 @@ fn testUtf16CountCodepoints() !void {
535535}
536536
537537test "utf16 count codepoints" {
538 @setEvalBranchQuota(2000);
538539 try testUtf16CountCodepoints();
539540 try comptime testUtf16CountCodepoints();
540541}
lib/std/zig/AstGen.zig+107-30
......@@ -66,6 +66,10 @@ scratch: std.ArrayListUnmanaged(u32) = .{},
6666/// of ZIR.
6767/// The key is the ref operand; the value is the ref instruction.
6868ref_table: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
69/// Any information which should trigger invalidation of incremental compilation
70/// data should be used to update this hasher. The result is the final source
71/// hash of the enclosing declaration/etc.
72src_hasher: std.zig.SrcHasher,
6973
7074const InnerError = error{ OutOfMemory, AnalysisFail };
7175
......@@ -137,6 +141,7 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
137141 .arena = arena.allocator(),
138142 .tree = &tree,
139143 .nodes_need_rl = &nodes_need_rl,
144 .src_hasher = undefined, // `structDeclInner` for the root struct will set this
140145 };
141146 defer astgen.deinit(gpa);
142147
......@@ -1422,6 +1427,8 @@ fn fnProtoExpr(
14221427 .is_extern = false,
14231428 .is_noinline = false,
14241429 .noalias_bits = noalias_bits,
1430
1431 .proto_hash = undefined, // ignored for `body_gz == null`
14251432 });
14261433
14271434 _ = try block_scope.addBreak(.break_inline, block_inst, result);
......@@ -4007,6 +4014,13 @@ fn fnDecl(
40074014 const tree = astgen.tree;
40084015 const token_tags = tree.tokens.items(.tag);
40094016
4017 const old_hasher = astgen.src_hasher;
4018 defer astgen.src_hasher = old_hasher;
4019 astgen.src_hasher = std.zig.SrcHasher.init(.{});
4020 // We don't add the full source yet, because we also need the prototype hash!
4021 // The source slice is added towards the *end* of this function.
4022 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
4023
40104024 // missing function name already happened in scanDecls()
40114025 const fn_name_token = fn_proto.name_token orelse return error.AnalysisFail;
40124026
......@@ -4300,11 +4314,21 @@ fn fnDecl(
43004314 .is_extern = true,
43014315 .is_noinline = is_noinline,
43024316 .noalias_bits = noalias_bits,
4317 .proto_hash = undefined, // ignored for `body_gz == null`
43034318 });
43044319 } else func: {
43054320 // as a scope, fn_gz encloses ret_gz, but for instruction list, fn_gz stacks on ret_gz
43064321 fn_gz.instructions_top = ret_gz.instructions.items.len;
43074322
4323 // Construct the prototype hash.
4324 // Leave `astgen.src_hasher` unmodified; this will be used for hashing
4325 // the *whole* function declaration, including its body.
4326 var proto_hasher = astgen.src_hasher;
4327 const proto_node = tree.nodes.items(.data)[decl_node].lhs;
4328 proto_hasher.update(tree.getNodeSource(proto_node));
4329 var proto_hash: std.zig.SrcHash = undefined;
4330 proto_hasher.final(&proto_hash);
4331
43084332 const prev_fn_block = astgen.fn_block;
43094333 const prev_fn_ret_ty = astgen.fn_ret_ty;
43104334 defer {
......@@ -4362,16 +4386,22 @@ fn fnDecl(
43624386 .is_extern = false,
43634387 .is_noinline = is_noinline,
43644388 .noalias_bits = noalias_bits,
4389 .proto_hash = proto_hash,
43654390 });
43664391 };
43674392
4393 // *Now* we can incorporate the full source code into the hasher.
4394 astgen.src_hasher.update(tree.getNodeSource(decl_node));
4395
43684396 // We add this at the end so that its instruction index marks the end range
43694397 // of the top level declaration. addFunc already unstacked fn_gz and ret_gz.
43704398 _ = try decl_gz.addBreak(.break_inline, decl_inst, func_inst);
43714399
4400 var hash: std.zig.SrcHash = undefined;
4401 astgen.src_hasher.final(&hash);
43724402 try setDeclaration(
43734403 decl_inst,
4374 std.zig.hashSrc(tree.getNodeSource(decl_node)),
4404 hash,
43754405 .{ .named = fn_name_token },
43764406 decl_gz.decl_line,
43774407 is_pub,
......@@ -4395,6 +4425,12 @@ fn globalVarDecl(
43954425 const tree = astgen.tree;
43964426 const token_tags = tree.tokens.items(.tag);
43974427
4428 const old_hasher = astgen.src_hasher;
4429 defer astgen.src_hasher = old_hasher;
4430 astgen.src_hasher = std.zig.SrcHasher.init(.{});
4431 astgen.src_hasher.update(tree.getNodeSource(node));
4432 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
4433
43984434 const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
43994435 // We do this at the beginning so that the instruction index marks the range start
44004436 // of the top level declaration.
......@@ -4534,9 +4570,11 @@ fn globalVarDecl(
45344570 _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, addrspace_inst, node);
45354571 }
45364572
4573 var hash: std.zig.SrcHash = undefined;
4574 astgen.src_hasher.final(&hash);
45374575 try setDeclaration(
45384576 decl_inst,
4539 std.zig.hashSrc(tree.getNodeSource(node)),
4577 hash,
45404578 .{ .named = name_token },
45414579 block_scope.decl_line,
45424580 is_pub,
......@@ -4562,6 +4600,12 @@ fn comptimeDecl(
45624600 const node_datas = tree.nodes.items(.data);
45634601 const body_node = node_datas[node].lhs;
45644602
4603 const old_hasher = astgen.src_hasher;
4604 defer astgen.src_hasher = old_hasher;
4605 astgen.src_hasher = std.zig.SrcHasher.init(.{});
4606 astgen.src_hasher.update(tree.getNodeSource(node));
4607 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
4608
45654609 // Up top so the ZIR instruction index marks the start range of this
45664610 // top-level declaration.
45674611 const decl_inst = try gz.makeDeclaration(node);
......@@ -4584,9 +4628,11 @@ fn comptimeDecl(
45844628 _ = try decl_block.addBreak(.break_inline, decl_inst, .void_value);
45854629 }
45864630
4631 var hash: std.zig.SrcHash = undefined;
4632 astgen.src_hasher.final(&hash);
45874633 try setDeclaration(
45884634 decl_inst,
4589 std.zig.hashSrc(tree.getNodeSource(node)),
4635 hash,
45904636 .@"comptime",
45914637 decl_block.decl_line,
45924638 false,
......@@ -4607,6 +4653,12 @@ fn usingnamespaceDecl(
46074653 const tree = astgen.tree;
46084654 const node_datas = tree.nodes.items(.data);
46094655
4656 const old_hasher = astgen.src_hasher;
4657 defer astgen.src_hasher = old_hasher;
4658 astgen.src_hasher = std.zig.SrcHasher.init(.{});
4659 astgen.src_hasher.update(tree.getNodeSource(node));
4660 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
4661
46104662 const type_expr = node_datas[node].lhs;
46114663 const is_pub = blk: {
46124664 const main_tokens = tree.nodes.items(.main_token);
......@@ -4634,9 +4686,11 @@ fn usingnamespaceDecl(
46344686 const namespace_inst = try typeExpr(&decl_block, &decl_block.base, type_expr);
46354687 _ = try decl_block.addBreak(.break_inline, decl_inst, namespace_inst);
46364688
4689 var hash: std.zig.SrcHash = undefined;
4690 astgen.src_hasher.final(&hash);
46374691 try setDeclaration(
46384692 decl_inst,
4639 std.zig.hashSrc(tree.getNodeSource(node)),
4693 hash,
46404694 .@"usingnamespace",
46414695 decl_block.decl_line,
46424696 is_pub,
......@@ -4658,6 +4712,12 @@ fn testDecl(
46584712 const node_datas = tree.nodes.items(.data);
46594713 const body_node = node_datas[node].rhs;
46604714
4715 const old_hasher = astgen.src_hasher;
4716 defer astgen.src_hasher = old_hasher;
4717 astgen.src_hasher = std.zig.SrcHasher.init(.{});
4718 astgen.src_hasher.update(tree.getNodeSource(node));
4719 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
4720
46614721 // Up top so the ZIR instruction index marks the start range of this
46624722 // top-level declaration.
46634723 const decl_inst = try gz.makeDeclaration(node);
......@@ -4819,13 +4879,18 @@ fn testDecl(
48194879 .is_extern = false,
48204880 .is_noinline = false,
48214881 .noalias_bits = 0,
4882
4883 // Tests don't have a prototype that needs hashing
4884 .proto_hash = .{0} ** 16,
48224885 });
48234886
48244887 _ = try decl_block.addBreak(.break_inline, decl_inst, func_inst);
48254888
4889 var hash: std.zig.SrcHash = undefined;
4890 astgen.src_hasher.final(&hash);
48264891 try setDeclaration(
48274892 decl_inst,
4828 std.zig.hashSrc(tree.getNodeSource(node)),
4893 hash,
48294894 test_name,
48304895 decl_block.decl_line,
48314896 false,
......@@ -4983,10 +5048,12 @@ fn structDeclInner(
49835048 }
49845049 };
49855050
4986 var fields_hasher = std.zig.SrcHasher.init(.{});
4987 fields_hasher.update(@tagName(layout));
5051 const old_hasher = astgen.src_hasher;
5052 defer astgen.src_hasher = old_hasher;
5053 astgen.src_hasher = std.zig.SrcHasher.init(.{});
5054 astgen.src_hasher.update(@tagName(layout));
49885055 if (backing_int_node != 0) {
4989 fields_hasher.update(tree.getNodeSource(backing_int_node));
5056 astgen.src_hasher.update(tree.getNodeSource(backing_int_node));
49905057 }
49915058
49925059 var sfba = std.heap.stackFallback(256, astgen.arena);
......@@ -5009,7 +5076,7 @@ fn structDeclInner(
50095076 .field => |field| field,
50105077 };
50115078
5012 fields_hasher.update(tree.getNodeSource(member_node));
5079 astgen.src_hasher.update(tree.getNodeSource(member_node));
50135080
50145081 if (!is_tuple) {
50155082 const field_name = try astgen.identAsString(member.ast.main_token);
......@@ -5139,7 +5206,7 @@ fn structDeclInner(
51395206 }
51405207
51415208 var fields_hash: std.zig.SrcHash = undefined;
5142 fields_hasher.final(&fields_hash);
5209 astgen.src_hasher.final(&fields_hash);
51435210
51445211 try gz.setStruct(decl_inst, .{
51455212 .src_node = node,
......@@ -5240,11 +5307,13 @@ fn unionDeclInner(
52405307 var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
52415308 defer wip_members.deinit();
52425309
5243 var fields_hasher = std.zig.SrcHasher.init(.{});
5244 fields_hasher.update(@tagName(layout));
5245 fields_hasher.update(&.{@intFromBool(auto_enum_tok != null)});
5310 const old_hasher = astgen.src_hasher;
5311 defer astgen.src_hasher = old_hasher;
5312 astgen.src_hasher = std.zig.SrcHasher.init(.{});
5313 astgen.src_hasher.update(@tagName(layout));
5314 astgen.src_hasher.update(&.{@intFromBool(auto_enum_tok != null)});
52465315 if (arg_node != 0) {
5247 fields_hasher.update(astgen.tree.getNodeSource(arg_node));
5316 astgen.src_hasher.update(astgen.tree.getNodeSource(arg_node));
52485317 }
52495318
52505319 var sfba = std.heap.stackFallback(256, astgen.arena);
......@@ -5261,7 +5330,7 @@ fn unionDeclInner(
52615330 .decl => continue,
52625331 .field => |field| field,
52635332 };
5264 fields_hasher.update(astgen.tree.getNodeSource(member_node));
5333 astgen.src_hasher.update(astgen.tree.getNodeSource(member_node));
52655334 member.convertToNonTupleLike(astgen.tree.nodes);
52665335 if (member.ast.tuple_like) {
52675336 return astgen.failTok(member.ast.main_token, "union field missing name", .{});
......@@ -5364,7 +5433,7 @@ fn unionDeclInner(
53645433 }
53655434
53665435 var fields_hash: std.zig.SrcHash = undefined;
5367 fields_hasher.final(&fields_hash);
5436 astgen.src_hasher.final(&fields_hash);
53685437
53695438 if (!block_scope.isEmpty()) {
53705439 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
......@@ -5578,11 +5647,13 @@ fn containerDecl(
55785647 var wip_members = try WipMembers.init(gpa, &astgen.scratch, @intCast(counts.decls), @intCast(counts.total_fields), bits_per_field, max_field_size);
55795648 defer wip_members.deinit();
55805649
5581 var fields_hasher = std.zig.SrcHasher.init(.{});
5650 const old_hasher = astgen.src_hasher;
5651 defer astgen.src_hasher = old_hasher;
5652 astgen.src_hasher = std.zig.SrcHasher.init(.{});
55825653 if (container_decl.ast.arg != 0) {
5583 fields_hasher.update(tree.getNodeSource(container_decl.ast.arg));
5654 astgen.src_hasher.update(tree.getNodeSource(container_decl.ast.arg));
55845655 }
5585 fields_hasher.update(&.{@intFromBool(nonexhaustive)});
5656 astgen.src_hasher.update(&.{@intFromBool(nonexhaustive)});
55865657
55875658 var sfba = std.heap.stackFallback(256, astgen.arena);
55885659 const sfba_allocator = sfba.get();
......@@ -5596,7 +5667,7 @@ fn containerDecl(
55965667 for (container_decl.ast.members) |member_node| {
55975668 if (member_node == counts.nonexhaustive_node)
55985669 continue;
5599 fields_hasher.update(tree.getNodeSource(member_node));
5670 astgen.src_hasher.update(tree.getNodeSource(member_node));
56005671 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
56015672 .decl => continue,
56025673 .field => |field| field,
......@@ -5676,7 +5747,7 @@ fn containerDecl(
56765747 }
56775748
56785749 var fields_hash: std.zig.SrcHash = undefined;
5679 fields_hasher.final(&fields_hash);
5750 astgen.src_hasher.final(&fields_hash);
56805751
56815752 const body = block_scope.instructionsSlice();
56825753 const body_len = astgen.countBodyLenAfterFixups(body);
......@@ -8478,6 +8549,10 @@ fn tunnelThroughClosure(
84788549 });
84798550 }
84808551
8552 // Incorporate the capture index into the source hash, so that changes in
8553 // the order of captures cause suitable re-analysis.
8554 astgen.src_hasher.update(std.mem.asBytes(&cur_capture_index));
8555
84818556 // Add an instruction to get the value from the closure.
84828557 return gz.addExtendedNodeSmall(.closure_get, inner_ref_node, cur_capture_index);
84838558}
......@@ -9306,6 +9381,13 @@ fn builtinCall(
93069381 },
93079382
93089383 .src => {
9384 // Incorporate the source location into the source hash, so that
9385 // changes in the source location of `@src()` result in re-analysis.
9386 astgen.src_hasher.update(
9387 std.mem.asBytes(&astgen.source_line) ++
9388 std.mem.asBytes(&astgen.source_column),
9389 );
9390
93099391 const token_starts = tree.tokens.items(.start);
93109392 const node_start = token_starts[tree.firstToken(node)];
93119393 astgen.advanceSourceCursor(node_start);
......@@ -12122,6 +12204,9 @@ const GenZir = struct {
1212212204 is_test: bool,
1212312205 is_extern: bool,
1212412206 is_noinline: bool,
12207
12208 /// Ignored if `body_gz == null`.
12209 proto_hash: std.zig.SrcHash,
1212512210 }) !Zir.Inst.Ref {
1212612211 assert(args.src_node != 0);
1212712212 const astgen = gz.astgen;
......@@ -12150,15 +12235,7 @@ const GenZir = struct {
1215012235
1215112236 const columns = args.lbrace_column | (rbrace_column << 16);
1215212237
12153 const proto_hash: std.zig.SrcHash = switch (node_tags[fn_decl]) {
12154 .fn_decl => sig_hash: {
12155 const proto_node = node_datas[fn_decl].lhs;
12156 break :sig_hash std.zig.hashSrc(tree.getNodeSource(proto_node));
12157 },
12158 .test_decl => std.zig.hashSrc(""), // tests don't have a prototype
12159 else => unreachable,
12160 };
12161 const proto_hash_arr: [4]u32 = @bitCast(proto_hash);
12238 const proto_hash_arr: [4]u32 = @bitCast(args.proto_hash);
1216212239
1216312240 src_locs_and_hash_buffer = .{
1216412241 args.lbrace_line,
src/InternPool.zig+4
......@@ -2391,6 +2391,7 @@ pub const Key = union(enum) {
23912391 func: Index,
23922392 arg_values: []const Index,
23932393 result: Index,
2394 branch_count: u32,
23942395 };
23952396
23962397 pub fn hash32(key: Key, ip: *const InternPool) u32 {
......@@ -6157,6 +6158,7 @@ pub const MemoizedCall = struct {
61576158 func: Index,
61586159 args_len: u32,
61596160 result: Index,
6161 branch_count: u32,
61606162};
61616163
61626164pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void {
......@@ -6785,6 +6787,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
67856787 .func = extra.data.func,
67866788 .arg_values = @ptrCast(extra_list.view().items(.@"0")[extra.end..][0..extra.data.args_len]),
67876789 .result = extra.data.result,
6790 .branch_count = extra.data.branch_count,
67886791 } };
67896792 },
67906793 };
......@@ -7955,6 +7958,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
79557958 .func = memoized_call.func,
79567959 .args_len = @intCast(memoized_call.arg_values.len),
79577960 .result = memoized_call.result,
7961 .branch_count = memoized_call.branch_count,
79587962 }),
79597963 });
79607964 extra.appendSliceAssumeCapacity(.{@ptrCast(memoized_call.arg_values)});
src/Sema.zig+110-78
......@@ -113,6 +113,11 @@ type_references: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{},
113113/// `AnalUnit` multiple times.
114114dependencies: std.AutoArrayHashMapUnmanaged(InternPool.Dependee, void) = .{},
115115
116/// Whether memoization of this call is permitted. Operations with side effects global
117/// to the `Sema`, such as `@setEvalBranchQuota`, set this to `false`. It is observed
118/// by `analyzeCall`.
119allow_memoize: bool = true,
120
116121const MaybeComptimeAlloc = struct {
117122 /// The runtime index of the `alloc` instruction.
118123 runtime_index: Value.RuntimeIndex,
......@@ -5524,6 +5529,7 @@ fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi
55245529 .needed_comptime_reason = "eval branch quota must be comptime-known",
55255530 }));
55265531 sema.branch_quota = @max(sema.branch_quota, quota);
5532 sema.allow_memoize = false;
55275533}
55285534
55295535fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
......@@ -6416,6 +6422,7 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst
64166422 }
64176423
64186424 zcu.intern_pool.funcMaxStackAlignment(sema.func_index, alignment);
6425 sema.allow_memoize = false;
64196426}
64206427
64216428fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
......@@ -6434,6 +6441,7 @@ fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData)
64346441 .cau => return, // does nothing outside a function
64356442 };
64366443 ip.funcSetCold(func, is_cold);
6444 sema.allow_memoize = false;
64376445}
64386446
64396447fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
......@@ -6445,6 +6453,7 @@ fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
64456453 .cau => return, // does nothing outside a function
64466454 };
64476455 ip.funcSetDisableInstrumentation(func);
6456 sema.allow_memoize = false;
64486457}
64496458
64506459fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
......@@ -7589,6 +7598,9 @@ fn analyzeCall(
75897598
75907599 const module_fn = zcu.funcInfo(module_fn_index);
75917600
7601 // The call site definitely depends on the function's signature.
7602 try sema.declareDependency(.{ .src_hash = module_fn.zir_body_inst });
7603
75927604 // This is not a function instance, so the function's `Nav` has a
75937605 // `Cau` -- we don't need to check `generic_owner`.
75947606 const fn_nav = ip.getNav(module_fn.owner_nav);
......@@ -7725,101 +7737,121 @@ fn analyzeCall(
77257737 } }));
77267738 }
77277739
7728 // This `res2` is here instead of directly breaking from `res` due to a stage1
7729 // bug generating invalid LLVM IR.
7730 const res2: Air.Inst.Ref = res2: {
7731 if (should_memoize and is_comptime_call) {
7732 if (zcu.intern_pool.getIfExists(.{ .memoized_call = .{
7740 memoize: {
7741 if (!should_memoize) break :memoize;
7742 if (!is_comptime_call) break :memoize;
7743 const memoized_call_index = ip.getIfExists(.{
7744 .memoized_call = .{
77337745 .func = module_fn_index,
77347746 .arg_values = memoized_arg_values,
7735 .result = .none,
7736 } })) |memoized_call_index| {
7737 const memoized_call = zcu.intern_pool.indexToKey(memoized_call_index).memoized_call;
7738 break :res2 Air.internedToRef(memoized_call.result);
7739 }
7747 .result = undefined, // ignored by hash+eql
7748 .branch_count = undefined, // ignored by hash+eql
7749 },
7750 }) orelse break :memoize;
7751 const memoized_call = ip.indexToKey(memoized_call_index).memoized_call;
7752 if (sema.branch_count + memoized_call.branch_count > sema.branch_quota) {
7753 // Let the call play out se we get the correct source location for the
7754 // "evaluation exceeded X backwards branches" error.
7755 break :memoize;
77407756 }
7757 sema.branch_count += memoized_call.branch_count;
7758 break :res Air.internedToRef(memoized_call.result);
7759 }
77417760
7742 new_fn_info.return_type = sema.fn_ret_ty.toIntern();
7743 if (!is_comptime_call and !block.is_typeof) {
7744 const zir_tags = sema.code.instructions.items(.tag);
7745 for (fn_info.param_body) |param| switch (zir_tags[@intFromEnum(param)]) {
7746 .param, .param_comptime => {
7747 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(param)].pl_tok;
7748 const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index);
7749 const param_name = sema.code.nullTerminatedString(extra.data.name);
7750 const inst = sema.inst_map.get(param).?;
7761 // Since we're doing an inline call, we depend on the source code of the whole
7762 // function declaration.
7763 try sema.declareDependency(.{ .src_hash = fn_cau.zir_index });
77517764
7752 try sema.addDbgVar(&child_block, inst, .dbg_arg_inline, param_name);
7753 },
7754 .param_anytype, .param_anytype_comptime => {
7755 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(param)].str_tok;
7756 const param_name = inst_data.get(sema.code);
7757 const inst = sema.inst_map.get(param).?;
7765 new_fn_info.return_type = sema.fn_ret_ty.toIntern();
7766 if (!is_comptime_call and !block.is_typeof) {
7767 const zir_tags = sema.code.instructions.items(.tag);
7768 for (fn_info.param_body) |param| switch (zir_tags[@intFromEnum(param)]) {
7769 .param, .param_comptime => {
7770 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(param)].pl_tok;
7771 const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index);
7772 const param_name = sema.code.nullTerminatedString(extra.data.name);
7773 const inst = sema.inst_map.get(param).?;
7774
7775 try sema.addDbgVar(&child_block, inst, .dbg_arg_inline, param_name);
7776 },
7777 .param_anytype, .param_anytype_comptime => {
7778 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(param)].str_tok;
7779 const param_name = inst_data.get(sema.code);
7780 const inst = sema.inst_map.get(param).?;
77587781
7759 try sema.addDbgVar(&child_block, inst, .dbg_arg_inline, param_name);
7760 },
7761 else => continue,
7762 };
7763 }
7782 try sema.addDbgVar(&child_block, inst, .dbg_arg_inline, param_name);
7783 },
7784 else => continue,
7785 };
7786 }
77647787
7765 if (is_comptime_call and ensure_result_used) {
7766 try sema.ensureResultUsed(block, sema.fn_ret_ty, call_src);
7767 }
7788 if (is_comptime_call and ensure_result_used) {
7789 try sema.ensureResultUsed(block, sema.fn_ret_ty, call_src);
7790 }
77687791
7769 if (is_comptime_call or block.is_typeof) {
7770 // Save the error trace as our first action in the function
7771 // to match the behavior of runtime function calls.
7772 const error_return_trace_index = try sema.analyzeSaveErrRetIndex(&child_block);
7773 sema.error_return_trace_index_on_fn_entry = error_return_trace_index;
7774 child_block.error_return_trace_index = error_return_trace_index;
7775 }
7792 if (is_comptime_call or block.is_typeof) {
7793 // Save the error trace as our first action in the function
7794 // to match the behavior of runtime function calls.
7795 const error_return_trace_index = try sema.analyzeSaveErrRetIndex(&child_block);
7796 sema.error_return_trace_index_on_fn_entry = error_return_trace_index;
7797 child_block.error_return_trace_index = error_return_trace_index;
7798 }
77767799
7777 const result = result: {
7778 sema.analyzeFnBody(&child_block, fn_info.body) catch |err| switch (err) {
7779 error.ComptimeReturn => break :result inlining.comptime_result,
7780 else => |e| return e,
7781 };
7782 break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, merges, need_debug_scope);
7800 // We temporarily set `allow_memoize` to `true` to track this comptime call.
7801 // It is restored after this call finishes analysis, so that a caller may
7802 // know whether an in-progress call (containing this call) may be memoized.
7803 const old_allow_memoize = sema.allow_memoize;
7804 defer sema.allow_memoize = old_allow_memoize and sema.allow_memoize;
7805 sema.allow_memoize = true;
7806
7807 // Store the current eval branch count so we can find out how many eval branches
7808 // the comptime call caused.
7809 const old_branch_count = sema.branch_count;
7810
7811 const result = result: {
7812 sema.analyzeFnBody(&child_block, fn_info.body) catch |err| switch (err) {
7813 error.ComptimeReturn => break :result inlining.comptime_result,
7814 else => |e| return e,
77837815 };
7816 break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, merges, need_debug_scope);
7817 };
77847818
7785 if (is_comptime_call) {
7786 const result_val = try sema.resolveConstValue(block, LazySrcLoc.unneeded, result, undefined);
7787 const result_interned = result_val.toIntern();
7788
7789 // Transform ad-hoc inferred error set types into concrete error sets.
7790 const result_transformed = try sema.resolveAdHocInferredErrorSet(block, call_src, result_interned);
7791
7792 // If the result can mutate comptime vars, we must not memoize it, as it contains
7793 // a reference to `comptime_allocs` so is not stable across instances of `Sema`.
7794 // TODO: check whether any external comptime memory was mutated by the
7795 // comptime function call. If so, then do not memoize the call here.
7796 if (should_memoize and !Value.fromInterned(result_interned).canMutateComptimeVarState(zcu)) {
7797 _ = try pt.intern(.{ .memoized_call = .{
7798 .func = module_fn_index,
7799 .arg_values = memoized_arg_values,
7800 .result = result_transformed,
7801 } });
7802 }
7819 if (is_comptime_call) {
7820 const result_val = try sema.resolveConstValue(block, LazySrcLoc.unneeded, result, undefined);
7821 const result_interned = result_val.toIntern();
78037822
7804 break :res2 Air.internedToRef(result_transformed);
7805 }
7823 // Transform ad-hoc inferred error set types into concrete error sets.
7824 const result_transformed = try sema.resolveAdHocInferredErrorSet(block, call_src, result_interned);
78067825
7807 if (try sema.resolveValue(result)) |result_val| {
7808 const result_transformed = try sema.resolveAdHocInferredErrorSet(block, call_src, result_val.toIntern());
7809 break :res2 Air.internedToRef(result_transformed);
7826 // If the result can mutate comptime vars, we must not memoize it, as it contains
7827 // a reference to `comptime_allocs` so is not stable across instances of `Sema`.
7828 // TODO: check whether any external comptime memory was mutated by the
7829 // comptime function call. If so, then do not memoize the call here.
7830 if (should_memoize and sema.allow_memoize and !Value.fromInterned(result_interned).canMutateComptimeVarState(zcu)) {
7831 _ = try pt.intern(.{ .memoized_call = .{
7832 .func = module_fn_index,
7833 .arg_values = memoized_arg_values,
7834 .result = result_transformed,
7835 .branch_count = sema.branch_count - old_branch_count,
7836 } });
78107837 }
78117838
7812 const new_ty = try sema.resolveAdHocInferredErrorSetTy(block, call_src, sema.typeOf(result).toIntern());
7813 if (new_ty != .none) {
7814 // TODO: mutate in place the previous instruction if possible
7815 // rather than adding a bitcast instruction.
7816 break :res2 try block.addBitCast(Type.fromInterned(new_ty), result);
7817 }
7839 break :res Air.internedToRef(result_transformed);
7840 }
78187841
7819 break :res2 result;
7820 };
7842 if (try sema.resolveValue(result)) |result_val| {
7843 const result_transformed = try sema.resolveAdHocInferredErrorSet(block, call_src, result_val.toIntern());
7844 break :res Air.internedToRef(result_transformed);
7845 }
7846
7847 const new_ty = try sema.resolveAdHocInferredErrorSetTy(block, call_src, sema.typeOf(result).toIntern());
7848 if (new_ty != .none) {
7849 // TODO: mutate in place the previous instruction if possible
7850 // rather than adding a bitcast instruction.
7851 break :res try block.addBitCast(Type.fromInterned(new_ty), result);
7852 }
78217853
7822 break :res res2;
7854 break :res result;
78237855 } else res: {
78247856 assert(!func_ty_info.is_generic);
78257857
src/register_manager.zig+2
......@@ -93,6 +93,8 @@ pub fn RegisterManager(
9393 comptime set: []const Register,
9494 reg: Register,
9595 ) ?std.math.IntFittingRange(0, set.len - 1) {
96 @setEvalBranchQuota(3000);
97
9698 const Id = @TypeOf(reg.id());
9799 comptime var min_id: Id = std.math.maxInt(Id);
98100 comptime var max_id: Id = std.math.minInt(Id);
test/incremental/delete_comptime_decls+1-1
......@@ -31,7 +31,7 @@ pub fn main() void {}
3131comptime {
3232 const x: [*c]u8 = null;
3333 var runtime_len: usize = undefined;
34 runtime_len = 0;
34 runtime_len = 0;
3535 const y = x[0..runtime_len];
3636 _ = y;
3737}
test/incremental/modify_inline_fn created+23
......@@ -0,0 +1,23 @@
1#target=x86_64-linux
2#update=initial version
3#file=main.zig
4const std = @import("std");
5pub fn main() !void {
6 const str = getStr();
7 try std.io.getStdOut().writeAll(str);
8}
9inline fn getStr() []const u8 {
10 return "foo\n";
11}
12#expect_stdout="foo\n"
13#update=change the string
14#file=main.zig
15const std = @import("std");
16pub fn main() !void {
17 const str = getStr();
18 try std.io.getStdOut().writeAll(str);
19}
20inline fn getStr() []const u8 {
21 return "bar\n";
22}
23#expect_stdout="bar\n"
test/incremental/move_src created+29
......@@ -0,0 +1,29 @@
1#target=x86_64-linux
2#update=initial version
3#file=main.zig
4const std = @import("std");
5pub fn main() !void {
6 try std.io.getStdOut().writer().print("{d} {d}\n", .{ foo(), bar() });
7}
8fn foo() u32 {
9 return @src().line;
10}
11fn bar() u32 {
12 return 123;
13}
14#expect_stdout="6 123\n"
15
16#update=add newline
17#file=main.zig
18const std = @import("std");
19pub fn main() !void {
20 try std.io.getStdOut().writer().print("{d} {d}\n", .{ foo(), bar() });
21}
22
23fn foo() u32 {
24 return @src().line;
25}
26fn bar() u32 {
27 return 123;
28}
29#expect_stdout="7 123\n"