@@ -113,6 +113,11 @@ type_references: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{},
113
113
/// `AnalUnit` multiple times.
114
114
dependencies: std.AutoArrayHashMapUnmanaged(InternPool.Dependee, void) = .{},
115
115
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`.
119
+ allow_memoize: bool = true,
120
+
116
121
const MaybeComptimeAlloc = struct {
117
122
/// The runtime index of the `alloc` instruction.
118
123
runtime_index: Value.RuntimeIndex,
@@ -5524,6 +5529,7 @@ fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi
5524
5529
.needed_comptime_reason = "eval branch quota must be comptime-known",
5525
5530
}));
5526
5531
sema.branch_quota = @max(sema.branch_quota, quota);
5532
+ sema.allow_memoize = false;
5527
5533
}
5528
5534
5529
5535
fn 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
6416
6422
}
6417
6423
6418
6424
zcu.intern_pool.funcMaxStackAlignment(sema.func_index, alignment);
6425
+ sema.allow_memoize = false;
6419
6426
}
6420
6427
6421
6428
fn 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)
6434
6441
.cau => return, // does nothing outside a function
6435
6442
};
6436
6443
ip.funcSetCold(func, is_cold);
6444
+ sema.allow_memoize = false;
6437
6445
}
6438
6446
6439
6447
fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
@@ -6445,6 +6453,7 @@ fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
6445
6453
.cau => return, // does nothing outside a function
6446
6454
};
6447
6455
ip.funcSetDisableInstrumentation(func);
6456
+ sema.allow_memoize = false;
6448
6457
}
6449
6458
6450
6459
fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
@@ -7727,15 +7736,25 @@ fn analyzeCall(
7727
7736
// This `res2` is here instead of directly breaking from `res` due to a stage1
7728
7737
// bug generating invalid LLVM IR.
7729
7738
const res2: Air.Inst.Ref = res2: {
7730
- if (should_memoize and is_comptime_call) {
7731
- if (zcu.intern_pool.getIfExists(.{ .memoized_call = .{
7732
- .func = module_fn_index,
7733
- .arg_values = memoized_arg_values,
7734
- .result = .none,
7735
- } })) |memoized_call_index| {
7736
- const memoized_call = zcu.intern_pool.indexToKey(memoized_call_index).memoized_call;
7737
- break :res2 Air.internedToRef(memoized_call.result);
7739
+ memoize: {
7740
+ if (!should_memoize) break :memoize;
7741
+ if (!is_comptime_call) break :memoize;
7742
+ const memoized_call_index = ip.getIfExists(.{
7743
+ .memoized_call = .{
7744
+ .func = module_fn_index,
7745
+ .arg_values = memoized_arg_values,
7746
+ .result = undefined, // ignored by hash+eql
7747
+ .branch_count = undefined, // ignored by hash+eql
7748
+ },
7749
+ }) orelse break :memoize;
7750
+ const memoized_call = ip.indexToKey(memoized_call_index).memoized_call;
7751
+ if (sema.branch_count + memoized_call.branch_count > sema.branch_quota) {
7752
+ // Let the call play out se we get the correct source location for the
7753
+ // "evaluation exceeded X backwards branches" error.
7754
+ break :memoize;
7738
7755
}
7756
+ sema.branch_count += memoized_call.branch_count;
7757
+ break :res2 Air.internedToRef(memoized_call.result);
7739
7758
}
7740
7759
7741
7760
new_fn_info.return_type = sema.fn_ret_ty.toIntern();
@@ -7773,6 +7792,17 @@ fn analyzeCall(
7773
7792
child_block.error_return_trace_index = error_return_trace_index;
7774
7793
}
7775
7794
7795
+ // We temporarily set `allow_memoize` to `true` to track this comptime call.
7796
+ // It is restored after this call finishes analysis, so that a caller may
7797
+ // know whether an in-progress call (containing this call) may be memoized.
7798
+ const old_allow_memoize = sema.allow_memoize;
7799
+ defer sema.allow_memoize = old_allow_memoize and sema.allow_memoize;
7800
+ sema.allow_memoize = true;
7801
+
7802
+ // Store the current eval branch count so we can find out how many eval branches
7803
+ // the comptime call caused.
7804
+ const old_branch_count = sema.branch_count;
7805
+
7776
7806
const result = result: {
7777
7807
sema.analyzeFnBody(&child_block, fn_info.body) catch |err| switch (err) {
7778
7808
error.ComptimeReturn => break :result inlining.comptime_result,
@@ -7792,11 +7822,12 @@ fn analyzeCall(
7792
7822
// a reference to `comptime_allocs` so is not stable across instances of `Sema`.
7793
7823
// TODO: check whether any external comptime memory was mutated by the
7794
7824
// comptime function call. If so, then do not memoize the call here.
7795
- if (should_memoize and !Value.fromInterned(result_interned).canMutateComptimeVarState(zcu)) {
7825
+ if (should_memoize and sema.allow_memoize and !Value.fromInterned(result_interned).canMutateComptimeVarState(zcu)) {
7796
7826
_ = try pt.intern(.{ .memoized_call = .{
7797
7827
.func = module_fn_index,
7798
7828
.arg_values = memoized_arg_values,
7799
7829
.result = result_transformed,
7830
+ .branch_count = sema.branch_count - old_branch_count,
7800
7831
} });
7801
7832
}
7802
7833
0 commit comments