authorgravatar for jacoblevgw@gmail.comJacob G-W <jacoblevgw@gmail.com> 2021-01-03 15:45:22-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-04 12:42:52-07:00
log638f93ebdceb860974aae54b6f8c2c9f52157305
tree9197ddee7f48b05a2f77ac6f46018ab0299c26ae
parentaa0906e9aaaf36bc928b5502bdb34e7a0409b2c0

stage2: implementation of `@setEvalBranchQuota`:

`@setEvalBranchQuota` can be called before the comptime/inline call stack is created. For example: ```zig @setEvalBranchQuota(100); comptime { while (true) {} } ``` Here we need to set the branch_quota before the comptime block creates a scope for the branch_count.

5 files changed, 76 insertions(+), 5 deletions(-)

src/Module.zig+21-4
......@@ -765,6 +765,8 @@ pub const Scope = struct {
765765 label: ?Label = null,
766766 inlining: ?*Inlining,
767767 is_comptime: bool,
768 /// Shared to sub-blocks.
769 branch_quota: *u32,
768770
769771 pub const InstTable = std.AutoHashMap(*zir.Inst, *Inst);
770772
......@@ -792,8 +794,7 @@ pub const Scope = struct {
792794
793795 pub const Shared = struct {
794796 caller: ?*Fn,
795 branch_count: u64,
796 branch_quota: u64,
797 branch_count: u32,
797798 };
798799 };
799800
......@@ -1104,6 +1105,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
11041105 var inst_table = Scope.Block.InstTable.init(self.gpa);
11051106 defer inst_table.deinit();
11061107
1108 var branch_quota: u32 = 1000;
1109
11071110 var block_scope: Scope.Block = .{
11081111 .parent = null,
11091112 .inst_table = &inst_table,
......@@ -1113,6 +1116,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
11131116 .arena = &decl_arena.allocator,
11141117 .inlining = null,
11151118 .is_comptime = false,
1119 .branch_quota = &branch_quota,
11161120 };
11171121 defer block_scope.instructions.deinit(self.gpa);
11181122
......@@ -1297,6 +1301,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12971301 var decl_inst_table = Scope.Block.InstTable.init(self.gpa);
12981302 defer decl_inst_table.deinit();
12991303
1304 var branch_quota: u32 = 1000;
1305
13001306 var block_scope: Scope.Block = .{
13011307 .parent = null,
13021308 .inst_table = &decl_inst_table,
......@@ -1306,6 +1312,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
13061312 .arena = &decl_arena.allocator,
13071313 .inlining = null,
13081314 .is_comptime = true,
1315 .branch_quota = &branch_quota,
13091316 };
13101317 defer block_scope.instructions.deinit(self.gpa);
13111318
......@@ -1367,6 +1374,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
13671374 var var_inst_table = Scope.Block.InstTable.init(self.gpa);
13681375 defer var_inst_table.deinit();
13691376
1377 var branch_quota_vi: u32 = 1000;
13701378 var inner_block: Scope.Block = .{
13711379 .parent = null,
13721380 .inst_table = &var_inst_table,
......@@ -1376,6 +1384,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
13761384 .arena = &gen_scope_arena.allocator,
13771385 .inlining = null,
13781386 .is_comptime = true,
1387 .branch_quota = &branch_quota_vi,
13791388 };
13801389 defer inner_block.instructions.deinit(self.gpa);
13811390 try zir_sema.analyzeBody(self, &inner_block, .{
......@@ -1494,6 +1503,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
14941503 var inst_table = Scope.Block.InstTable.init(self.gpa);
14951504 defer inst_table.deinit();
14961505
1506 var branch_quota: u32 = 1000;
1507
14971508 var block_scope: Scope.Block = .{
14981509 .parent = null,
14991510 .inst_table = &inst_table,
......@@ -1503,6 +1514,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
15031514 .arena = &analysis_arena.allocator,
15041515 .inlining = null,
15051516 .is_comptime = true,
1517 .branch_quota = &branch_quota,
15061518 };
15071519 defer block_scope.instructions.deinit(self.gpa);
15081520
......@@ -1875,6 +1887,8 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
18751887 defer decl.typed_value.most_recent.arena.?.* = arena.state;
18761888 var inst_table = Scope.Block.InstTable.init(self.gpa);
18771889 defer inst_table.deinit();
1890 var branch_quota: u32 = 1000;
1891
18781892 var inner_block: Scope.Block = .{
18791893 .parent = null,
18801894 .inst_table = &inst_table,
......@@ -1884,6 +1898,7 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
18841898 .arena = &arena.allocator,
18851899 .inlining = null,
18861900 .is_comptime = false,
1901 .branch_quota = &branch_quota,
18871902 };
18881903 defer inner_block.instructions.deinit(self.gpa);
18891904
......@@ -3466,7 +3481,9 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic
34663481 .arena = parent_block.arena,
34673482 .inlining = parent_block.inlining,
34683483 .is_comptime = parent_block.is_comptime,
3484 .branch_quota = parent_block.branch_quota,
34693485 };
3486
34703487 defer fail_block.instructions.deinit(mod.gpa);
34713488
34723489 _ = try mod.safetyPanic(&fail_block, ok.src, panic_id);
......@@ -3532,10 +3549,10 @@ pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex)
35323549pub fn emitBackwardBranch(mod: *Module, block: *Scope.Block, src: usize) !void {
35333550 const shared = block.inlining.?.shared;
35343551 shared.branch_count += 1;
3535 if (shared.branch_count > shared.branch_quota) {
3552 if (shared.branch_count > block.branch_quota.*) {
35363553 // TODO show the "called from here" stack
35373554 return mod.fail(&block.base, src, "evaluation exceeded {d} backwards branches", .{
3538 shared.branch_quota,
3555 block.branch_quota.*,
35393556 });
35403557 }
35413558}
src/astgen.zig+15
......@@ -2317,6 +2317,19 @@ fn compileError(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerE
23172317 return addZIRUnOp(mod, scope, src, .compileerror, target);
23182318}
23192319
2320fn setEvalBranchQuota(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
2321 try ensureBuiltinParamCount(mod, scope, call, 1);
2322 const tree = scope.tree();
2323 const src = tree.token_locs[call.builtin_token].start;
2324 const params = call.params();
2325 const target = try expr(mod, scope, .none, params[0]);
2326 const u32_type = try addZIRInstConst(mod, scope, src, .{
2327 .ty = Type.initTag(.type),
2328 .val = Value.initTag(.u32_type),
2329 });
2330 return addZIRUnOp(mod, scope, src, .setevalbranchquota, try rlWrap(mod, scope, .{ .ty = u32_type }, target));
2331}
2332
23202333fn typeOf(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
23212334 const tree = scope.tree();
23222335 const arena = scope.arena();
......@@ -2362,6 +2375,8 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built
23622375 return rlWrap(mod, scope, rl, try import(mod, scope, call));
23632376 } else if (mem.eql(u8, builtin_name, "@compileError")) {
23642377 return compileError(mod, scope, call);
2378 } else if (mem.eql(u8, builtin_name, "@setEvalBranchQuota")) {
2379 return setEvalBranchQuota(mod, scope, call);
23652380 } else {
23662381 return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{s}'", .{builtin_name});
23672382 }
src/zir.zig+4
......@@ -127,6 +127,8 @@ pub const Inst = struct {
127127 coerce_to_ptr_elem,
128128 /// Emit an error message and fail compilation.
129129 compileerror,
130 /// Changes the maximum number of backwards branches that compile-time code execution can use before giving up and making a compile error.
131 setevalbranchquota,
130132 /// Conditional branch. Splits control flow based on a boolean condition value.
131133 condbr,
132134 /// Special case, has no textual representation.
......@@ -347,6 +349,7 @@ pub const Inst = struct {
347349 .anyframe_type,
348350 .bitnot,
349351 .import,
352 .setevalbranchquota,
350353 => UnOp,
351354
352355 .add,
......@@ -535,6 +538,7 @@ pub const Inst = struct {
535538 .switch_range,
536539 .typeof_peer,
537540 .resolve_inferred_alloc,
541 .setevalbranchquota,
538542 => false,
539543
540544 .@"break",
src/zir_sema.zig+21-1
......@@ -81,6 +81,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
8181 .mut_slice_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.mut_slice_type).?, true, .Slice),
8282 .ptr_type => return analyzeInstPtrType(mod, scope, old_inst.castTag(.ptr_type).?),
8383 .store => return analyzeInstStore(mod, scope, old_inst.castTag(.store).?),
84 .setevalbranchquota => return analyzeInstSetEvalBranchQuota(mod, scope, old_inst.castTag(.setevalbranchquota).?),
8485 .str => return analyzeInstStr(mod, scope, old_inst.castTag(.str).?),
8586 .int => {
8687 const big_int = old_inst.castTag(.int).?.positionals.int;
......@@ -486,6 +487,18 @@ fn analyzeInstStoreToInferredPtr(
486487 return mod.storePtr(scope, inst.base.src, bitcasted_ptr, value);
487488}
488489
490fn analyzeInstSetEvalBranchQuota(
491 mod: *Module,
492 scope: *Scope,
493 inst: *zir.Inst.UnOp,
494) InnerError!*Inst {
495 const b = try mod.requireFunctionBlock(scope, inst.base.src);
496 const quota = @truncate(u32, try resolveInt(mod, scope, inst.positionals.operand, Type.initTag(.u32)));
497 if (b.branch_quota.* < quota)
498 b.branch_quota.* = quota;
499 return mod.constVoid(scope, inst.base.src);
500}
501
489502fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
490503 const ptr = try resolveInst(mod, scope, inst.positionals.lhs);
491504 const value = try resolveInst(mod, scope, inst.positionals.rhs);
......@@ -594,6 +607,7 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError
594607 .arena = parent_block.arena,
595608 .inlining = parent_block.inlining,
596609 .is_comptime = parent_block.is_comptime,
610 .branch_quota = parent_block.branch_quota,
597611 };
598612 defer child_block.instructions.deinit(mod.gpa);
599613
......@@ -619,6 +633,7 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c
619633 .label = null,
620634 .inlining = parent_block.inlining,
621635 .is_comptime = parent_block.is_comptime or is_comptime,
636 .branch_quota = parent_block.branch_quota,
622637 };
623638 defer child_block.instructions.deinit(mod.gpa);
624639
......@@ -666,6 +681,7 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt
666681 }),
667682 .inlining = parent_block.inlining,
668683 .is_comptime = is_comptime or parent_block.is_comptime,
684 .branch_quota = parent_block.branch_quota,
669685 };
670686 const merges = &child_block.label.?.merges;
671687
......@@ -867,7 +883,6 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
867883 // Otherwise we pass on the shared data from the parent scope.
868884 var shared_inlining = Scope.Block.Inlining.Shared{
869885 .branch_count = 0,
870 .branch_quota = 1000,
871886 .caller = b.func,
872887 };
873888 // This one is shared among sub-blocks within the same callee, but not
......@@ -896,7 +911,9 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
896911 .label = null,
897912 .inlining = &inlining,
898913 .is_comptime = is_comptime_call,
914 .branch_quota = b.branch_quota,
899915 };
916
900917 const merges = &child_block.inlining.?.merges;
901918
902919 defer child_block.instructions.deinit(mod.gpa);
......@@ -1417,6 +1434,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
14171434 .arena = parent_block.arena,
14181435 .inlining = parent_block.inlining,
14191436 .is_comptime = parent_block.is_comptime,
1437 .branch_quota = parent_block.branch_quota,
14201438 };
14211439 defer case_block.instructions.deinit(mod.gpa);
14221440
......@@ -1960,6 +1978,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
19601978 .arena = parent_block.arena,
19611979 .inlining = parent_block.inlining,
19621980 .is_comptime = parent_block.is_comptime,
1981 .branch_quota = parent_block.branch_quota,
19631982 };
19641983 defer true_block.instructions.deinit(mod.gpa);
19651984 try analyzeBody(mod, &true_block, inst.positionals.then_body);
......@@ -1973,6 +1992,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
19731992 .arena = parent_block.arena,
19741993 .inlining = parent_block.inlining,
19751994 .is_comptime = parent_block.is_comptime,
1995 .branch_quota = parent_block.branch_quota,
19761996 };
19771997 defer false_block.instructions.deinit(mod.gpa);
19781998 try analyzeBody(mod, &false_block, inst.positionals.else_body);
test/stage2/cbe.zig+15
......@@ -67,7 +67,22 @@ pub fn addCases(ctx: *TestContext) !void {
6767 \\}
6868 , "");
6969 }
70 {
71 var case = ctx.exeFromCompiledC("@setEvalBranchQuota", .{});
7072
73 case.addCompareOutput(
74 \\export fn main() i32 {
75 \\ @setEvalBranchQuota(1001);
76 \\ const y = rec(1001);
77 \\ return y - 1;
78 \\}
79 \\
80 \\inline fn rec(n: usize) usize {
81 \\ if (n <= 1) return n;
82 \\ return rec(n - 1);
83 \\}
84 , "");
85 }
7186 ctx.c("empty start function", linux_x64,
7287 \\export fn _start() noreturn {
7388 \\ unreachable;