authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-25 00:37:52-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-25 00:55:36-07:00
log31023de6c4b3957ef356be01b5454426844955a9
tree7951b8c7265ace392bde49165b505762f1cc33e8
parent12d18a36e5eed4b0898e93df2fd13b56061049b3

stage2: implement inline while

Introduce "inline" variants of ZIR tags: * block => block_inline * repeat => repeat_inline * break => break_inline * condbr => condbr_inline The inline variants perform control flow at compile-time, and they utilize the return value of `Sema.analyzeBody`. `analyzeBody` now returns an Index, not a Ref, which is the ZIR index of a break instruction. This effectively communicates both the intended break target block as well as the operand, allowing parent blocks to find out whether they, in turn, should return the break instruction up the call stack, or accept the operand as the block's result and continue analyzing instructions in the block. Additionally: * removed the deprecated ZIR tag `block_comptime`. * removed `break_void_node` so that all break instructions use the same Data. * zir.Code: remove the `root_start` and `root_len` fields. There is now implied to be a block at index 0 for the root body. This is so that `break_inline` has something to point at and we no longer need the special instruction `break_flat`. * implement source location byteOffset() for .node_offset_if_cond .node_offset_for_cond is probably redundant and can be deleted. We don't have `comptime var` supported yet, so this commit adds a test that at least makes sure the condition is required to be comptime known for `inline while`.

6 files changed, 227 insertions(+), 176 deletions(-)

lib/std/zig/parse.zig+1-4
......@@ -59,10 +59,7 @@ pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!Tree {
5959 parser.nodes.appendAssumeCapacity(.{
6060 .tag = .root,
6161 .main_token = 0,
62 .data = .{
63 .lhs = undefined,
64 .rhs = undefined,
65 },
62 .data = undefined,
6663 });
6764 const root_members = try parser.parseContainerMembers();
6865 const root_decls = try root_members.toSpan(&parser);
src/Module.zig+58-58
......@@ -952,15 +952,11 @@ pub const Scope = struct {
952952 /// initialized, but empty, state.
953953 pub fn finish(gz: *GenZir) !zir.Code {
954954 const gpa = gz.zir_code.gpa;
955 const root_start = @intCast(u32, gz.zir_code.extra.items.len);
956 const root_len = @intCast(u32, gz.instructions.items.len);
957 try gz.zir_code.extra.appendSlice(gpa, gz.instructions.items);
955 try gz.setBlockBody(0);
958956 return zir.Code{
959957 .instructions = gz.zir_code.instructions.toOwnedSlice(),
960958 .string_bytes = gz.zir_code.string_bytes.toOwnedSlice(gpa),
961959 .extra = gz.zir_code.extra.toOwnedSlice(gpa),
962 .root_start = root_start,
963 .root_len = root_len,
964960 };
965961 }
966962
......@@ -1224,11 +1220,12 @@ pub const Scope = struct {
12241220
12251221 pub fn addBreak(
12261222 gz: *GenZir,
1223 tag: zir.Inst.Tag,
12271224 break_block: zir.Inst.Index,
12281225 operand: zir.Inst.Ref,
12291226 ) !zir.Inst.Index {
12301227 return gz.addAsIndex(.{
1231 .tag = .@"break",
1228 .tag = tag,
12321229 .data = .{ .@"break" = .{
12331230 .block_inst = break_block,
12341231 .operand = operand,
......@@ -1236,20 +1233,6 @@ pub const Scope = struct {
12361233 });
12371234 }
12381235
1239 pub fn addBreakVoid(
1240 gz: *GenZir,
1241 break_block: zir.Inst.Index,
1242 node_index: ast.Node.Index,
1243 ) !zir.Inst.Index {
1244 return gz.addAsIndex(.{
1245 .tag = .break_void_node,
1246 .data = .{ .break_void_node = .{
1247 .src_node = gz.zir_code.decl.nodeIndexToRelative(node_index),
1248 .block_inst = break_block,
1249 } },
1250 });
1251 }
1252
12531236 pub fn addBin(
12541237 gz: *GenZir,
12551238 tag: zir.Inst.Tag,
......@@ -1323,11 +1306,11 @@ pub const Scope = struct {
13231306
13241307 /// Note that this returns a `zir.Inst.Index` not a ref.
13251308 /// Leaves the `payload_index` field undefined.
1326 pub fn addCondBr(gz: *GenZir, node: ast.Node.Index) !zir.Inst.Index {
1309 pub fn addCondBr(gz: *GenZir, tag: zir.Inst.Tag, node: ast.Node.Index) !zir.Inst.Index {
13271310 try gz.instructions.ensureCapacity(gz.zir_code.gpa, gz.instructions.items.len + 1);
13281311 const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
13291312 try gz.zir_code.instructions.append(gz.zir_code.gpa, .{
1330 .tag = .condbr,
1313 .tag = tag,
13311314 .data = .{ .pl_node = .{
13321315 .src_node = gz.zir_code.decl.nodeIndexToRelative(node),
13331316 .payload_index = undefined,
......@@ -1462,6 +1445,24 @@ pub const WipZirCode = struct {
14621445 }
14631446};
14641447
1448/// Call `deinit` on the result.
1449fn initAstGen(mod: *Module, decl: *Decl, arena: *Allocator) !WipZirCode {
1450 var wzc: WipZirCode = .{
1451 .decl = decl,
1452 .arena = arena,
1453 .gpa = mod.gpa,
1454 };
1455 // Must be a block instruction at index 0 with the root body.
1456 try wzc.instructions.append(mod.gpa, .{
1457 .tag = .block,
1458 .data = .{ .pl_node = .{
1459 .src_node = 0,
1460 .payload_index = undefined,
1461 } },
1462 });
1463 return wzc;
1464}
1465
14651466/// This struct holds data necessary to construct API-facing `AllErrors.Message`.
14661467/// Its memory is managed with the general purpose allocator so that they
14671468/// can be created and destroyed in response to incremental updates.
......@@ -1572,10 +1573,10 @@ pub const SrcLoc = struct {
15721573 const token_starts = tree.tokens.items(.start);
15731574 return token_starts[tok_index];
15741575 },
1575 .node_abs => |node_index| {
1576 .node_abs => |node| {
15761577 const tree = src_loc.container.file_scope.base.tree();
15771578 const token_starts = tree.tokens.items(.start);
1578 const tok_index = tree.firstToken(node_index);
1579 const tok_index = tree.firstToken(node);
15791580 return token_starts[tok_index];
15801581 },
15811582 .byte_offset => |byte_off| {
......@@ -1591,15 +1592,14 @@ pub const SrcLoc = struct {
15911592 },
15921593 .node_offset => |node_off| {
15931594 const decl = src_loc.container.decl;
1594 const node_index = decl.relativeToNodeIndex(node_off);
1595 const node = decl.relativeToNodeIndex(node_off);
15951596 const tree = decl.container.file_scope.base.tree();
15961597 const main_tokens = tree.nodes.items(.main_token);
1597 const tok_index = main_tokens[node_index];
1598 const tok_index = main_tokens[node];
15981599 const token_starts = tree.tokens.items(.start);
15991600 return token_starts[tok_index];
16001601 },
16011602 .node_offset_var_decl_ty => @panic("TODO"),
1602 .node_offset_for_cond => @panic("TODO"),
16031603 .node_offset_builtin_call_arg0 => @panic("TODO"),
16041604 .node_offset_builtin_call_arg1 => @panic("TODO"),
16051605 .node_offset_builtin_call_argn => unreachable, // Handled specially in `Sema`.
......@@ -1610,7 +1610,27 @@ pub const SrcLoc = struct {
16101610 .node_offset_deref_ptr => @panic("TODO"),
16111611 .node_offset_asm_source => @panic("TODO"),
16121612 .node_offset_asm_ret_ty => @panic("TODO"),
1613 .node_offset_if_cond => @panic("TODO"),
1613
1614 .node_offset_for_cond, .node_offset_if_cond => |node_off| {
1615 const decl = src_loc.container.decl;
1616 const node = decl.relativeToNodeIndex(node_off);
1617 const tree = decl.container.file_scope.base.tree();
1618 const node_tags = tree.nodes.items(.tag);
1619 const cond_expr = switch (node_tags[node]) {
1620 .if_simple => tree.ifSimple(node).ast.cond_expr,
1621 .@"if" => tree.ifFull(node).ast.cond_expr,
1622 .while_simple => tree.whileSimple(node).ast.cond_expr,
1623 .while_cont => tree.whileCont(node).ast.cond_expr,
1624 .@"while" => tree.whileFull(node).ast.cond_expr,
1625 .for_simple => tree.forSimple(node).ast.cond_expr,
1626 .@"for" => tree.forFull(node).ast.cond_expr,
1627 else => unreachable,
1628 };
1629 const main_tokens = tree.nodes.items(.main_token);
1630 const tok_index = main_tokens[cond_expr];
1631 const token_starts = tree.tokens.items(.start);
1632 return token_starts[tok_index];
1633 },
16141634 .node_offset_bin_op => @panic("TODO"),
16151635 .node_offset_bin_lhs => @panic("TODO"),
16161636 .node_offset_bin_rhs => @panic("TODO"),
......@@ -2034,11 +2054,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool {
20342054 defer analysis_arena.deinit();
20352055
20362056 var code: zir.Code = blk: {
2037 var wip_zir_code: WipZirCode = .{
2038 .decl = decl,
2039 .arena = &analysis_arena.allocator,
2040 .gpa = mod.gpa,
2041 };
2057 var wip_zir_code = try mod.initAstGen(decl, &analysis_arena.allocator);
20422058 defer wip_zir_code.deinit();
20432059
20442060 var gen_scope: Scope.GenZir = .{
......@@ -2111,11 +2127,7 @@ fn astgenAndSemaFn(
21112127 var fn_type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
21122128 defer fn_type_scope_arena.deinit();
21132129
2114 var fn_type_wip_zir_code: WipZirCode = .{
2115 .decl = decl,
2116 .arena = &fn_type_scope_arena.allocator,
2117 .gpa = mod.gpa,
2118 };
2130 var fn_type_wip_zir_code = try mod.initAstGen(decl, &fn_type_scope_arena.allocator);
21192131 defer fn_type_wip_zir_code.deinit();
21202132
21212133 var fn_type_scope: Scope.GenZir = .{
......@@ -2270,7 +2282,7 @@ fn astgenAndSemaFn(
22702282 const tag: zir.Inst.Tag = if (is_var_args) .fn_type_var_args else .fn_type;
22712283 break :fn_type try fn_type_scope.addFnType(tag, return_type_inst, param_types);
22722284 };
2273 _ = try fn_type_scope.addUnNode(.break_flat, fn_type_inst, 0);
2285 _ = try fn_type_scope.addBreak(.break_inline, 0, fn_type_inst);
22742286
22752287 // We need the memory for the Type to go into the arena for the Decl
22762288 var decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
......@@ -2348,12 +2360,8 @@ fn astgenAndSemaFn(
23482360
23492361 const fn_zir: zir.Code = blk: {
23502362 // We put the ZIR inside the Decl arena.
2351 var wip_zir_code: WipZirCode = .{
2352 .decl = decl,
2353 .arena = &decl_arena.allocator,
2354 .gpa = mod.gpa,
2355 .ref_start_index = @intCast(u32, zir.Inst.Ref.typed_value_map.len + param_count),
2356 };
2363 var wip_zir_code = try mod.initAstGen(decl, &decl_arena.allocator);
2364 wip_zir_code.ref_start_index = @intCast(u32, zir.Inst.Ref.typed_value_map.len + param_count);
23572365 defer wip_zir_code.deinit();
23582366
23592367 var gen_scope: Scope.GenZir = .{
......@@ -2559,11 +2567,7 @@ fn astgenAndSemaVarDecl(
25592567 var gen_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
25602568 defer gen_scope_arena.deinit();
25612569
2562 var wip_zir_code: WipZirCode = .{
2563 .decl = decl,
2564 .arena = &gen_scope_arena.allocator,
2565 .gpa = mod.gpa,
2566 };
2570 var wip_zir_code = try mod.initAstGen(decl, &gen_scope_arena.allocator);
25672571 defer wip_zir_code.deinit();
25682572
25692573 var gen_scope: Scope.GenZir = .{
......@@ -2583,7 +2587,7 @@ fn astgenAndSemaVarDecl(
25832587 init_result_loc,
25842588 var_decl.ast.init_node,
25852589 );
2586 _ = try gen_scope.addUnNode(.break_flat, init_inst, var_decl.ast.init_node);
2590 _ = try gen_scope.addBreak(.break_inline, 0, init_inst);
25872591 var code = try gen_scope.finish();
25882592 defer code.deinit(mod.gpa);
25892593 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
......@@ -2611,7 +2615,7 @@ fn astgenAndSemaVarDecl(
26112615 };
26122616 defer block_scope.instructions.deinit(mod.gpa);
26132617
2614 const init_inst_zir_ref = try sema.root(&block_scope);
2618 const init_inst_zir_ref = try sema.rootAsRef(&block_scope);
26152619 // The result location guarantees the type coercion.
26162620 const analyzed_init_inst = try sema.resolveInst(init_inst_zir_ref);
26172621 // The is_comptime in the Scope.Block guarantees the result is comptime-known.
......@@ -2632,11 +2636,7 @@ fn astgenAndSemaVarDecl(
26322636 var type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
26332637 defer type_scope_arena.deinit();
26342638
2635 var wip_zir_code: WipZirCode = .{
2636 .decl = decl,
2637 .arena = &type_scope_arena.allocator,
2638 .gpa = mod.gpa,
2639 };
2639 var wip_zir_code = try mod.initAstGen(decl, &type_scope_arena.allocator);
26402640 defer wip_zir_code.deinit();
26412641
26422642 var type_scope: Scope.GenZir = .{
......@@ -2647,7 +2647,7 @@ fn astgenAndSemaVarDecl(
26472647 defer type_scope.instructions.deinit(mod.gpa);
26482648
26492649 const var_type = try astgen.typeExpr(mod, &type_scope.base, var_decl.ast.type_node);
2650 _ = try type_scope.addUnNode(.break_flat, var_type, 0);
2650 _ = try type_scope.addBreak(.break_inline, 0, var_type);
26512651
26522652 var code = try type_scope.finish();
26532653 defer code.deinit(mod.gpa);
src/Sema.zig+72-45
......@@ -60,14 +60,21 @@ const InnerError = Module.InnerError;
6060const Decl = Module.Decl;
6161const LazySrcLoc = Module.LazySrcLoc;
6262
63pub fn root(sema: *Sema, root_block: *Scope.Block) !zir.Inst.Ref {
64 const root_body = sema.code.extra[sema.code.root_start..][0..sema.code.root_len];
63pub fn root(sema: *Sema, root_block: *Scope.Block) !zir.Inst.Index {
64 const inst_data = sema.code.instructions.items(.data)[0].pl_node;
65 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
66 const root_body = sema.code.extra[extra.end..][0..extra.data.body_len];
6567 return sema.analyzeBody(root_block, root_body);
6668}
6769
68/// Assumes that `root_block` ends with `break_flat`.
70pub fn rootAsRef(sema: *Sema, root_block: *Scope.Block) !zir.Inst.Ref {
71 const break_inst = try sema.root(root_block);
72 return sema.code.instructions.items(.data)[break_inst].@"break".operand;
73}
74
75/// Assumes that `root_block` ends with `break_inline`.
6976pub fn rootAsType(sema: *Sema, root_block: *Scope.Block) !Type {
70 const zir_inst_ref = try sema.root(root_block);
77 const zir_inst_ref = try sema.rootAsRef(root_block);
7178 // Source location is unneeded because resolveConstValue must have already
7279 // been successfully called when coercing the value to a type, from the
7380 // result location.
......@@ -78,17 +85,22 @@ pub fn rootAsType(sema: *Sema, root_block: *Scope.Block) !Type {
7885/// return type of `analyzeBody` so that we can tail call them.
7986/// Only appropriate to return when the instruction is known to be NoReturn
8087/// solely based on the ZIR tag.
81const always_noreturn: InnerError!zir.Inst.Ref = .none;
88const always_noreturn: InnerError!zir.Inst.Index = @as(zir.Inst.Index, undefined);
8289
8390/// This function is the main loop of `Sema` and it can be used in two different ways:
8491/// * The traditional way where there are N breaks out of the block and peer type
8592/// resolution is done on the break operands. In this case, the `zir.Inst.Index`
8693/// part of the return value will be `undefined`, and callsites should ignore it,
8794/// finding the block result value via the block scope.
88/// * The "flat" way. There is only 1 break out of the block, and it is with a `break_flat`
95/// * The "flat" way. There is only 1 break out of the block, and it is with a `break_inline`
8996/// instruction. In this case, the `zir.Inst.Index` part of the return value will be
90/// the block result value. No block scope needs to be created for this strategy.
91pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Index) !zir.Inst.Ref {
97/// the break instruction. This communicates both which block the break applies to, as
98/// well as the operand. No block scope needs to be created for this strategy.
99pub fn analyzeBody(
100 sema: *Sema,
101 block: *Scope.Block,
102 body: []const zir.Inst.Index,
103) InnerError!zir.Inst.Index {
92104 // No tracy calls here, to avoid interfering with the tail call mechanism.
93105
94106 const map = block.sema.inst_map;
......@@ -127,8 +139,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde
127139 .bitcast => try sema.zirBitcast(block, inst),
128140 .bitcast_ref => try sema.zirBitcastRef(block, inst),
129141 .bitcast_result_ptr => try sema.zirBitcastResultPtr(block, inst),
130 .block => try sema.zirBlock(block, inst, false),
131 .block_comptime => try sema.zirBlock(block, inst, true),
142 .block => try sema.zirBlock(block, inst),
132143 .bool_not => try sema.zirBoolNot(block, inst),
133144 .bool_and => try sema.zirBoolOp(block, inst, false),
134145 .bool_or => try sema.zirBoolOp(block, inst, true),
......@@ -227,8 +238,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde
227238 // tail call them here.
228239 .condbr => return sema.zirCondbr(block, inst),
229240 .@"break" => return sema.zirBreak(block, inst),
230 .break_void_node => return sema.zirBreakVoidNode(block, inst),
231 .break_flat => return sema.code.instructions.items(.data)[inst].un_node.operand,
241 .break_inline => return inst,
232242 .compile_error => return sema.zirCompileError(block, inst),
233243 .ret_coerce => return sema.zirRetTok(block, inst, true),
234244 .ret_node => return sema.zirRetNode(block, inst),
......@@ -286,13 +296,43 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde
286296 continue;
287297 },
288298
289 // Special case: send comptime control flow back to the beginning of this block.
299 // Special case instructions to handle comptime control flow.
290300 .repeat_inline => {
301 // Send comptime control flow back to the beginning of this block.
291302 const src: LazySrcLoc = .{ .node_offset = datas[inst].node };
292303 try sema.emitBackwardBranch(block, src);
293304 i = 0;
294305 continue;
295306 },
307 .block_inline => blk: {
308 // Directly analyze the block body without introducing a new block.
309 const inst_data = datas[inst].pl_node;
310 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
311 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
312 const break_inst = try sema.analyzeBody(block, inline_body);
313 const break_data = datas[break_inst].@"break";
314 if (inst == break_data.block_inst) {
315 break :blk try sema.resolveInst(break_data.operand);
316 } else {
317 return break_inst;
318 }
319 },
320 .condbr_inline => blk: {
321 const inst_data = datas[inst].pl_node;
322 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
323 const extra = sema.code.extraData(zir.Inst.CondBr, inst_data.payload_index);
324 const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];
325 const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
326 const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition);
327 const inline_body = if (cond.val.toBool()) then_body else else_body;
328 const break_inst = try sema.analyzeBody(block, inline_body);
329 const break_data = datas[break_inst].@"break";
330 if (inst == break_data.block_inst) {
331 break :blk try sema.resolveInst(break_data.operand);
332 } else {
333 return break_inst;
334 }
335 },
296336 };
297337 if (map[inst].ty.isNoReturn())
298338 return always_noreturn;
......@@ -745,7 +785,7 @@ fn zirInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*In
745785 return sema.mod.constIntUnsigned(sema.arena, .unneeded, Type.initTag(.comptime_int), int);
746786}
747787
748fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {
788fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
749789 const tracy = trace(@src());
750790 defer tracy.end();
751791
......@@ -783,7 +823,7 @@ fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
783823 }
784824}
785825
786fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {
826fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
787827 const tracy = trace(@src());
788828 defer tracy.end();
789829
......@@ -854,12 +894,7 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerE
854894 return sema.analyzeBlockBody(parent_block, &child_block, merges);
855895}
856896
857fn zirBlock(
858 sema: *Sema,
859 parent_block: *Scope.Block,
860 inst: zir.Inst.Index,
861 is_comptime: bool,
862) InnerError!*Inst {
897fn zirBlock(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
863898 const tracy = trace(@src());
864899 defer tracy.end();
865900
......@@ -896,7 +931,7 @@ fn zirBlock(
896931 },
897932 }),
898933 .inlining = parent_block.inlining,
899 .is_comptime = is_comptime or parent_block.is_comptime,
934 .is_comptime = parent_block.is_comptime,
900935 };
901936 const merges = &child_block.label.?.merges;
902937
......@@ -1000,7 +1035,7 @@ fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
10001035 _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
10011036}
10021037
1003fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {
1038fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
10041039 const tracy = trace(@src());
10051040 defer tracy.end();
10061041
......@@ -1009,22 +1044,13 @@ fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!z
10091044 return sema.analyzeBreak(block, sema.src, inst_data.block_inst, operand);
10101045}
10111046
1012fn zirBreakVoidNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {
1013 const tracy = trace(@src());
1014 defer tracy.end();
1015
1016 const inst_data = sema.code.instructions.items(.data)[inst].break_void_node;
1017 const void_inst = try sema.mod.constVoid(sema.arena, .unneeded);
1018 return sema.analyzeBreak(block, inst_data.src(), inst_data.block_inst, void_inst);
1019}
1020
10211047fn analyzeBreak(
10221048 sema: *Sema,
10231049 start_block: *Scope.Block,
10241050 src: LazySrcLoc,
10251051 zir_block: zir.Inst.Index,
10261052 operand: *Inst,
1027) InnerError!zir.Inst.Ref {
1053) InnerError!zir.Inst.Index {
10281054 var block = start_block;
10291055 while (true) {
10301056 if (block.label) |*label| {
......@@ -2844,7 +2870,8 @@ fn zirBoolBr(
28442870 const tracy = trace(@src());
28452871 defer tracy.end();
28462872
2847 const inst_data = sema.code.instructions.items(.data)[inst].bool_br;
2873 const datas = sema.code.instructions.items(.data);
2874 const inst_data = datas[inst].bool_br;
28482875 const src: LazySrcLoc = .unneeded;
28492876 const lhs = try sema.resolveInst(inst_data.lhs);
28502877 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
......@@ -2856,9 +2883,9 @@ fn zirBoolBr(
28562883 }
28572884 // comptime-known left-hand side. No need for a block here; the result
28582885 // is simply the rhs expression. Here we rely on there only being 1
2859 // break instruction (`break_flat`).
2860 const zir_inst_ref = try sema.analyzeBody(parent_block, body);
2861 return sema.resolveInst(zir_inst_ref);
2886 // break instruction (`break_inline`).
2887 const break_inst = try sema.analyzeBody(parent_block, body);
2888 return sema.resolveInst(datas[break_inst].@"break".operand);
28622889 }
28632890
28642891 const block_inst = try sema.arena.create(Inst.Block);
......@@ -2889,8 +2916,8 @@ fn zirBoolBr(
28892916 });
28902917 _ = try lhs_block.addBr(src, block_inst, lhs_result);
28912918
2892 const rhs_result_zir_ref = try sema.analyzeBody(rhs_block, body);
2893 const rhs_result = try sema.resolveInst(rhs_result_zir_ref);
2919 const rhs_break_inst = try sema.analyzeBody(rhs_block, body);
2920 const rhs_result = try sema.resolveInst(datas[rhs_break_inst].@"break".operand);
28942921 _ = try rhs_block.addBr(src, block_inst, rhs_result);
28952922
28962923 const tzir_then_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, then_block.instructions.items) };
......@@ -2959,7 +2986,7 @@ fn zirCondbr(
29592986 sema: *Sema,
29602987 parent_block: *Scope.Block,
29612988 inst: zir.Inst.Index,
2962) InnerError!zir.Inst.Ref {
2989) InnerError!zir.Inst.Index {
29632990 const tracy = trace(@src());
29642991 defer tracy.end();
29652992
......@@ -3008,7 +3035,7 @@ fn zirCondbr(
30083035 return always_noreturn;
30093036}
30103037
3011fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {
3038fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
30123039 const tracy = trace(@src());
30133040 defer tracy.end();
30143041
......@@ -3030,7 +3057,7 @@ fn zirRetTok(
30303057 block: *Scope.Block,
30313058 inst: zir.Inst.Index,
30323059 need_coercion: bool,
3033) InnerError!zir.Inst.Ref {
3060) InnerError!zir.Inst.Index {
30343061 const tracy = trace(@src());
30353062 defer tracy.end();
30363063
......@@ -3041,7 +3068,7 @@ fn zirRetTok(
30413068 return sema.analyzeRet(block, operand, src, need_coercion);
30423069}
30433070
3044fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {
3071fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
30453072 const tracy = trace(@src());
30463073 defer tracy.end();
30473074
......@@ -3058,7 +3085,7 @@ fn analyzeRet(
30583085 operand: *Inst,
30593086 src: LazySrcLoc,
30603087 need_coercion: bool,
3061) InnerError!zir.Inst.Ref {
3088) InnerError!zir.Inst.Index {
30623089 if (block.inlining) |inlining| {
30633090 // We are inlining a function call; rewrite the `ret` as a `break`.
30643091 try inlining.merges.results.append(sema.gpa, operand);
......@@ -3244,7 +3271,7 @@ fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id:
32443271 try parent_block.instructions.append(sema.gpa, &block_inst.base);
32453272}
32463273
3247fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId) !zir.Inst.Ref {
3274fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId) !zir.Inst.Index {
32483275 // TODO Once we have a panic function to call, call it here instead of breakpoint.
32493276 _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
32503277 _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach);
src/astgen.zig+27-20
......@@ -699,7 +699,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerErro
699699 };
700700
701701 if (rhs == 0) {
702 _ = try parent_gz.addBreakVoid(block_inst, node);
702 _ = try parent_gz.addBreak(.@"break", block_inst, .void_value);
703703 return zir.Inst.Ref.unreachable_value;
704704 }
705705 block_gz.break_count += 1;
......@@ -707,7 +707,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerErro
707707 const operand = try expr(mod, parent_scope, block_gz.break_result_loc, rhs);
708708 const have_store_to_block = block_gz.rvalue_rl_count != prev_rvalue_rl_count;
709709
710 const br = try parent_gz.addBreak(block_inst, operand);
710 const br = try parent_gz.addBreak(.@"break", block_inst, operand);
711711
712712 if (block_gz.break_result_loc == .block_ptr) {
713713 try block_gz.labeled_breaks.append(mod.gpa, br);
......@@ -860,7 +860,7 @@ fn labeledBlockExpr(
860860 const tracy = trace(@src());
861861 defer tracy.end();
862862
863 assert(zir_tag == .block or zir_tag == .block_comptime);
863 assert(zir_tag == .block);
864864
865865 const tree = parent_scope.tree();
866866 const main_tokens = tree.nodes.items(.main_token);
......@@ -911,8 +911,6 @@ fn labeledBlockExpr(
911911 for (block_scope.labeled_breaks.items) |br| {
912912 zir_datas[br].@"break".operand = .void_value;
913913 }
914 // TODO technically not needed since we changed the tag to break_void but
915 // would be better still to elide the ones that are in this list.
916914 try block_scope.setBlockBody(block_inst);
917915
918916 return gz.zir_code.indexToRef(block_inst);
......@@ -1027,7 +1025,7 @@ fn blockExprStmts(
10271025 .bitcast_result_ptr,
10281026 .bit_or,
10291027 .block,
1030 .block_comptime,
1028 .block_inline,
10311029 .loop,
10321030 .bool_br_and,
10331031 .bool_br_or,
......@@ -1122,9 +1120,9 @@ fn blockExprStmts(
11221120 .compile_log,
11231121 .ensure_err_payload_void,
11241122 .@"break",
1125 .break_void_node,
1126 .break_flat,
1123 .break_inline,
11271124 .condbr,
1125 .condbr_inline,
11281126 .compile_error,
11291127 .ret_node,
11301128 .ret_tok,
......@@ -1663,7 +1661,7 @@ fn orelseCatchExpr(
16631661 };
16641662 const operand = try expr(mod, &block_scope.base, operand_rl, lhs);
16651663 const cond = try block_scope.addUnNode(cond_op, operand, node);
1666 const condbr = try block_scope.addCondBr(node);
1664 const condbr = try block_scope.addCondBr(.condbr, node);
16671665
16681666 const block = try parent_gz.addBlock(.block, node);
16691667 try parent_gz.instructions.append(mod.gpa, block);
......@@ -1731,6 +1729,7 @@ fn orelseCatchExpr(
17311729 else_result,
17321730 block,
17331731 block,
1732 .@"break",
17341733 );
17351734}
17361735
......@@ -1750,6 +1749,7 @@ fn finishThenElseBlock(
17501749 else_result: zir.Inst.Ref,
17511750 main_block: zir.Inst.Index,
17521751 then_break_block: zir.Inst.Index,
1752 break_tag: zir.Inst.Tag,
17531753) InnerError!zir.Inst.Ref {
17541754 // We now have enough information to decide whether the result instruction should
17551755 // be communicated via result location pointer or break instructions.
......@@ -1758,11 +1758,11 @@ fn finishThenElseBlock(
17581758 switch (strat.tag) {
17591759 .break_void => {
17601760 if (!wzc.refIsNoReturn(then_result)) {
1761 _ = try then_scope.addBreakVoid(then_break_block, then_src);
1761 _ = try then_scope.addBreak(break_tag, then_break_block, .void_value);
17621762 }
17631763 const elide_else = if (else_result != .none) wzc.refIsNoReturn(else_result) else false;
17641764 if (!elide_else) {
1765 _ = try else_scope.addBreakVoid(main_block, else_src);
1765 _ = try else_scope.addBreak(break_tag, main_block, .void_value);
17661766 }
17671767 assert(!strat.elide_store_to_block_ptr_instructions);
17681768 try setCondBrPayload(condbr, cond, then_scope, else_scope);
......@@ -1770,14 +1770,14 @@ fn finishThenElseBlock(
17701770 },
17711771 .break_operand => {
17721772 if (!wzc.refIsNoReturn(then_result)) {
1773 _ = try then_scope.addBreak(then_break_block, then_result);
1773 _ = try then_scope.addBreak(break_tag, then_break_block, then_result);
17741774 }
17751775 if (else_result != .none) {
17761776 if (!wzc.refIsNoReturn(else_result)) {
1777 _ = try else_scope.addBreak(main_block, else_result);
1777 _ = try else_scope.addBreak(break_tag, main_block, else_result);
17781778 }
17791779 } else {
1780 _ = try else_scope.addBreakVoid(main_block, else_src);
1780 _ = try else_scope.addBreak(break_tag, main_block, .void_value);
17811781 }
17821782 if (strat.elide_store_to_block_ptr_instructions) {
17831783 try setCondBrPayloadElideBlockStorePtr(condbr, cond, then_scope, else_scope);
......@@ -1944,7 +1944,7 @@ fn boolBinOp(
19441944 };
19451945 defer rhs_scope.instructions.deinit(mod.gpa);
19461946 const rhs = try expr(mod, &rhs_scope.base, .{ .ty = .bool_type }, node_datas[node].rhs);
1947 _ = try rhs_scope.addUnNode(.break_flat, rhs, node);
1947 _ = try rhs_scope.addBreak(.break_inline, bool_br, rhs);
19481948 try rhs_scope.setBoolBrBody(bool_br);
19491949
19501950 const block_ref = gz.zir_code.indexToRef(bool_br);
......@@ -1979,7 +1979,7 @@ fn ifExpr(
19791979 }
19801980 };
19811981
1982 const condbr = try block_scope.addCondBr(node);
1982 const condbr = try block_scope.addCondBr(.condbr, node);
19831983
19841984 const block = try parent_gz.addBlock(.block, node);
19851985 try parent_gz.instructions.append(mod.gpa, block);
......@@ -2042,6 +2042,7 @@ fn ifExpr(
20422042 else_info.result,
20432043 block,
20442044 block,
2045 .@"break",
20452046 );
20462047}
20472048
......@@ -2108,7 +2109,9 @@ fn whileExpr(
21082109 try checkLabelRedefinition(mod, scope, label_token);
21092110 }
21102111 const parent_gz = scope.getGenZir();
2111 const loop_block = try parent_gz.addBlock(.loop, node);
2112 const is_inline = while_full.inline_token != null;
2113 const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop;
2114 const loop_block = try parent_gz.addBlock(loop_tag, node);
21122115 try parent_gz.instructions.append(mod.gpa, loop_block);
21132116
21142117 var loop_scope: Scope.GenZir = .{
......@@ -2140,8 +2143,10 @@ fn whileExpr(
21402143 }
21412144 };
21422145
2143 const condbr = try continue_scope.addCondBr(node);
2144 const cond_block = try loop_scope.addBlock(.block, node);
2146 const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
2147 const condbr = try continue_scope.addCondBr(condbr_tag, node);
2148 const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block;
2149 const cond_block = try loop_scope.addBlock(block_tag, node);
21452150 try loop_scope.instructions.append(mod.gpa, cond_block);
21462151 try continue_scope.setBlockBody(cond_block);
21472152
......@@ -2152,7 +2157,6 @@ fn whileExpr(
21522157 if (while_full.ast.cont_expr != 0) {
21532158 _ = try expr(mod, &loop_scope.base, .{ .ty = .void_type }, while_full.ast.cont_expr);
21542159 }
2155 const is_inline = while_full.inline_token != null;
21562160 const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
21572161 _ = try loop_scope.addNode(repeat_tag, node);
21582162
......@@ -2208,6 +2212,7 @@ fn whileExpr(
22082212 return mod.failTok(scope, some.token, "unused while loop label", .{});
22092213 }
22102214 }
2215 const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
22112216 return finishThenElseBlock(
22122217 mod,
22132218 scope,
......@@ -2224,6 +2229,7 @@ fn whileExpr(
22242229 else_info.result,
22252230 loop_block,
22262231 cond_block,
2232 break_tag,
22272233 );
22282234}
22292235
......@@ -2424,6 +2430,7 @@ fn forExpr(
24242430 else_info.result,
24252431 for_block,
24262432 cond_block,
2433 .@"break",
24272434 );
24282435}
24292436
src/zir.zig+32-49
......@@ -26,6 +26,8 @@ const LazySrcLoc = Module.LazySrcLoc;
2626/// handled by the codegen backend, and errors reported there. However for now,
2727/// inline assembly is not an exception.
2828pub const Code = struct {
29 /// There is always implicitly a `block` instruction at index 0.
30 /// This is so that `break_inline` can break from the root block.
2931 instructions: std.MultiArrayList(Inst).Slice,
3032 /// In order to store references to strings in fewer bytes, we copy all
3133 /// string bytes into here. String bytes can be null. It is up to whomever
......@@ -35,11 +37,6 @@ pub const Code = struct {
3537 string_bytes: []u8,
3638 /// The meaning of this data is determined by `Inst.Tag` value.
3739 extra: []u32,
38 /// First ZIR instruction in this `Code`.
39 /// `extra` at this index contains a `Ref` for every root member.
40 root_start: u32,
41 /// Number of ZIR instructions in the implicit root block of the `Code`.
42 root_len: u32,
4340
4441 /// Returns the requested data, as well as the new index which is at the start of the
4542 /// trailers for the object.
......@@ -98,17 +95,14 @@ pub const Code = struct {
9895 .arena = &arena.allocator,
9996 .scope = scope,
10097 .code = code,
101 .indent = 2,
98 .indent = 0,
10299 .param_count = param_count,
103100 };
104101
105102 const decl_name = scope.srcDecl().?.name;
106103 const stderr = std.io.getStdErr().writer();
107 try stderr.print("ZIR {s} {s} {{\n", .{ kind, decl_name });
108
109 const root_body = code.extra[code.root_start..][0..code.root_len];
110 try writer.writeBody(stderr, root_body);
111
104 try stderr.print("ZIR {s} {s} %0 ", .{ kind, decl_name });
105 try writer.writeInstToStream(stderr, 0);
112106 try stderr.print("}} // ZIR {s} {s}\n\n", .{ kind, decl_name });
113107 }
114108};
......@@ -189,8 +183,11 @@ pub const Inst = struct {
189183 /// A labeled block of code, which can return a value.
190184 /// Uses the `pl_node` union field. Payload is `Block`.
191185 block,
192 /// Same as `block` but additionally makes the inner instructions execute at comptime.
193 block_comptime,
186 /// A list of instructions which are analyzed in the parent context, without
187 /// generating a runtime block. Must terminate with an "inline" variant of
188 /// a noreturn instruction.
189 /// Uses the `pl_node` union field. Payload is `Block`.
190 block_inline,
194191 /// Boolean AND. See also `bit_and`.
195192 /// Uses the `pl_node` union field. Payload is `Bin`.
196193 bool_and,
......@@ -212,16 +209,12 @@ pub const Inst = struct {
212209 /// Uses the `break` union field.
213210 /// Uses the source information from previous instruction.
214211 @"break",
215 /// Same as `break` but has source information in the form of an AST node, and
216 /// the operand is assumed to be the void value.
217 /// Uses the `break_void_node` union field.
218 break_void_node,
219 /// Return a value from a block. This is a special form that is only valid
220 /// when there is exactly 1 break from a block (this one). This instruction
221 /// allows using the return value from `Sema.analyzeBody`. The block is
222 /// assumed to be the direct parent of this instruction.
223 /// Uses the `un_node` union field. The AST node is unused.
224 break_flat,
212 /// Return a value from a block. This instruction is used as the terminator
213 /// of a `block_inline`. It allows using the return value from `Sema.analyzeBody`.
214 /// This instruction may also be used when it is known that there is only one
215 /// break instruction in a block, and the target block is the parent.
216 /// Uses the `break` union field.
217 break_inline,
225218 /// Uses the `node` union field.
226219 breakpoint,
227220 /// Function call with modifier `.auto`.
......@@ -270,7 +263,11 @@ pub const Inst = struct {
270263 /// Uses the `pl_node` union field. AST node is an if, while, for, etc.
271264 /// Payload is `CondBr`.
272265 condbr,
273 /// Special case, has no textual representation.
266 /// Same as `condbr`, except the condition is coerced to a comptime value, and
267 /// only the taken branch is analyzed. The then block and else block must
268 /// terminate with an "inline" variant of a noreturn instruction.
269 condbr_inline,
270 /// A comptime known value.
274271 /// Uses the `const` union field.
275272 @"const",
276273 /// Declares the beginning of a statement. Used for debug info.
......@@ -640,7 +637,7 @@ pub const Inst = struct {
640637 .bitcast_result_ptr,
641638 .bit_or,
642639 .block,
643 .block_comptime,
640 .block_inline,
644641 .loop,
645642 .bool_br_and,
646643 .bool_br_or,
......@@ -744,9 +741,9 @@ pub const Inst = struct {
744741 => false,
745742
746743 .@"break",
747 .break_void_node,
748 .break_flat,
744 .break_inline,
749745 .condbr,
746 .condbr_inline,
750747 .compile_error,
751748 .ret_node,
752749 .ret_tok,
......@@ -1194,16 +1191,6 @@ pub const Inst = struct {
11941191 return .{ .node_offset = self.src_node };
11951192 }
11961193 },
1197 break_void_node: struct {
1198 /// Offset from Decl AST node index.
1199 /// `Tag` determines which kind of AST node this points to.
1200 src_node: i32,
1201 block_inst: Index,
1202
1203 pub fn src(self: @This()) LazySrcLoc {
1204 return .{ .node_offset = self.src_node };
1205 }
1206 },
12071194 @"break": struct {
12081195 block_inst: Index,
12091196 operand: Ref,
......@@ -1410,7 +1397,6 @@ const Writer = struct {
14101397 .err_union_payload_unsafe_ptr,
14111398 .err_union_code,
14121399 .err_union_code_ptr,
1413 .break_flat,
14141400 .is_non_null,
14151401 .is_null,
14161402 .is_non_null_ptr,
......@@ -1438,9 +1424,11 @@ const Writer = struct {
14381424 .int => try self.writeInt(stream, inst),
14391425 .str => try self.writeStr(stream, inst),
14401426 .elided => try stream.writeAll(")"),
1441 .break_void_node => try self.writeBreakVoidNode(stream, inst),
14421427 .int_type => try self.writeIntType(stream, inst),
1443 .@"break" => try self.writeBreak(stream, inst),
1428
1429 .@"break",
1430 .break_inline,
1431 => try self.writeBreak(stream, inst),
14441432
14451433 .@"asm",
14461434 .asm_volatile,
......@@ -1487,11 +1475,13 @@ const Writer = struct {
14871475 => try self.writePlNodeCall(stream, inst),
14881476
14891477 .block,
1490 .block_comptime,
1478 .block_inline,
14911479 .loop,
14921480 => try self.writePlNodeBlock(stream, inst),
14931481
1494 .condbr => try self.writePlNodeCondBr(stream, inst),
1482 .condbr,
1483 .condbr_inline,
1484 => try self.writePlNodeCondBr(stream, inst),
14951485
14961486 .as_node => try self.writeAs(stream, inst),
14971487
......@@ -1771,13 +1761,6 @@ const Writer = struct {
17711761 return self.writeFnTypeCommon(stream, param_types, inst_data.return_type, var_args, cc);
17721762 }
17731763
1774 fn writeBreakVoidNode(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1775 const inst_data = self.code.instructions.items(.data)[inst].break_void_node;
1776 try self.writeInstIndex(stream, inst_data.block_inst);
1777 try stream.writeAll(") ");
1778 try self.writeSrc(stream, inst_data.src());
1779 }
1780
17811764 fn writeIntType(self: *Writer, stream: anytype, inst: Inst.Index) !void {
17821765 const int_type = self.code.instructions.items(.data)[inst].int_type;
17831766 const prefix: u8 = switch (int_type.signedness) {
test/stage2/test.zig+37
......@@ -621,6 +621,43 @@ pub fn addCases(ctx: *TestContext) !void {
621621 "hello\nhello\nhello\nhello\n",
622622 );
623623
624 // inline while requires the condition to be comptime known.
625 case.addError(
626 \\export fn _start() noreturn {
627 \\ var i: u32 = 0;
628 \\ inline while (i < 4) : (i += 1) print();
629 \\ assert(i == 4);
630 \\
631 \\ exit();
632 \\}
633 \\
634 \\fn print() void {
635 \\ asm volatile ("syscall"
636 \\ :
637 \\ : [number] "{rax}" (1),
638 \\ [arg1] "{rdi}" (1),
639 \\ [arg2] "{rsi}" (@ptrToInt("hello\n")),
640 \\ [arg3] "{rdx}" (6)
641 \\ : "rcx", "r11", "memory"
642 \\ );
643 \\ return;
644 \\}
645 \\
646 \\pub fn assert(ok: bool) void {
647 \\ if (!ok) unreachable; // assertion failure
648 \\}
649 \\
650 \\fn exit() noreturn {
651 \\ asm volatile ("syscall"
652 \\ :
653 \\ : [number] "{rax}" (231),
654 \\ [arg1] "{rdi}" (0)
655 \\ : "rcx", "r11", "memory"
656 \\ );
657 \\ unreachable;
658 \\}
659 , &[_][]const u8{":3:21: error: unable to resolve comptime value"});
660
624661 // Labeled blocks (no conditional branch)
625662 case.addCompareOutput(
626663 \\export fn _start() noreturn {