authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-22 23:46:51-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-22 23:47:13-07:00
logd24be85be88737db8399b492931647056c547614
treee6079e363a2b5bcdc1b100b1149c313b13197f41
parent568f333681e6ecf8c60c5bbe04ea1e494d966d48

stage2: fix `if` expressions


5 files changed, 270 insertions(+), 135 deletions(-)

BRANCH_TODO+1-1
......@@ -18,7 +18,6 @@ Merge TODO list:
1818
1919Performance optimizations to look into:
2020 * don't store end index for blocks; rely on last instruction being noreturn
21 * introduce special form for function call statement with 0 or 1 parameters
2221 * look into not storing the field name of field access as a string in zir
2322 instructions. or, look into introducing interning to string_bytes (local
2423 to the owner Decl), or, look into allowing field access based on a token/node
......@@ -31,3 +30,4 @@ Performance optimizations to look into:
3130 function ZIR.
3231 * enum literals can use small strings
3332 * string literals can use small strings
33 * don't need the Sema coercion on condbr condition, it's done with result locations
src/Module.zig+14
......@@ -1307,6 +1307,7 @@ pub const Scope = struct {
13071307 /// Note that this returns a `zir.Inst.Index` not a ref.
13081308 /// Leaves the `payload_index` field undefined.
13091309 pub fn addCondBr(gz: *GenZir, node: ast.Node.Index) !zir.Inst.Index {
1310 try gz.instructions.ensureCapacity(gz.zir_code.gpa, gz.instructions.items.len + 1);
13101311 const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
13111312 try gz.zir_code.instructions.append(gz.zir_code.gpa, .{
13121313 .tag = .condbr,
......@@ -1315,6 +1316,7 @@ pub const Scope = struct {
13151316 .payload_index = undefined,
13161317 } },
13171318 });
1319 gz.instructions.appendAssumeCapacity(new_index);
13181320 return new_index;
13191321 }
13201322
......@@ -1398,6 +1400,14 @@ pub const WipZirCode = struct {
13981400 return result;
13991401 }
14001402
1403 pub fn refIsNoReturn(wzc: WipZirCode, zir_inst_ref: zir.Inst.Ref) bool {
1404 if (zir_inst_ref >= wzc.ref_start_index) {
1405 const zir_inst = zir_inst_ref - wzc.ref_start_index;
1406 return wzc.instructions.items(.tag)[zir_inst].isNoReturn();
1407 }
1408 return false;
1409 }
1410
14011411 pub fn deinit(wzc: *WipZirCode) void {
14021412 wzc.instructions.deinit(wzc.gpa);
14031413 wzc.extra.deinit(wzc.gpa);
......@@ -2290,6 +2300,7 @@ fn astgenAndSemaFn(
22902300 .decl = decl,
22912301 .arena = &decl_arena.allocator,
22922302 .gpa = mod.gpa,
2303 .ref_start_index = @intCast(u32, zir.const_inst_list.len + param_count),
22932304 };
22942305 defer wip_zir_code.deinit();
22952306
......@@ -3199,6 +3210,9 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {
31993210 };
32003211 defer inner_block.instructions.deinit(mod.gpa);
32013212
3213 // TZIR currently requires the arg parameters to be the first N instructions
3214 try inner_block.instructions.appendSlice(mod.gpa, param_inst_list);
3215
32023216 func.state = .in_progress;
32033217 log.debug("set {s} to in_progress", .{decl.name});
32043218
src/Sema.zig+13-15
......@@ -218,7 +218,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde
218218 // tail call them here.
219219 .condbr => return sema.zirCondbr(block, inst),
220220 .@"break" => return sema.zirBreak(block, inst),
221 .break_void_tok => return sema.zirBreakVoidTok(block, inst),
221 .break_void_node => return sema.zirBreakVoidNode(block, inst),
222222 .break_flat => return sema.code.instructions.items(.data)[inst].un_node.operand,
223223 .compile_error => return sema.zirCompileError(block, inst),
224224 .ret_coerce => return sema.zirRetTok(block, inst, true),
......@@ -955,20 +955,18 @@ fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!z
955955 const tracy = trace(@src());
956956 defer tracy.end();
957957
958 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
959 const operand = try sema.resolveInst(bin_inst.rhs);
960 const zir_block = bin_inst.lhs;
961 return sema.analyzeBreak(block, sema.src, zir_block, operand);
958 const inst_data = sema.code.instructions.items(.data)[inst].@"break";
959 const operand = try sema.resolveInst(inst_data.operand);
960 return sema.analyzeBreak(block, sema.src, inst_data.block_inst, operand);
962961}
963962
964fn zirBreakVoidTok(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
963fn zirBreakVoidNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
965964 const tracy = trace(@src());
966965 defer tracy.end();
967966
968 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
969 const zir_block = inst_data.operand;
967 const inst_data = sema.code.instructions.items(.data)[inst].break_void_node;
970968 const void_inst = try sema.mod.constVoid(sema.arena, .unneeded);
971 return sema.analyzeBreak(block, inst_data.src(), zir_block, void_inst);
969 return sema.analyzeBreak(block, inst_data.src(), inst_data.block_inst, void_inst);
972970}
973971
974972fn analyzeBreak(
......@@ -982,7 +980,6 @@ fn analyzeBreak(
982980 while (true) {
983981 if (block.label) |*label| {
984982 if (label.zir_block == zir_block) {
985 try sema.requireFunctionBlock(block, src);
986983 // Here we add a br instruction, but we over-allocate a little bit
987984 // (if necessary) to make it possible to convert the instruction into
988985 // a br_block_flat instruction later.
......@@ -1000,7 +997,7 @@ fn analyzeBreak(
1000997 .operand = operand,
1001998 .block = label.merges.block_inst,
1002999 };
1003 try block.instructions.append(sema.gpa, &br.base);
1000 try start_block.instructions.append(sema.gpa, &br.base);
10041001 try label.merges.results.append(sema.gpa, operand);
10051002 try label.merges.br_list.append(sema.gpa, br);
10061003 return always_noreturn;
......@@ -2613,10 +2610,11 @@ fn zirCmp(
26132610 const tracy = trace(@src());
26142611 defer tracy.end();
26152612
2616 const src: LazySrcLoc = .todo;
2617 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
2618 const lhs = try sema.resolveInst(bin_inst.lhs);
2619 const rhs = try sema.resolveInst(bin_inst.rhs);
2613 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2614 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
2615 const src: LazySrcLoc = inst_data.src();
2616 const lhs = try sema.resolveInst(extra.lhs);
2617 const rhs = try sema.resolveInst(extra.rhs);
26202618
26212619 const is_equality_cmp = switch (op) {
26222620 .eq, .neq => true,
src/astgen.zig+143-95
......@@ -428,12 +428,12 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In
428428 .if_simple => return ifExpr(mod, scope, rl, node, tree.ifSimple(node)),
429429 .@"if" => return ifExpr(mod, scope, rl, node, tree.ifFull(node)),
430430
431 .while_simple => return whileExpr(mod, scope, rl, tree.whileSimple(node)),
432 .while_cont => return whileExpr(mod, scope, rl, tree.whileCont(node)),
433 .@"while" => return whileExpr(mod, scope, rl, tree.whileFull(node)),
431 .while_simple => return whileExpr(mod, scope, rl, node, tree.whileSimple(node)),
432 .while_cont => return whileExpr(mod, scope, rl, node, tree.whileCont(node)),
433 .@"while" => return whileExpr(mod, scope, rl, node, tree.whileFull(node)),
434434
435 .for_simple => return forExpr(mod, scope, rl, tree.forSimple(node)),
436 .@"for" => return forExpr(mod, scope, rl, tree.forFull(node)),
435 .for_simple => return forExpr(mod, scope, rl, node, tree.forSimple(node)),
436 .@"for" => return forExpr(mod, scope, rl, node, tree.forFull(node)),
437437
438438 // TODO handling these separately would actually be simpler & have fewer branches
439439 // once we have a ZIR instruction for each of these 3 cases.
......@@ -956,7 +956,7 @@ fn labeledBlockExpr(
956956 // The code took advantage of the result location as a pointer.
957957 // Turn the break instruction operands into void.
958958 for (block_scope.labeled_breaks.items) |br| {
959 zir_datas[br].bin.rhs = 0;
959 zir_datas[br].@"break".operand = @enumToInt(zir.Const.void_value);
960960 }
961961 // TODO technically not needed since we changed the tag to break_void but
962962 // would be better still to elide the ones that are in this list.
......@@ -1169,7 +1169,7 @@ fn blockExprStmts(
11691169 .compile_log,
11701170 .ensure_err_payload_void,
11711171 .@"break",
1172 .break_void_tok,
1172 .break_void_node,
11731173 .break_flat,
11741174 .condbr,
11751175 .compile_error,
......@@ -1749,13 +1749,13 @@ fn orelseCatchExpr(
17491749
17501750 return finishThenElseBlock(
17511751 mod,
1752 scope,
1753 rl,
17541752 &block_scope,
1753 rl,
1754 node,
17551755 &then_scope,
17561756 &else_scope,
1757 &condbr.positionals.then_body,
1758 &condbr.positionals.else_body,
1757 condbr,
1758 cond,
17591759 src,
17601760 src,
17611761 then_result,
......@@ -1767,75 +1767,87 @@ fn orelseCatchExpr(
17671767
17681768fn finishThenElseBlock(
17691769 mod: *Module,
1770 parent_scope: *Scope,
1771 rl: ResultLoc,
17721770 block_scope: *Scope.GenZir,
1771 rl: ResultLoc,
1772 node: ast.Node.Index,
17731773 then_scope: *Scope.GenZir,
17741774 else_scope: *Scope.GenZir,
1775 then_body: *zir.Body,
1776 else_body: *zir.Body,
1777 then_src: usize,
1778 else_src: usize,
1775 condbr: zir.Inst.Index,
1776 cond: zir.Inst.Ref,
1777 then_src: ast.Node.Index,
1778 else_src: ast.Node.Index,
17791779 then_result: zir.Inst.Ref,
1780 else_result: ?*zir.Inst,
1781 main_block: zir.Inst.Ref.Block,
1782 then_break_block: zir.Inst.Ref.Block,
1780 else_result: zir.Inst.Ref,
1781 main_block: zir.Inst.Index,
1782 then_break_block: zir.Inst.Index,
17831783) InnerError!zir.Inst.Ref {
17841784 // We now have enough information to decide whether the result instruction should
17851785 // be communicated via result location pointer or break instructions.
17861786 const strat = rlStrategy(rl, block_scope);
1787 const wzc = block_scope.zir_code;
17871788 switch (strat.tag) {
17881789 .break_void => {
1789 if (!then_result.tag.isNoReturn()) {
1790 _ = try addZirInstTag(mod, &then_scope.base, then_src, .break_void, .{
1791 .block = then_break_block,
1790 if (!wzc.refIsNoReturn(then_result)) {
1791 _ = try then_scope.add(.{
1792 .tag = .break_void_node,
1793 .data = .{ .break_void_node = .{
1794 .src_node = wzc.decl.nodeIndexToRelative(then_src),
1795 .block_inst = then_break_block,
1796 } },
17921797 });
17931798 }
1794 if (else_result) |inst| {
1795 if (!inst.tag.isNoReturn()) {
1796 _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{
1797 .block = main_block,
1798 });
1799 }
1800 } else {
1801 _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{
1802 .block = main_block,
1799 const elide_else = if (else_result != 0) wzc.refIsNoReturn(else_result) else false;
1800 if (!elide_else) {
1801 _ = try else_scope.add(.{
1802 .tag = .break_void_node,
1803 .data = .{ .break_void_node = .{
1804 .src_node = wzc.decl.nodeIndexToRelative(else_src),
1805 .block_inst = main_block,
1806 } },
18031807 });
18041808 }
18051809 assert(!strat.elide_store_to_block_ptr_instructions);
1806 try then_scope.setBlockBody(then_body);
1807 try else_scope.setBlockBody(else_body);
1808 return &main_block.base;
1810 try setCondBrPayload(condbr, cond, then_scope, else_scope);
1811 return wzc.ref_start_index + main_block;
18091812 },
18101813 .break_operand => {
1811 if (!then_result.tag.isNoReturn()) {
1812 _ = try addZirInstTag(mod, &then_scope.base, then_src, .@"break", .{
1813 .block = then_break_block,
1814 .operand = then_result,
1814 if (!wzc.refIsNoReturn(then_result)) {
1815 _ = try then_scope.add(.{
1816 .tag = .@"break",
1817 .data = .{ .@"break" = .{
1818 .block_inst = then_break_block,
1819 .operand = then_result,
1820 } },
18151821 });
18161822 }
1817 if (else_result) |inst| {
1818 if (!inst.tag.isNoReturn()) {
1819 _ = try addZirInstTag(mod, &else_scope.base, else_src, .@"break", .{
1820 .block = main_block,
1821 .operand = inst,
1823 if (else_result != 0) {
1824 if (!wzc.refIsNoReturn(else_result)) {
1825 _ = try else_scope.add(.{
1826 .tag = .@"break",
1827 .data = .{ .@"break" = .{
1828 .block_inst = main_block,
1829 .operand = else_result,
1830 } },
18221831 });
18231832 }
18241833 } else {
1825 _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{
1826 .block = main_block,
1834 _ = try else_scope.add(.{
1835 .tag = .break_void_node,
1836 .data = .{ .break_void_node = .{
1837 .src_node = wzc.decl.nodeIndexToRelative(else_src),
1838 .block_inst = main_block,
1839 } },
18271840 });
18281841 }
18291842 if (strat.elide_store_to_block_ptr_instructions) {
1830 try copyBodyWithElidedStoreBlockPtr(then_body, then_scope.*);
1831 try copyBodyWithElidedStoreBlockPtr(else_body, else_scope.*);
1843 try setCondBrPayloadElideBlockStorePtr(condbr, cond, then_scope, else_scope);
18321844 } else {
1833 try then_scope.setBlockBody(then_body);
1834 try else_scope.setBlockBody(else_body);
1845 try setCondBrPayload(condbr, cond, then_scope, else_scope);
18351846 }
1847 const block_ref = wzc.ref_start_index + main_block;
18361848 switch (rl) {
1837 .ref => return &main_block.base,
1838 else => return rvalue(mod, parent_scope, rl, &main_block.base),
1849 .ref => return block_ref,
1850 else => return rvalue(mod, &block_scope.base, rl, block_ref, node),
18391851 }
18401852 },
18411853 }
......@@ -1951,18 +1963,18 @@ fn simpleBinOp(
19511963 mod: *Module,
19521964 scope: *Scope,
19531965 rl: ResultLoc,
1954 infix_node: ast.Node.Index,
1966 node: ast.Node.Index,
19551967 op_inst_tag: zir.Inst.Tag,
19561968) InnerError!zir.Inst.Ref {
1957 const tree = scope.tree();
1969 const gz = scope.getGenZir();
1970 const tree = gz.tree();
19581971 const node_datas = tree.nodes.items(.data);
19591972
1960 const gz = scope.getGenZir();
1961 const result = try gz.addPlNode(op_inst_tag, infix_node, zir.Inst.Bin{
1962 .lhs = try expr(mod, scope, .none, node_datas[infix_node].lhs),
1963 .rhs = try expr(mod, scope, .none, node_datas[infix_node].rhs),
1973 const result = try gz.addPlNode(op_inst_tag, node, zir.Inst.Bin{
1974 .lhs = try expr(mod, scope, .none, node_datas[node].lhs),
1975 .rhs = try expr(mod, scope, .none, node_datas[node].rhs),
19641976 });
1965 return rvalue(mod, scope, rl, result, infix_node);
1977 return rvalue(mod, scope, rl, result, node);
19661978}
19671979
19681980fn boolBinOp(
......@@ -2000,7 +2012,6 @@ fn ifExpr(
20002012 node: ast.Node.Index,
20012013 if_full: ast.full.If,
20022014) InnerError!zir.Inst.Ref {
2003 if (true) @panic("TODO update for zir-memory-layout");
20042015 const parent_gz = scope.getGenZir();
20052016 var block_scope: Scope.GenZir = .{
20062017 .parent = scope,
......@@ -2011,8 +2022,6 @@ fn ifExpr(
20112022 setBlockResultLoc(&block_scope, rl);
20122023 defer block_scope.instructions.deinit(mod.gpa);
20132024
2014 const tree = parent_gz.tree();
2015
20162025 const cond = c: {
20172026 // TODO https://github.com/ziglang/zig/issues/7929
20182027 if (if_full.error_token) |error_token| {
......@@ -2031,11 +2040,9 @@ fn ifExpr(
20312040 try parent_gz.instructions.append(mod.gpa, block);
20322041 try block_scope.setBlockBody(block);
20332042
2034 const then_src = token_starts[tree.lastToken(if_full.ast.then_expr)];
20352043 var then_scope: Scope.GenZir = .{
20362044 .parent = scope,
2037 .decl = block_scope.decl,
2038 .arena = block_scope.arena,
2045 .zir_code = parent_gz.zir_code,
20392046 .force_comptime = block_scope.force_comptime,
20402047 .instructions = .{},
20412048 };
......@@ -2052,36 +2059,38 @@ fn ifExpr(
20522059
20532060 var else_scope: Scope.GenZir = .{
20542061 .parent = scope,
2055 .decl = block_scope.decl,
2056 .arena = block_scope.arena,
2062 .zir_code = parent_gz.zir_code,
20572063 .force_comptime = block_scope.force_comptime,
20582064 .instructions = .{},
20592065 };
20602066 defer else_scope.instructions.deinit(mod.gpa);
20612067
20622068 const else_node = if_full.ast.else_expr;
2063 const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: {
2069 const else_info: struct {
2070 src: ast.Node.Index,
2071 result: zir.Inst.Ref,
2072 } = if (else_node != 0) blk: {
20642073 block_scope.break_count += 1;
20652074 const sub_scope = &else_scope.base;
20662075 break :blk .{
2067 .src = token_starts[tree.lastToken(else_node)],
2076 .src = else_node,
20682077 .result = try expr(mod, sub_scope, block_scope.break_result_loc, else_node),
20692078 };
20702079 } else .{
2071 .src = token_starts[tree.lastToken(if_full.ast.then_expr)],
2072 .result = null,
2080 .src = if_full.ast.then_expr,
2081 .result = 0,
20732082 };
20742083
20752084 return finishThenElseBlock(
20762085 mod,
2077 scope,
2078 rl,
20792086 &block_scope,
2087 rl,
2088 node,
20802089 &then_scope,
20812090 &else_scope,
2082 &condbr.positionals.then_body,
2083 &condbr.positionals.else_body,
2084 then_src,
2091 condbr,
2092 cond,
2093 if_full.ast.then_expr,
20852094 else_info.src,
20862095 then_result,
20872096 else_info.result,
......@@ -2090,25 +2099,63 @@ fn ifExpr(
20902099 );
20912100}
20922101
2093/// Expects to find exactly 1 .store_to_block_ptr instruction.
2094fn copyBodyWithElidedStoreBlockPtr(body: *zir.Body, scope: Module.Scope.GenZir) !void {
2095 body.* = .{
2096 .instructions = try scope.arena.alloc(zir.Inst.Ref, scope.instructions.items.len - 1),
2097 };
2098 var dst_index: usize = 0;
2099 for (scope.instructions.items) |src_inst| {
2100 if (src_inst.tag != .store_to_block_ptr) {
2101 body.instructions[dst_index] = src_inst;
2102 dst_index += 1;
2102fn setCondBrPayload(
2103 condbr: zir.Inst.Index,
2104 cond: zir.Inst.Ref,
2105 then_scope: *Scope.GenZir,
2106 else_scope: *Scope.GenZir,
2107) !void {
2108 const wzc = then_scope.zir_code;
2109
2110 try wzc.extra.ensureCapacity(wzc.gpa, wzc.extra.items.len +
2111 @typeInfo(zir.Inst.CondBr).Struct.fields.len +
2112 then_scope.instructions.items.len + else_scope.instructions.items.len);
2113
2114 const zir_datas = wzc.instructions.items(.data);
2115 zir_datas[condbr].pl_node.payload_index = wzc.addExtraAssumeCapacity(zir.Inst.CondBr{
2116 .condition = cond,
2117 .then_body_len = @intCast(u32, then_scope.instructions.items.len),
2118 .else_body_len = @intCast(u32, else_scope.instructions.items.len),
2119 });
2120 wzc.extra.appendSliceAssumeCapacity(then_scope.instructions.items);
2121 wzc.extra.appendSliceAssumeCapacity(else_scope.instructions.items);
2122}
2123
2124/// If `elide_block_store_ptr` is set, expects to find exactly 1 .store_to_block_ptr instruction.
2125fn setCondBrPayloadElideBlockStorePtr(
2126 condbr: zir.Inst.Index,
2127 cond: zir.Inst.Ref,
2128 then_scope: *Scope.GenZir,
2129 else_scope: *Scope.GenZir,
2130) !void {
2131 const wzc = then_scope.zir_code;
2132
2133 try wzc.extra.ensureCapacity(wzc.gpa, wzc.extra.items.len +
2134 @typeInfo(zir.Inst.CondBr).Struct.fields.len +
2135 then_scope.instructions.items.len + else_scope.instructions.items.len - 2);
2136
2137 const zir_datas = wzc.instructions.items(.data);
2138 zir_datas[condbr].pl_node.payload_index = wzc.addExtraAssumeCapacity(zir.Inst.CondBr{
2139 .condition = cond,
2140 .then_body_len = @intCast(u32, then_scope.instructions.items.len - 1),
2141 .else_body_len = @intCast(u32, else_scope.instructions.items.len - 1),
2142 });
2143
2144 const zir_tags = wzc.instructions.items(.tag);
2145 for ([_]*Scope.GenZir{ then_scope, else_scope }) |scope| {
2146 for (scope.instructions.items) |src_inst| {
2147 if (zir_tags[src_inst] != .store_to_block_ptr) {
2148 wzc.extra.appendAssumeCapacity(src_inst);
2149 }
21032150 }
21042151 }
2105 assert(dst_index == body.instructions.len);
21062152}
21072153
21082154fn whileExpr(
21092155 mod: *Module,
21102156 scope: *Scope,
21112157 rl: ResultLoc,
2158 node: ast.Node.Index,
21122159 while_full: ast.full.While,
21132160) InnerError!zir.Inst.Ref {
21142161 if (true) @panic("TODO update for zir-memory-layout");
......@@ -2245,13 +2292,13 @@ fn whileExpr(
22452292 }
22462293 return finishThenElseBlock(
22472294 mod,
2248 scope,
2249 rl,
22502295 &loop_scope,
2296 rl,
2297 node,
22512298 &then_scope,
22522299 &else_scope,
2253 &condbr.positionals.then_body,
2254 &condbr.positionals.else_body,
2300 condbr,
2301 cond,
22552302 then_src,
22562303 else_info.src,
22572304 then_result,
......@@ -2265,6 +2312,7 @@ fn forExpr(
22652312 mod: *Module,
22662313 scope: *Scope,
22672314 rl: ResultLoc,
2315 node: ast.Node.Index,
22682316 for_full: ast.full.While,
22692317) InnerError!zir.Inst.Ref {
22702318 if (true) @panic("TODO update for zir-memory-layout");
......@@ -2442,13 +2490,13 @@ fn forExpr(
24422490 }
24432491 return finishThenElseBlock(
24442492 mod,
2445 scope,
2446 rl,
24472493 &loop_scope,
2494 rl,
2495 node,
24482496 &then_scope,
24492497 &else_scope,
2450 &condbr.positionals.then_body,
2451 &condbr.positionals.else_body,
2498 condbr,
2499 cond,
24522500 then_src,
24532501 else_info.src,
24542502 then_result,
src/zir.zig+99-24
......@@ -90,7 +90,7 @@ pub const Code = struct {
9090 .arena = &arena.allocator,
9191 .scope = scope,
9292 .code = code,
93 .indent = 4,
93 .indent = 2,
9494 .param_count = param_count,
9595 };
9696
......@@ -469,15 +469,13 @@ pub const Inst = struct {
469469 /// Uses the `bool_br` union field.
470470 bool_br_or,
471471 /// Return a value from a block.
472 /// Uses the `bin` union field: `lhs` is `Index` to the block (*not* `Ref`!),
473 /// `rhs` is operand.
472 /// Uses the `break` union field.
474473 /// Uses the source information from previous instruction.
475474 @"break",
476 /// Same as `break` but has source information in the form of a token, and
475 /// Same as `break` but has source information in the form of an AST node, and
477476 /// the operand is assumed to be the void value.
478 /// Uses the `un_tok` union field.
479 /// Note that the block operand is a `Index`, not `Ref`.
480 break_void_tok,
477 /// Uses the `break_void_node` union field.
478 break_void_node,
481479 /// Return a value from a block. This is a special form that is only valid
482480 /// when there is exactly 1 break from a block (this one). This instruction
483481 /// allows using the return value from `Sema.analyzeBody`. The block is
......@@ -997,7 +995,7 @@ pub const Inst = struct {
997995 => false,
998996
999997 .@"break",
1000 .break_void_tok,
998 .break_void_node,
1001999 .break_flat,
10021000 .condbr,
10031001 .compile_error,
......@@ -1023,10 +1021,9 @@ pub const Inst = struct {
10231021 /// This logic is implemented in `Sema.resolveRef`.
10241022 pub const Ref = u32;
10251023
1026 /// For instructions whose payload fits into 8 bytes, this is used.
1027 /// When an instruction's payload does not fit, bin_op is used, and
1028 /// lhs and rhs refer to `Tag`-specific values, with one of the operands
1029 /// used to index into a separate array specific to that instruction.
1024 /// All instructions have an 8-byte payload, which is contained within
1025 /// this union. `Tag` determines which union field is active, as well as
1026 /// how to interpret the data within.
10301027 pub const Data = union {
10311028 /// Used for unary operators, with an AST node source location.
10321029 un_node: struct {
......@@ -1161,6 +1158,20 @@ pub const Inst = struct {
11611158 return .{ .node_offset = self.src_node };
11621159 }
11631160 },
1161 break_void_node: struct {
1162 /// Offset from Decl AST node index.
1163 /// `Tag` determines which kind of AST node this points to.
1164 src_node: i32,
1165 block_inst: Index,
1166
1167 pub fn src(self: @This()) LazySrcLoc {
1168 return .{ .node_offset = self.src_node };
1169 }
1170 },
1171 @"break": struct {
1172 block_inst: Index,
1173 operand: Ref,
1174 },
11641175
11651176 // Make sure we don't accidentally add a field to make this union
11661177 // bigger than expected. Note that in Debug builds, Zig is allowed
......@@ -1368,7 +1379,6 @@ const Writer = struct {
13681379 .break_flat,
13691380 => try self.writeUnNode(stream, inst),
13701381
1371 .break_void_tok,
13721382 .is_non_null,
13731383 .is_null,
13741384 .is_non_null_ptr,
......@@ -1394,16 +1404,11 @@ const Writer = struct {
13941404 .int => try self.writeInt(stream, inst),
13951405 .str => try self.writeStr(stream, inst),
13961406 .elided => try stream.writeAll(")"),
1407 .break_void_node => try self.writeBreakVoidNode(stream, inst),
13971408
13981409 .@"asm",
13991410 .asm_volatile,
1400 .block,
1401 .block_comptime,
1402 .call,
1403 .call_chkused,
1404 .call_compile_time,
14051411 .compile_log,
1406 .condbr,
14071412 .elem_ptr_node,
14081413 .elem_val_node,
14091414 .field_ptr,
......@@ -1441,6 +1446,17 @@ const Writer = struct {
14411446 .xor,
14421447 => try self.writePlNodeBin(stream, inst),
14431448
1449 .call,
1450 .call_chkused,
1451 .call_compile_time,
1452 => try self.writePlNodeCall(stream, inst),
1453
1454 .block,
1455 .block_comptime,
1456 => try self.writePlNodeBlock(stream, inst),
1457
1458 .condbr => try self.writePlNodeCondBr(stream, inst),
1459
14441460 .as_node => try self.writeAs(stream, inst),
14451461
14461462 .breakpoint,
......@@ -1531,7 +1547,8 @@ const Writer = struct {
15311547 inst: Inst.Index,
15321548 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
15331549 const inst_data = self.code.instructions.items(.data)[inst].param_type;
1534 try stream.writeAll("TODO)");
1550 try self.writeInstRef(stream, inst_data.callee);
1551 try stream.print(", {d})", .{inst_data.param_index});
15351552 }
15361553
15371554 fn writePtrTypeSimple(
......@@ -1591,6 +1608,53 @@ const Writer = struct {
15911608 try self.writeSrc(stream, inst_data.src());
15921609 }
15931610
1611 fn writePlNodeCall(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1612 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1613 const extra = self.code.extraData(Inst.Call, inst_data.payload_index);
1614 const args = self.code.extra[extra.end..][0..extra.data.args_len];
1615 try self.writeInstRef(stream, extra.data.callee);
1616 try stream.writeAll(", [");
1617 for (args) |arg, i| {
1618 if (i != 0) try stream.writeAll(", ");
1619 try self.writeInstRef(stream, arg);
1620 }
1621 try stream.writeAll("]) ");
1622 try self.writeSrc(stream, inst_data.src());
1623 }
1624
1625 fn writePlNodeBlock(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1626 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1627 const extra = self.code.extraData(Inst.Block, inst_data.payload_index);
1628 const body = self.code.extra[extra.end..][0..extra.data.body_len];
1629 try stream.writeAll("{\n");
1630 self.indent += 2;
1631 try self.writeBody(stream, body);
1632 self.indent -= 2;
1633 try stream.writeByteNTimes(' ', self.indent);
1634 try stream.writeAll("}) ");
1635 try self.writeSrc(stream, inst_data.src());
1636 }
1637
1638 fn writePlNodeCondBr(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1639 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1640 const extra = self.code.extraData(Inst.CondBr, inst_data.payload_index);
1641 const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len];
1642 const else_body = self.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
1643 try self.writeInstRef(stream, extra.data.condition);
1644 try stream.writeAll(", {\n");
1645 self.indent += 2;
1646 try self.writeBody(stream, then_body);
1647 self.indent -= 2;
1648 try stream.writeByteNTimes(' ', self.indent);
1649 try stream.writeAll("}, {\n");
1650 self.indent += 2;
1651 try self.writeBody(stream, else_body);
1652 self.indent -= 2;
1653 try stream.writeByteNTimes(' ', self.indent);
1654 try stream.writeAll("}) ");
1655 try self.writeSrc(stream, inst_data.src());
1656 }
1657
15941658 fn writeAs(self: *Writer, stream: anytype, inst: Inst.Index) !void {
15951659 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
15961660 const extra = self.code.extraData(Inst.As, inst_data.payload_index).data;
......@@ -1671,6 +1735,13 @@ const Writer = struct {
16711735 return self.writeFnTypeCommon(stream, param_types, inst_data.return_type, var_args, cc);
16721736 }
16731737
1738 fn writeBreakVoidNode(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1739 const inst_data = self.code.instructions.items(.data)[inst].break_void_node;
1740 try self.writeInstIndex(stream, inst_data.block_inst);
1741 try stream.writeAll(") ");
1742 try self.writeSrc(stream, inst_data.src());
1743 }
1744
16741745 fn writeUnreachable(self: *Writer, stream: anytype, inst: Inst.Index) !void {
16751746 const inst_data = self.code.instructions.items(.data)[inst].@"unreachable";
16761747 const safety_str = if (inst_data.safety) "safe" else "unsafe";
......@@ -1686,12 +1757,12 @@ const Writer = struct {
16861757 var_args: bool,
16871758 cc: Inst.Ref,
16881759 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1689 try stream.writeAll("(");
1760 try stream.writeAll("[");
16901761 for (param_types) |param_type, i| {
16911762 if (i != 0) try stream.writeAll(", ");
16921763 try self.writeInstRef(stream, param_type);
16931764 }
1694 try stream.writeAll("), ");
1765 try stream.writeAll("], ");
16951766 try self.writeInstRef(stream, ret_ty);
16961767 try self.writeOptionalInstRef(stream, ", cc=", cc);
16971768 try self.writeFlag(stream, ", var_args", var_args);
......@@ -1707,7 +1778,7 @@ const Writer = struct {
17071778 try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)});
17081779 }
17091780
1710 fn writeInstRef(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1781 fn writeInstRef(self: *Writer, stream: anytype, inst: Inst.Ref) !void {
17111782 var i: usize = inst;
17121783
17131784 if (i < const_inst_list.len) {
......@@ -1720,7 +1791,11 @@ const Writer = struct {
17201791 }
17211792 i -= self.param_count;
17221793
1723 return stream.print("%{d}", .{i});
1794 return self.writeInstIndex(stream, @intCast(Inst.Index, i));
1795 }
1796
1797 fn writeInstIndex(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1798 return stream.print("%{d}", .{inst});
17241799 }
17251800
17261801 fn writeOptionalInstRef(