authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-23 21:37:10-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-23 21:37:10-07:00
log13ced07f23311bef859d07cdd25e0e4fa95ab76a
tree6800f8f5e311983d66accee7ef8bf5dd25112685
parentbf7c3e9355530680b066a573c1743f9d570fddc5

stage2: fix while loops

also start to form a plan for how inline while loops will work

5 files changed, 158 insertions(+), 125 deletions(-)

src/Module.zig+9-20
......@@ -700,14 +700,7 @@ pub const Scope = struct {
700700 /// It is shared among all the blocks in an inline or comptime called
701701 /// function.
702702 pub const Inlining = struct {
703 /// Shared state among the entire inline/comptime call stack.
704 shared: *Shared,
705703 merges: Merges,
706
707 pub const Shared = struct {
708 caller: ?*Fn,
709 branch_count: u32,
710 };
711704 };
712705
713706 pub const Merges = struct {
......@@ -2015,6 +2008,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool {
20152008 .inst_map = try analysis_arena.allocator.alloc(*ir.Inst, code.instructions.len),
20162009 .owner_decl = decl,
20172010 .func = null,
2011 .owner_func = null,
20182012 .param_inst_list = &.{},
20192013 };
20202014 var block_scope: Scope.Block = .{
......@@ -2236,6 +2230,7 @@ fn astgenAndSemaFn(
22362230 .inst_map = try fn_type_scope_arena.allocator.alloc(*ir.Inst, fn_type_code.instructions.len),
22372231 .owner_decl = decl,
22382232 .func = null,
2233 .owner_func = null,
22392234 .param_inst_list = &.{},
22402235 };
22412236 var block_scope: Scope.Block = .{
......@@ -2544,6 +2539,7 @@ fn astgenAndSemaVarDecl(
25442539 .inst_map = try gen_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len),
25452540 .owner_decl = decl,
25462541 .func = null,
2542 .owner_func = null,
25472543 .param_inst_list = &.{},
25482544 };
25492545 var block_scope: Scope.Block = .{
......@@ -2608,6 +2604,7 @@ fn astgenAndSemaVarDecl(
26082604 .inst_map = try type_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len),
26092605 .owner_decl = decl,
26102606 .func = null,
2607 .owner_func = null,
26112608 .param_inst_list = &.{},
26122609 };
26132610 var block_scope: Scope.Block = .{
......@@ -3192,6 +3189,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {
31923189 .inst_map = try mod.gpa.alloc(*ir.Inst, func.zir.instructions.len),
31933190 .owner_decl = decl,
31943191 .func = func,
3192 .owner_func = func,
31953193 .param_inst_list = param_inst_list,
31963194 };
31973195 defer mod.gpa.free(sema.inst_map);
......@@ -3681,20 +3679,11 @@ pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) In
36813679 switch (scope.tag) {
36823680 .block => {
36833681 const block = scope.cast(Scope.Block).?;
3684 if (block.inlining) |inlining| {
3685 if (inlining.shared.caller) |func| {
3686 func.state = .sema_failure;
3687 } else {
3688 block.sema.owner_decl.analysis = .sema_failure;
3689 block.sema.owner_decl.generation = mod.generation;
3690 }
3682 if (block.sema.owner_func) |func| {
3683 func.state = .sema_failure;
36913684 } else {
3692 if (block.sema.func) |func| {
3693 func.state = .sema_failure;
3694 } else {
3695 block.sema.owner_decl.analysis = .sema_failure;
3696 block.sema.owner_decl.generation = mod.generation;
3697 }
3685 block.sema.owner_decl.analysis = .sema_failure;
3686 block.sema.owner_decl.generation = mod.generation;
36983687 }
36993688 mod.failed_decls.putAssumeCapacityNoClobber(block.sema.owner_decl, err_msg);
37003689 },
src/Sema.zig+75-28
......@@ -17,6 +17,12 @@ inst_map: []*Inst,
1717/// and `src_decl` of `Scope.Block` is the `Decl` of the callee.
1818/// This `Decl` owns the arena memory of this `Sema`.
1919owner_decl: *Decl,
20/// For an inline or comptime function call, this will be the root parent function
21/// which contains the callsite. Corresponds to `owner_decl`.
22owner_func: ?*Module.Fn,
23/// The function this ZIR code is the body of, according to the source code.
24/// This starts out the same as `owner_func` and then diverges in the case of
25/// an inline or comptime function call.
2026func: ?*Module.Fn,
2127/// For now, TZIR requires arg instructions to be the first N instructions in the
2228/// TZIR code. We store references here for the purpose of `resolveInst`.
......@@ -26,6 +32,7 @@ func: ?*Module.Fn,
2632/// > param_count: u32
2733param_inst_list: []const *ir.Inst,
2834branch_quota: u32 = 1000,
35branch_count: u32 = 0,
2936/// This field is updated when a new source location becomes active, so that
3037/// instructions which do not have explicitly mapped source locations still have
3138/// access to the source location set by the previous instruction which did
......@@ -86,6 +93,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde
8693
8794 const map = block.sema.inst_map;
8895 const tags = block.sema.code.instructions.items(.tag);
96 const datas = block.sema.code.instructions.items(.data);
8997
9098 // We use a while(true) loop here to avoid a redundant way of breaking out of
9199 // the loop. The only way to break out of the loop is with a `noreturn`
......@@ -178,6 +186,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde
178186 .is_non_null_ptr => try sema.zirIsNullPtr(block, inst, true),
179187 .is_null => try sema.zirIsNull(block, inst, false),
180188 .is_null_ptr => try sema.zirIsNullPtr(block, inst, false),
189 .loop => try sema.zirLoop(block, inst),
181190 .merge_error_sets => try sema.zirMergeErrorSets(block, inst),
182191 .mod_rem => try sema.zirArithmetic(block, inst),
183192 .mul => try sema.zirArithmetic(block, inst),
......@@ -225,7 +234,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde
225234 .ret_node => return sema.zirRetNode(block, inst),
226235 .ret_tok => return sema.zirRetTok(block, inst, false),
227236 .@"unreachable" => return sema.zirUnreachable(block, inst),
228 .loop => return sema.zirLoop(block, inst),
237 .repeat => return sema.zirRepeat(block, inst),
229238
230239 // Instructions that we know can *never* be noreturn based solely on
231240 // their tag. We avoid needlessly checking if they are noreturn and
......@@ -276,6 +285,14 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde
276285 try sema.zirResolveInferredAlloc(block, inst);
277286 continue;
278287 },
288
289 // Special case: send comptime control flow back to the beginning of this block.
290 .repeat_inline => {
291 const src: LazySrcLoc = .{ .node_offset = datas[inst].node };
292 try sema.emitBackwardBranch(block, src);
293 i = 0;
294 continue;
295 },
279296 };
280297 if (map[inst].ty.isNoReturn())
281298 return always_noreturn;
......@@ -764,14 +781,50 @@ fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
764781 }
765782}
766783
767fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {
784fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {
785 const tracy = trace(@src());
786 defer tracy.end();
787
788 const src_node = sema.code.instructions.items(.data)[inst].node;
789 const src: LazySrcLoc = .{ .node_offset = src_node };
790 try sema.requireRuntimeBlock(block, src);
791 return always_noreturn;
792}
793
794fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
768795 const tracy = trace(@src());
769796 defer tracy.end();
770797
771798 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
772799 const src = inst_data.src();
773 const extra = sema.code.extraData(zir.Inst.MultiOp, inst_data.payload_index);
774 const body = sema.code.extra[extra.end..][0..extra.data.operands_len];
800 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
801 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
802
803 // TZIR expects a block outside the loop block too.
804 const block_inst = try sema.arena.create(Inst.Block);
805 block_inst.* = .{
806 .base = .{
807 .tag = Inst.Block.base_tag,
808 .ty = undefined,
809 .src = src,
810 },
811 .body = undefined,
812 };
813
814 var child_block = parent_block.makeSubBlock();
815 child_block.label = Scope.Block.Label{
816 .zir_block = inst,
817 .merges = .{
818 .results = .{},
819 .br_list = .{},
820 .block_inst = block_inst,
821 },
822 };
823 const merges = &child_block.label.?.merges;
824
825 defer child_block.instructions.deinit(sema.gpa);
826 defer merges.results.deinit(sema.gpa);
827 defer merges.br_list.deinit(sema.gpa);
775828
776829 // Reserve space for a Loop instruction so that generated Break instructions can
777830 // point to it, even if it doesn't end up getting used because the code ends up being
......@@ -786,23 +839,17 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerE
786839 .body = undefined,
787840 };
788841
789 var child_block: Scope.Block = .{
790 .parent = parent_block,
791 .sema = sema,
792 .src_decl = parent_block.src_decl,
793 .instructions = .{},
794 .inlining = parent_block.inlining,
795 .is_comptime = parent_block.is_comptime,
796 };
797 defer child_block.instructions.deinit(sema.gpa);
842 var loop_block = child_block.makeSubBlock();
843 defer loop_block.instructions.deinit(sema.gpa);
798844
799 _ = try sema.analyzeBody(&child_block, body);
845 _ = try sema.analyzeBody(&loop_block, body);
800846
801847 // Loop repetition is implied so the last instruction may or may not be a noreturn instruction.
802848
803 try parent_block.instructions.append(sema.gpa, &loop_inst.base);
804 loop_inst.body = .{ .instructions = try sema.arena.dupe(*Inst, child_block.instructions.items) };
805 return always_noreturn;
849 try child_block.instructions.append(sema.gpa, &loop_inst.base);
850 loop_inst.body = .{ .instructions = try sema.arena.dupe(*Inst, loop_block.instructions.items) };
851
852 return sema.analyzeBlockBody(parent_block, &child_block, merges);
806853}
807854
808855fn zirBlock(
......@@ -1160,16 +1207,9 @@ fn analyzeCall(
11601207 },
11611208 .body = undefined,
11621209 };
1163 // If this is the top of the inline/comptime call stack, we use this data.
1164 // Otherwise we pass on the shared data from the parent scope.
1165 var shared_inlining: Scope.Block.Inlining.Shared = .{
1166 .branch_count = 0,
1167 .caller = sema.func,
1168 };
11691210 // This one is shared among sub-blocks within the same callee, but not
11701211 // shared among the entire inline/comptime call stack.
11711212 var inlining: Scope.Block.Inlining = .{
1172 .shared = if (block.inlining) |inlining| inlining.shared else &shared_inlining,
11731213 .merges = .{
11741214 .results = .{},
11751215 .br_list = .{},
......@@ -1183,8 +1223,11 @@ fn analyzeCall(
11831223 .code = module_fn.zir,
11841224 .inst_map = try sema.gpa.alloc(*ir.Inst, module_fn.zir.instructions.len),
11851225 .owner_decl = sema.owner_decl,
1226 .owner_func = sema.owner_func,
11861227 .func = module_fn,
11871228 .param_inst_list = casted_args,
1229 .branch_quota = sema.branch_quota,
1230 .branch_count = sema.branch_count,
11881231 };
11891232 defer sema.gpa.free(inline_sema.inst_map);
11901233
......@@ -1210,7 +1253,12 @@ fn analyzeCall(
12101253 // the block_inst above.
12111254 _ = try inline_sema.root(&child_block);
12121255
1213 break :res try inline_sema.analyzeBlockBody(block, &child_block, merges);
1256 const result = try inline_sema.analyzeBlockBody(block, &child_block, merges);
1257
1258 sema.branch_quota = inline_sema.branch_quota;
1259 sema.branch_count = inline_sema.branch_count;
1260
1261 break :res result;
12141262 } else res: {
12151263 try sema.requireRuntimeBlock(block, call_src);
12161264 break :res try block.addCall(call_src, ret_type, func, casted_args);
......@@ -3169,9 +3217,8 @@ fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: Pani
31693217}
31703218
31713219fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void {
3172 const shared = block.inlining.?.shared;
3173 shared.branch_count += 1;
3174 if (shared.branch_count > sema.branch_quota) {
3220 sema.branch_count += 1;
3221 if (sema.branch_count > sema.branch_quota) {
31753222 // TODO show the "called from here" stack
31763223 return sema.mod.fail(&block.base, src, "evaluation exceeded {d} backwards branches", .{sema.branch_quota});
31773224 }
src/astgen.zig+35-62
......@@ -1055,6 +1055,7 @@ fn blockExprStmts(
10551055 .bit_or,
10561056 .block,
10571057 .block_comptime,
1058 .loop,
10581059 .bool_br_and,
10591060 .bool_br_or,
10601061 .bool_not,
......@@ -1156,12 +1157,13 @@ fn blockExprStmts(
11561157 .ret_tok,
11571158 .ret_coerce,
11581159 .@"unreachable",
1159 .loop,
11601160 .elided,
11611161 .store,
11621162 .store_to_block_ptr,
11631163 .store_to_inferred_ptr,
11641164 .resolve_inferred_alloc,
1165 .repeat,
1166 .repeat_inline,
11651167 => break :b true,
11661168 }
11671169 } else switch (maybe_unused_result) {
......@@ -2145,20 +2147,16 @@ fn whileExpr(
21452147 node: ast.Node.Index,
21462148 while_full: ast.full.While,
21472149) InnerError!zir.Inst.Ref {
2148 if (true) @panic("TODO update for zir-memory-layout");
21492150 if (while_full.label_token) |label_token| {
21502151 try checkLabelRedefinition(mod, scope, label_token);
21512152 }
2152 if (while_full.inline_token) |inline_token| {
2153 return mod.failTok(scope, inline_token, "TODO inline while", .{});
2154 }
2155
21562153 const parent_gz = scope.getGenZir();
2154 const loop_block = try parent_gz.addBlock(.loop, node);
2155 try parent_gz.instructions.append(mod.gpa, loop_block);
21572156
21582157 var loop_scope: Scope.GenZir = .{
21592158 .parent = scope,
2160 .decl = scope.ownerDecl().?,
2161 .arena = scope.arena(),
2159 .zir_code = parent_gz.zir_code,
21622160 .force_comptime = parent_gz.force_comptime,
21632161 .instructions = .{},
21642162 };
......@@ -2167,21 +2165,12 @@ fn whileExpr(
21672165
21682166 var continue_scope: Scope.GenZir = .{
21692167 .parent = &loop_scope.base,
2170 .decl = loop_scope.decl,
2171 .arena = loop_scope.arena,
2168 .zir_code = parent_gz.zir_code,
21722169 .force_comptime = loop_scope.force_comptime,
21732170 .instructions = .{},
21742171 };
21752172 defer continue_scope.instructions.deinit(mod.gpa);
21762173
2177 const tree = gz.tree();
2178 const main_tokens = tree.nodes.items(.main_token);
2179
2180 const while_src = token_starts[while_full.ast.while_token];
2181 const void_type = try addZIRInstConst(mod, scope, while_src, .{
2182 .ty = Type.initTag(.type),
2183 .val = Value.initTag(.void_type),
2184 });
21852174 const cond = c: {
21862175 // TODO https://github.com/ziglang/zig/issues/7929
21872176 if (while_full.error_token) |error_token| {
......@@ -2189,59 +2178,41 @@ fn whileExpr(
21892178 } else if (while_full.payload_token) |payload_token| {
21902179 return mod.failTok(scope, payload_token, "TODO implement while optional", .{});
21912180 } else {
2192 const bool_type = try addZIRInstConst(mod, &continue_scope.base, while_src, .{
2193 .ty = Type.initTag(.type),
2194 .val = Value.initTag(.bool_type),
2195 });
2196 break :c try expr(mod, &continue_scope.base, .{ .ty = bool_type }, while_full.ast.cond_expr);
2181 const bool_type_rl: ResultLoc = .{ .ty = @enumToInt(zir.Const.bool_type) };
2182 break :c try expr(mod, &continue_scope.base, bool_type_rl, while_full.ast.cond_expr);
21972183 }
21982184 };
21992185
2200 const condbr = try addZIRInstSpecial(mod, &continue_scope.base, while_src, zir.Inst.CondBr, .{
2201 .condition = cond,
2202 .then_body = undefined, // populated below
2203 .else_body = undefined, // populated below
2204 }, .{});
2205 const cond_block = try addZIRInstBlock(mod, &loop_scope.base, while_src, .block, .{
2206 .instructions = try loop_scope.arena.dupe(zir.Inst.Ref, continue_scope.instructions.items),
2207 });
2186 const condbr = try continue_scope.addCondBr(node);
2187 const cond_block = try loop_scope.addBlock(.block, node);
2188 try loop_scope.instructions.append(mod.gpa, cond_block);
2189 try continue_scope.setBlockBody(cond_block);
2190
22082191 // TODO avoid emitting the continue expr when there
22092192 // are no jumps to it. This happens when the last statement of a while body is noreturn
22102193 // and there are no `continue` statements.
22112194 // The "repeat" at the end of a loop body is implied.
22122195 if (while_full.ast.cont_expr != 0) {
2213 _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, while_full.ast.cont_expr);
2196 const void_type_rl: ResultLoc = .{ .ty = @enumToInt(zir.Const.void_type) };
2197 _ = try expr(mod, &loop_scope.base, void_type_rl, while_full.ast.cont_expr);
22142198 }
2215 const loop = try scope.arena().create(zir.Inst.Loop);
2216 loop.* = .{
2217 .base = .{
2218 .tag = .loop,
2219 .src = while_src,
2220 },
2221 .positionals = .{
2222 .body = .{
2223 .instructions = try scope.arena().dupe(zir.Inst.Ref, loop_scope.instructions.items),
2224 },
2225 },
2226 .kw_args = .{},
2227 };
2228 const while_block = try addZIRInstBlock(mod, scope, while_src, .block, .{
2229 .instructions = try scope.arena().dupe(zir.Inst.Ref, &[1]zir.Inst.Ref{&loop.base}),
2230 });
2231 loop_scope.break_block = while_block;
2199 const is_inline = while_full.inline_token != null;
2200 const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
2201 _ = try loop_scope.addNode(repeat_tag, node);
2202
2203 try loop_scope.setBlockBody(loop_block);
2204 loop_scope.break_block = loop_block;
22322205 loop_scope.continue_block = cond_block;
22332206 if (while_full.label_token) |label_token| {
22342207 loop_scope.label = @as(?Scope.GenZir.Label, Scope.GenZir.Label{
22352208 .token = label_token,
2236 .block_inst = while_block,
2209 .block_inst = loop_block,
22372210 });
22382211 }
22392212
2240 const then_src = token_starts[tree.lastToken(while_full.ast.then_expr)];
22412213 var then_scope: Scope.GenZir = .{
22422214 .parent = &continue_scope.base,
2243 .decl = continue_scope.decl,
2244 .arena = continue_scope.arena,
2215 .zir_code = parent_gz.zir_code,
22452216 .force_comptime = continue_scope.force_comptime,
22462217 .instructions = .{},
22472218 };
......@@ -2254,29 +2225,31 @@ fn whileExpr(
22542225
22552226 var else_scope: Scope.GenZir = .{
22562227 .parent = &continue_scope.base,
2257 .decl = continue_scope.decl,
2258 .arena = continue_scope.arena,
2228 .zir_code = parent_gz.zir_code,
22592229 .force_comptime = continue_scope.force_comptime,
22602230 .instructions = .{},
22612231 };
22622232 defer else_scope.instructions.deinit(mod.gpa);
22632233
22642234 const else_node = while_full.ast.else_expr;
2265 const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: {
2235 const else_info: struct {
2236 src: ast.Node.Index,
2237 result: zir.Inst.Ref,
2238 } = if (else_node != 0) blk: {
22662239 loop_scope.break_count += 1;
22672240 const sub_scope = &else_scope.base;
22682241 break :blk .{
2269 .src = token_starts[tree.lastToken(else_node)],
2242 .src = else_node,
22702243 .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node),
22712244 };
22722245 } else .{
2273 .src = token_starts[tree.lastToken(while_full.ast.then_expr)],
2274 .result = null,
2246 .src = while_full.ast.then_expr,
2247 .result = 0,
22752248 };
22762249
22772250 if (loop_scope.label) |some| {
22782251 if (!some.used) {
2279 return mod.fail(scope, token_starts[some.token], "unused while loop label", .{});
2252 return mod.failTok(scope, some.token, "unused while loop label", .{});
22802253 }
22812254 }
22822255 return finishThenElseBlock(
......@@ -2289,11 +2262,11 @@ fn whileExpr(
22892262 &else_scope,
22902263 condbr,
22912264 cond,
2292 then_src,
2265 while_full.ast.then_expr,
22932266 else_info.src,
22942267 then_result,
22952268 else_info.result,
2296 while_block,
2269 loop_block,
22972270 cond_block,
22982271 );
22992272}
src/ir.zig+10-8
......@@ -80,22 +80,24 @@ pub const Inst = struct {
8080 condbr,
8181 constant,
8282 dbg_stmt,
83 // ?T => bool
83 /// ?T => bool
8484 is_null,
85 // ?T => bool (inverted logic)
85 /// ?T => bool (inverted logic)
8686 is_non_null,
87 // *?T => bool
87 /// *?T => bool
8888 is_null_ptr,
89 // *?T => bool (inverted logic)
89 /// *?T => bool (inverted logic)
9090 is_non_null_ptr,
91 // E!T => bool
91 /// E!T => bool
9292 is_err,
93 // *E!T => bool
93 /// *E!T => bool
9494 is_err_ptr,
9595 bool_and,
9696 bool_or,
9797 /// Read a value from a pointer.
9898 load,
99 /// A labeled block of code that loops forever. At the end of the body it is implied
100 /// to repeat; no explicit "repeat" instruction terminates loop bodies.
99101 loop,
100102 ptrtoint,
101103 ref,
......@@ -112,9 +114,9 @@ pub const Inst = struct {
112114 not,
113115 floatcast,
114116 intcast,
115 // ?T => T
117 /// ?T => T
116118 optional_payload,
117 // *?T => *T
119 /// *?T => *T
118120 optional_payload_ptr,
119121 wrap_optional,
120122 /// E!T -> T
src/zir.zig+29-7
......@@ -649,11 +649,19 @@ pub const Inst = struct {
649649 /// Return a boolean true if dereferenced pointer is an error
650650 /// Uses the `un_tok` field.
651651 is_err_ptr,
652 /// A labeled block of code that loops forever. At the end of the body it is implied
653 /// to repeat; no explicit "repeat" instruction terminates loop bodies.
652 /// A labeled block of code that loops forever. At the end of the body will have either
653 /// a `repeat` instruction or a `repeat_inline` instruction.
654654 /// Uses the `pl_node` field. The AST node is either a for loop or while loop.
655 /// This ZIR instruction is needed because TZIR does not (yet?) match ZIR, and Sema
656 /// needs to emit more than 1 TZIR block for this instruction.
655657 /// The payload is `Block`.
656658 loop,
659 /// Sends runtime control flow back to the beginning of the current block.
660 /// Uses the `node` field.
661 repeat,
662 /// Sends comptime control flow back to the beginning of the current block.
663 /// Uses the `node` field.
664 repeat_inline,
657665 /// Merge two error sets into one, `E1 || E2`.
658666 merge_error_sets,
659667 /// Ambiguously remainder division or modulus. If the computation would possibly have
......@@ -736,6 +744,7 @@ pub const Inst = struct {
736744 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`.
737745 slice_sentinel,
738746 /// Write a value to a pointer. For loading, see `deref`.
747 /// Uses the `bin` union field.
739748 store,
740749 /// Same as `store` but the type of the value being stored will be used to infer
741750 /// the block type. The LHS is the pointer to store to.
......@@ -902,6 +911,7 @@ pub const Inst = struct {
902911 .bit_or,
903912 .block,
904913 .block_comptime,
914 .loop,
905915 .bool_br_and,
906916 .bool_br_or,
907917 .bool_not,
......@@ -1012,7 +1022,8 @@ pub const Inst = struct {
10121022 .ret_tok,
10131023 .ret_coerce,
10141024 .@"unreachable",
1015 .loop,
1025 .repeat,
1026 .repeat_inline,
10161027 => true,
10171028 };
10181029 }
......@@ -1355,12 +1366,13 @@ const Writer = struct {
13551366 .bit_and,
13561367 .bit_or,
13571368 .as,
1358 .@"break",
13591369 .coerce_result_ptr,
13601370 .elem_ptr,
13611371 .elem_val,
13621372 .intcast,
13631373 .merge_error_sets,
1374 .store,
1375 .store_to_block_ptr,
13641376 => try self.writeBin(stream, inst),
13651377
13661378 .alloc,
......@@ -1425,6 +1437,7 @@ const Writer = struct {
14251437 .elided => try stream.writeAll(")"),
14261438 .break_void_node => try self.writeBreakVoidNode(stream, inst),
14271439 .int_type => try self.writeIntType(stream, inst),
1440 .@"break" => try self.writeBreak(stream, inst),
14281441
14291442 .@"asm",
14301443 .asm_volatile,
......@@ -1436,7 +1449,6 @@ const Writer = struct {
14361449 .field_ptr_named,
14371450 .field_val_named,
14381451 .floatcast,
1439 .loop,
14401452 .slice_start,
14411453 .slice_end,
14421454 .slice_sentinel,
......@@ -1473,6 +1485,7 @@ const Writer = struct {
14731485
14741486 .block,
14751487 .block_comptime,
1488 .loop,
14761489 => try self.writePlNodeBlock(stream, inst),
14771490
14781491 .condbr => try self.writePlNodeCondBr(stream, inst),
......@@ -1483,6 +1496,8 @@ const Writer = struct {
14831496 .dbg_stmt_node,
14841497 .ret_ptr,
14851498 .ret_type,
1499 .repeat,
1500 .repeat_inline,
14861501 => try self.writeNode(stream, inst),
14871502
14881503 .decl_ref,
......@@ -1506,8 +1521,6 @@ const Writer = struct {
15061521 .bitcast_result_ptr,
15071522 .error_union_type,
15081523 .error_set,
1509 .store,
1510 .store_to_block_ptr,
15111524 .store_to_inferred_ptr,
15121525 => try stream.writeAll("TODO)"),
15131526 }
......@@ -1772,6 +1785,15 @@ const Writer = struct {
17721785 try self.writeSrc(stream, int_type.src());
17731786 }
17741787
1788 fn writeBreak(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1789 const inst_data = self.code.instructions.items(.data)[inst].@"break";
1790
1791 try self.writeInstIndex(stream, inst_data.block_inst);
1792 try stream.writeAll(", ");
1793 try self.writeInstRef(stream, inst_data.operand);
1794 try stream.writeAll(")");
1795 }
1796
17751797 fn writeUnreachable(self: *Writer, stream: anytype, inst: Inst.Index) !void {
17761798 const inst_data = self.code.instructions.items(.data)[inst].@"unreachable";
17771799 const safety_str = if (inst_data.safety) "safe" else "unsafe";