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:...@@ -18,7 +18,6 @@ Merge TODO list:
1818
19Performance optimizations to look into:19Performance optimizations to look into:
20 * don't store end index for blocks; rely on last instruction being noreturn20 * 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
22 * look into not storing the field name of field access as a string in zir21 * look into not storing the field name of field access as a string in zir
23 instructions. or, look into introducing interning to string_bytes (local22 instructions. or, look into introducing interning to string_bytes (local
24 to the owner Decl), or, look into allowing field access based on a token/node23 to the owner Decl), or, look into allowing field access based on a token/node
...@@ -31,3 +30,4 @@ Performance optimizations to look into:...@@ -31,3 +30,4 @@ Performance optimizations to look into:
31 function ZIR.30 function ZIR.
32 * enum literals can use small strings31 * enum literals can use small strings
33 * string literals can use small strings32 * 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 {...@@ -1307,6 +1307,7 @@ pub const Scope = struct {
1307 /// Note that this returns a `zir.Inst.Index` not a ref.1307 /// Note that this returns a `zir.Inst.Index` not a ref.
1308 /// Leaves the `payload_index` field undefined.1308 /// Leaves the `payload_index` field undefined.
1309 pub fn addCondBr(gz: *GenZir, node: ast.Node.Index) !zir.Inst.Index {1309 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);
1310 const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);1311 const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
1311 try gz.zir_code.instructions.append(gz.zir_code.gpa, .{1312 try gz.zir_code.instructions.append(gz.zir_code.gpa, .{
1312 .tag = .condbr,1313 .tag = .condbr,
...@@ -1315,6 +1316,7 @@ pub const Scope = struct {...@@ -1315,6 +1316,7 @@ pub const Scope = struct {
1315 .payload_index = undefined,1316 .payload_index = undefined,
1316 } },1317 } },
1317 });1318 });
1319 gz.instructions.appendAssumeCapacity(new_index);
1318 return new_index;1320 return new_index;
1319 }1321 }
13201322
...@@ -1398,6 +1400,14 @@ pub const WipZirCode = struct {...@@ -1398,6 +1400,14 @@ pub const WipZirCode = struct {
1398 return result;1400 return result;
1399 }1401 }
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
1401 pub fn deinit(wzc: *WipZirCode) void {1411 pub fn deinit(wzc: *WipZirCode) void {
1402 wzc.instructions.deinit(wzc.gpa);1412 wzc.instructions.deinit(wzc.gpa);
1403 wzc.extra.deinit(wzc.gpa);1413 wzc.extra.deinit(wzc.gpa);
...@@ -2290,6 +2300,7 @@ fn astgenAndSemaFn(...@@ -2290,6 +2300,7 @@ fn astgenAndSemaFn(
2290 .decl = decl,2300 .decl = decl,
2291 .arena = &decl_arena.allocator,2301 .arena = &decl_arena.allocator,
2292 .gpa = mod.gpa,2302 .gpa = mod.gpa,
2303 .ref_start_index = @intCast(u32, zir.const_inst_list.len + param_count),
2293 };2304 };
2294 defer wip_zir_code.deinit();2305 defer wip_zir_code.deinit();
22952306
...@@ -3199,6 +3210,9 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {...@@ -3199,6 +3210,9 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {
3199 };3210 };
3200 defer inner_block.instructions.deinit(mod.gpa);3211 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
3202 func.state = .in_progress;3216 func.state = .in_progress;
3203 log.debug("set {s} to in_progress", .{decl.name});3217 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...@@ -218,7 +218,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde
218 // tail call them here.218 // tail call them here.
219 .condbr => return sema.zirCondbr(block, inst),219 .condbr => return sema.zirCondbr(block, inst),
220 .@"break" => return sema.zirBreak(block, inst),220 .@"break" => return sema.zirBreak(block, inst),
221 .break_void_tok => return sema.zirBreakVoidTok(block, inst),221 .break_void_node => return sema.zirBreakVoidNode(block, inst),
222 .break_flat => return sema.code.instructions.items(.data)[inst].un_node.operand,222 .break_flat => return sema.code.instructions.items(.data)[inst].un_node.operand,
223 .compile_error => return sema.zirCompileError(block, inst),223 .compile_error => return sema.zirCompileError(block, inst),
224 .ret_coerce => return sema.zirRetTok(block, inst, true),224 .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...@@ -955,20 +955,18 @@ fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!z
955 const tracy = trace(@src());955 const tracy = trace(@src());
956 defer tracy.end();956 defer tracy.end();
957957
958 const bin_inst = sema.code.instructions.items(.data)[inst].bin;958 const inst_data = sema.code.instructions.items(.data)[inst].@"break";
959 const operand = try sema.resolveInst(bin_inst.rhs);959 const operand = try sema.resolveInst(inst_data.operand);
960 const zir_block = bin_inst.lhs;960 return sema.analyzeBreak(block, sema.src, inst_data.block_inst, operand);
961 return sema.analyzeBreak(block, sema.src, zir_block, operand);
962}961}
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 {
965 const tracy = trace(@src());964 const tracy = trace(@src());
966 defer tracy.end();965 defer tracy.end();
967966
968 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;967 const inst_data = sema.code.instructions.items(.data)[inst].break_void_node;
969 const zir_block = inst_data.operand;
970 const void_inst = try sema.mod.constVoid(sema.arena, .unneeded);968 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);
972}970}
973971
974fn analyzeBreak(972fn analyzeBreak(
...@@ -982,7 +980,6 @@ fn analyzeBreak(...@@ -982,7 +980,6 @@ fn analyzeBreak(
982 while (true) {980 while (true) {
983 if (block.label) |*label| {981 if (block.label) |*label| {
984 if (label.zir_block == zir_block) {982 if (label.zir_block == zir_block) {
985 try sema.requireFunctionBlock(block, src);
986 // Here we add a br instruction, but we over-allocate a little bit983 // Here we add a br instruction, but we over-allocate a little bit
987 // (if necessary) to make it possible to convert the instruction into984 // (if necessary) to make it possible to convert the instruction into
988 // a br_block_flat instruction later.985 // a br_block_flat instruction later.
...@@ -1000,7 +997,7 @@ fn analyzeBreak(...@@ -1000,7 +997,7 @@ fn analyzeBreak(
1000 .operand = operand,997 .operand = operand,
1001 .block = label.merges.block_inst,998 .block = label.merges.block_inst,
1002 };999 };
1003 try block.instructions.append(sema.gpa, &br.base);1000 try start_block.instructions.append(sema.gpa, &br.base);
1004 try label.merges.results.append(sema.gpa, operand);1001 try label.merges.results.append(sema.gpa, operand);
1005 try label.merges.br_list.append(sema.gpa, br);1002 try label.merges.br_list.append(sema.gpa, br);
1006 return always_noreturn;1003 return always_noreturn;
...@@ -2613,10 +2610,11 @@ fn zirCmp(...@@ -2613,10 +2610,11 @@ fn zirCmp(
2613 const tracy = trace(@src());2610 const tracy = trace(@src());
2614 defer tracy.end();2611 defer tracy.end();
26152612
2616 const src: LazySrcLoc = .todo;2613 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2617 const bin_inst = sema.code.instructions.items(.data)[inst].bin;2614 const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
2618 const lhs = try sema.resolveInst(bin_inst.lhs);2615 const src: LazySrcLoc = inst_data.src();
2619 const rhs = try sema.resolveInst(bin_inst.rhs);2616 const lhs = try sema.resolveInst(extra.lhs);
2617 const rhs = try sema.resolveInst(extra.rhs);
26202618
2621 const is_equality_cmp = switch (op) {2619 const is_equality_cmp = switch (op) {
2622 .eq, .neq => true,2620 .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...@@ -428,12 +428,12 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In
428 .if_simple => return ifExpr(mod, scope, rl, node, tree.ifSimple(node)),428 .if_simple => return ifExpr(mod, scope, rl, node, tree.ifSimple(node)),
429 .@"if" => return ifExpr(mod, scope, rl, node, tree.ifFull(node)),429 .@"if" => return ifExpr(mod, scope, rl, node, tree.ifFull(node)),
430430
431 .while_simple => return whileExpr(mod, scope, rl, tree.whileSimple(node)),431 .while_simple => return whileExpr(mod, scope, rl, node, tree.whileSimple(node)),
432 .while_cont => return whileExpr(mod, scope, rl, tree.whileCont(node)),432 .while_cont => return whileExpr(mod, scope, rl, node, tree.whileCont(node)),
433 .@"while" => return whileExpr(mod, scope, rl, tree.whileFull(node)),433 .@"while" => return whileExpr(mod, scope, rl, node, tree.whileFull(node)),
434434
435 .for_simple => return forExpr(mod, scope, rl, tree.forSimple(node)),435 .for_simple => return forExpr(mod, scope, rl, node, tree.forSimple(node)),
436 .@"for" => return forExpr(mod, scope, rl, tree.forFull(node)),436 .@"for" => return forExpr(mod, scope, rl, node, tree.forFull(node)),
437437
438 // TODO handling these separately would actually be simpler & have fewer branches438 // TODO handling these separately would actually be simpler & have fewer branches
439 // once we have a ZIR instruction for each of these 3 cases.439 // once we have a ZIR instruction for each of these 3 cases.
...@@ -956,7 +956,7 @@ fn labeledBlockExpr(...@@ -956,7 +956,7 @@ fn labeledBlockExpr(
956 // The code took advantage of the result location as a pointer.956 // The code took advantage of the result location as a pointer.
957 // Turn the break instruction operands into void.957 // Turn the break instruction operands into void.
958 for (block_scope.labeled_breaks.items) |br| {958 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);
960 }960 }
961 // TODO technically not needed since we changed the tag to break_void but961 // TODO technically not needed since we changed the tag to break_void but
962 // would be better still to elide the ones that are in this list.962 // would be better still to elide the ones that are in this list.
...@@ -1169,7 +1169,7 @@ fn blockExprStmts(...@@ -1169,7 +1169,7 @@ fn blockExprStmts(
1169 .compile_log,1169 .compile_log,
1170 .ensure_err_payload_void,1170 .ensure_err_payload_void,
1171 .@"break",1171 .@"break",
1172 .break_void_tok,1172 .break_void_node,
1173 .break_flat,1173 .break_flat,
1174 .condbr,1174 .condbr,
1175 .compile_error,1175 .compile_error,
...@@ -1749,13 +1749,13 @@ fn orelseCatchExpr(...@@ -1749,13 +1749,13 @@ fn orelseCatchExpr(
17491749
1750 return finishThenElseBlock(1750 return finishThenElseBlock(
1751 mod,1751 mod,
1752 scope,
1753 rl,
1754 &block_scope,1752 &block_scope,
1753 rl,
1754 node,
1755 &then_scope,1755 &then_scope,
1756 &else_scope,1756 &else_scope,
1757 &condbr.positionals.then_body,1757 condbr,
1758 &condbr.positionals.else_body,1758 cond,
1759 src,1759 src,
1760 src,1760 src,
1761 then_result,1761 then_result,
...@@ -1767,75 +1767,87 @@ fn orelseCatchExpr(...@@ -1767,75 +1767,87 @@ fn orelseCatchExpr(
17671767
1768fn finishThenElseBlock(1768fn finishThenElseBlock(
1769 mod: *Module,1769 mod: *Module,
1770 parent_scope: *Scope,
1771 rl: ResultLoc,
1772 block_scope: *Scope.GenZir,1770 block_scope: *Scope.GenZir,
1771 rl: ResultLoc,
1772 node: ast.Node.Index,
1773 then_scope: *Scope.GenZir,1773 then_scope: *Scope.GenZir,
1774 else_scope: *Scope.GenZir,1774 else_scope: *Scope.GenZir,
1775 then_body: *zir.Body,1775 condbr: zir.Inst.Index,
1776 else_body: *zir.Body,1776 cond: zir.Inst.Ref,
1777 then_src: usize,1777 then_src: ast.Node.Index,
1778 else_src: usize,1778 else_src: ast.Node.Index,
1779 then_result: zir.Inst.Ref,1779 then_result: zir.Inst.Ref,
1780 else_result: ?*zir.Inst,1780 else_result: zir.Inst.Ref,
1781 main_block: zir.Inst.Ref.Block,1781 main_block: zir.Inst.Index,
1782 then_break_block: zir.Inst.Ref.Block,1782 then_break_block: zir.Inst.Index,
1783) InnerError!zir.Inst.Ref {1783) InnerError!zir.Inst.Ref {
1784 // We now have enough information to decide whether the result instruction should1784 // We now have enough information to decide whether the result instruction should
1785 // be communicated via result location pointer or break instructions.1785 // be communicated via result location pointer or break instructions.
1786 const strat = rlStrategy(rl, block_scope);1786 const strat = rlStrategy(rl, block_scope);
1787 const wzc = block_scope.zir_code;
1787 switch (strat.tag) {1788 switch (strat.tag) {
1788 .break_void => {1789 .break_void => {
1789 if (!then_result.tag.isNoReturn()) {1790 if (!wzc.refIsNoReturn(then_result)) {
1790 _ = try addZirInstTag(mod, &then_scope.base, then_src, .break_void, .{1791 _ = try then_scope.add(.{
1791 .block = then_break_block,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 } },
1792 });1797 });
1793 }1798 }
1794 if (else_result) |inst| {1799 const elide_else = if (else_result != 0) wzc.refIsNoReturn(else_result) else false;
1795 if (!inst.tag.isNoReturn()) {1800 if (!elide_else) {
1796 _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{1801 _ = try else_scope.add(.{
1797 .block = main_block,1802 .tag = .break_void_node,
1798 });1803 .data = .{ .break_void_node = .{
1799 }1804 .src_node = wzc.decl.nodeIndexToRelative(else_src),
1800 } else {1805 .block_inst = main_block,
1801 _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{1806 } },
1802 .block = main_block,
1803 });1807 });
1804 }1808 }
1805 assert(!strat.elide_store_to_block_ptr_instructions);1809 assert(!strat.elide_store_to_block_ptr_instructions);
1806 try then_scope.setBlockBody(then_body);1810 try setCondBrPayload(condbr, cond, then_scope, else_scope);
1807 try else_scope.setBlockBody(else_body);1811 return wzc.ref_start_index + main_block;
1808 return &main_block.base;
1809 },1812 },
1810 .break_operand => {1813 .break_operand => {
1811 if (!then_result.tag.isNoReturn()) {1814 if (!wzc.refIsNoReturn(then_result)) {
1812 _ = try addZirInstTag(mod, &then_scope.base, then_src, .@"break", .{1815 _ = try then_scope.add(.{
1813 .block = then_break_block,1816 .tag = .@"break",
1814 .operand = then_result,1817 .data = .{ .@"break" = .{
1818 .block_inst = then_break_block,
1819 .operand = then_result,
1820 } },
1815 });1821 });
1816 }1822 }
1817 if (else_result) |inst| {1823 if (else_result != 0) {
1818 if (!inst.tag.isNoReturn()) {1824 if (!wzc.refIsNoReturn(else_result)) {
1819 _ = try addZirInstTag(mod, &else_scope.base, else_src, .@"break", .{1825 _ = try else_scope.add(.{
1820 .block = main_block,1826 .tag = .@"break",
1821 .operand = inst,1827 .data = .{ .@"break" = .{
1828 .block_inst = main_block,
1829 .operand = else_result,
1830 } },
1822 });1831 });
1823 }1832 }
1824 } else {1833 } else {
1825 _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{1834 _ = try else_scope.add(.{
1826 .block = main_block,1835 .tag = .break_void_node,
1836 .data = .{ .break_void_node = .{
1837 .src_node = wzc.decl.nodeIndexToRelative(else_src),
1838 .block_inst = main_block,
1839 } },
1827 });1840 });
1828 }1841 }
1829 if (strat.elide_store_to_block_ptr_instructions) {1842 if (strat.elide_store_to_block_ptr_instructions) {
1830 try copyBodyWithElidedStoreBlockPtr(then_body, then_scope.*);1843 try setCondBrPayloadElideBlockStorePtr(condbr, cond, then_scope, else_scope);
1831 try copyBodyWithElidedStoreBlockPtr(else_body, else_scope.*);
1832 } else {1844 } else {
1833 try then_scope.setBlockBody(then_body);1845 try setCondBrPayload(condbr, cond, then_scope, else_scope);
1834 try else_scope.setBlockBody(else_body);
1835 }1846 }
1847 const block_ref = wzc.ref_start_index + main_block;
1836 switch (rl) {1848 switch (rl) {
1837 .ref => return &main_block.base,1849 .ref => return block_ref,
1838 else => return rvalue(mod, parent_scope, rl, &main_block.base),1850 else => return rvalue(mod, &block_scope.base, rl, block_ref, node),
1839 }1851 }
1840 },1852 },
1841 }1853 }
...@@ -1951,18 +1963,18 @@ fn simpleBinOp(...@@ -1951,18 +1963,18 @@ fn simpleBinOp(
1951 mod: *Module,1963 mod: *Module,
1952 scope: *Scope,1964 scope: *Scope,
1953 rl: ResultLoc,1965 rl: ResultLoc,
1954 infix_node: ast.Node.Index,1966 node: ast.Node.Index,
1955 op_inst_tag: zir.Inst.Tag,1967 op_inst_tag: zir.Inst.Tag,
1956) InnerError!zir.Inst.Ref {1968) InnerError!zir.Inst.Ref {
1957 const tree = scope.tree();1969 const gz = scope.getGenZir();
1970 const tree = gz.tree();
1958 const node_datas = tree.nodes.items(.data);1971 const node_datas = tree.nodes.items(.data);
19591972
1960 const gz = scope.getGenZir();1973 const result = try gz.addPlNode(op_inst_tag, node, zir.Inst.Bin{
1961 const result = try gz.addPlNode(op_inst_tag, infix_node, zir.Inst.Bin{1974 .lhs = try expr(mod, scope, .none, node_datas[node].lhs),
1962 .lhs = try expr(mod, scope, .none, node_datas[infix_node].lhs),1975 .rhs = try expr(mod, scope, .none, node_datas[node].rhs),
1963 .rhs = try expr(mod, scope, .none, node_datas[infix_node].rhs),
1964 });1976 });
1965 return rvalue(mod, scope, rl, result, infix_node);1977 return rvalue(mod, scope, rl, result, node);
1966}1978}
19671979
1968fn boolBinOp(1980fn boolBinOp(
...@@ -2000,7 +2012,6 @@ fn ifExpr(...@@ -2000,7 +2012,6 @@ fn ifExpr(
2000 node: ast.Node.Index,2012 node: ast.Node.Index,
2001 if_full: ast.full.If,2013 if_full: ast.full.If,
2002) InnerError!zir.Inst.Ref {2014) InnerError!zir.Inst.Ref {
2003 if (true) @panic("TODO update for zir-memory-layout");
2004 const parent_gz = scope.getGenZir();2015 const parent_gz = scope.getGenZir();
2005 var block_scope: Scope.GenZir = .{2016 var block_scope: Scope.GenZir = .{
2006 .parent = scope,2017 .parent = scope,
...@@ -2011,8 +2022,6 @@ fn ifExpr(...@@ -2011,8 +2022,6 @@ fn ifExpr(
2011 setBlockResultLoc(&block_scope, rl);2022 setBlockResultLoc(&block_scope, rl);
2012 defer block_scope.instructions.deinit(mod.gpa);2023 defer block_scope.instructions.deinit(mod.gpa);
20132024
2014 const tree = parent_gz.tree();
2015
2016 const cond = c: {2025 const cond = c: {
2017 // TODO https://github.com/ziglang/zig/issues/79292026 // TODO https://github.com/ziglang/zig/issues/7929
2018 if (if_full.error_token) |error_token| {2027 if (if_full.error_token) |error_token| {
...@@ -2031,11 +2040,9 @@ fn ifExpr(...@@ -2031,11 +2040,9 @@ fn ifExpr(
2031 try parent_gz.instructions.append(mod.gpa, block);2040 try parent_gz.instructions.append(mod.gpa, block);
2032 try block_scope.setBlockBody(block);2041 try block_scope.setBlockBody(block);
20332042
2034 const then_src = token_starts[tree.lastToken(if_full.ast.then_expr)];
2035 var then_scope: Scope.GenZir = .{2043 var then_scope: Scope.GenZir = .{
2036 .parent = scope,2044 .parent = scope,
2037 .decl = block_scope.decl,2045 .zir_code = parent_gz.zir_code,
2038 .arena = block_scope.arena,
2039 .force_comptime = block_scope.force_comptime,2046 .force_comptime = block_scope.force_comptime,
2040 .instructions = .{},2047 .instructions = .{},
2041 };2048 };
...@@ -2052,36 +2059,38 @@ fn ifExpr(...@@ -2052,36 +2059,38 @@ fn ifExpr(
20522059
2053 var else_scope: Scope.GenZir = .{2060 var else_scope: Scope.GenZir = .{
2054 .parent = scope,2061 .parent = scope,
2055 .decl = block_scope.decl,2062 .zir_code = parent_gz.zir_code,
2056 .arena = block_scope.arena,
2057 .force_comptime = block_scope.force_comptime,2063 .force_comptime = block_scope.force_comptime,
2058 .instructions = .{},2064 .instructions = .{},
2059 };2065 };
2060 defer else_scope.instructions.deinit(mod.gpa);2066 defer else_scope.instructions.deinit(mod.gpa);
20612067
2062 const else_node = if_full.ast.else_expr;2068 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: {
2064 block_scope.break_count += 1;2073 block_scope.break_count += 1;
2065 const sub_scope = &else_scope.base;2074 const sub_scope = &else_scope.base;
2066 break :blk .{2075 break :blk .{
2067 .src = token_starts[tree.lastToken(else_node)],2076 .src = else_node,
2068 .result = try expr(mod, sub_scope, block_scope.break_result_loc, else_node),2077 .result = try expr(mod, sub_scope, block_scope.break_result_loc, else_node),
2069 };2078 };
2070 } else .{2079 } else .{
2071 .src = token_starts[tree.lastToken(if_full.ast.then_expr)],2080 .src = if_full.ast.then_expr,
2072 .result = null,2081 .result = 0,
2073 };2082 };
20742083
2075 return finishThenElseBlock(2084 return finishThenElseBlock(
2076 mod,2085 mod,
2077 scope,
2078 rl,
2079 &block_scope,2086 &block_scope,
2087 rl,
2088 node,
2080 &then_scope,2089 &then_scope,
2081 &else_scope,2090 &else_scope,
2082 &condbr.positionals.then_body,2091 condbr,
2083 &condbr.positionals.else_body,2092 cond,
2084 then_src,2093 if_full.ast.then_expr,
2085 else_info.src,2094 else_info.src,
2086 then_result,2095 then_result,
2087 else_info.result,2096 else_info.result,
...@@ -2090,25 +2099,63 @@ fn ifExpr(...@@ -2090,25 +2099,63 @@ fn ifExpr(
2090 );2099 );
2091}2100}
20922101
2093/// Expects to find exactly 1 .store_to_block_ptr instruction.2102fn setCondBrPayload(
2094fn copyBodyWithElidedStoreBlockPtr(body: *zir.Body, scope: Module.Scope.GenZir) !void {2103 condbr: zir.Inst.Index,
2095 body.* = .{2104 cond: zir.Inst.Ref,
2096 .instructions = try scope.arena.alloc(zir.Inst.Ref, scope.instructions.items.len - 1),2105 then_scope: *Scope.GenZir,
2097 };2106 else_scope: *Scope.GenZir,
2098 var dst_index: usize = 0;2107) !void {
2099 for (scope.instructions.items) |src_inst| {2108 const wzc = then_scope.zir_code;
2100 if (src_inst.tag != .store_to_block_ptr) {2109
2101 body.instructions[dst_index] = src_inst;2110 try wzc.extra.ensureCapacity(wzc.gpa, wzc.extra.items.len +
2102 dst_index += 1;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 }
2103 }2150 }
2104 }2151 }
2105 assert(dst_index == body.instructions.len);
2106}2152}
21072153
2108fn whileExpr(2154fn whileExpr(
2109 mod: *Module,2155 mod: *Module,
2110 scope: *Scope,2156 scope: *Scope,
2111 rl: ResultLoc,2157 rl: ResultLoc,
2158 node: ast.Node.Index,
2112 while_full: ast.full.While,2159 while_full: ast.full.While,
2113) InnerError!zir.Inst.Ref {2160) InnerError!zir.Inst.Ref {
2114 if (true) @panic("TODO update for zir-memory-layout");2161 if (true) @panic("TODO update for zir-memory-layout");
...@@ -2245,13 +2292,13 @@ fn whileExpr(...@@ -2245,13 +2292,13 @@ fn whileExpr(
2245 }2292 }
2246 return finishThenElseBlock(2293 return finishThenElseBlock(
2247 mod,2294 mod,
2248 scope,
2249 rl,
2250 &loop_scope,2295 &loop_scope,
2296 rl,
2297 node,
2251 &then_scope,2298 &then_scope,
2252 &else_scope,2299 &else_scope,
2253 &condbr.positionals.then_body,2300 condbr,
2254 &condbr.positionals.else_body,2301 cond,
2255 then_src,2302 then_src,
2256 else_info.src,2303 else_info.src,
2257 then_result,2304 then_result,
...@@ -2265,6 +2312,7 @@ fn forExpr(...@@ -2265,6 +2312,7 @@ fn forExpr(
2265 mod: *Module,2312 mod: *Module,
2266 scope: *Scope,2313 scope: *Scope,
2267 rl: ResultLoc,2314 rl: ResultLoc,
2315 node: ast.Node.Index,
2268 for_full: ast.full.While,2316 for_full: ast.full.While,
2269) InnerError!zir.Inst.Ref {2317) InnerError!zir.Inst.Ref {
2270 if (true) @panic("TODO update for zir-memory-layout");2318 if (true) @panic("TODO update for zir-memory-layout");
...@@ -2442,13 +2490,13 @@ fn forExpr(...@@ -2442,13 +2490,13 @@ fn forExpr(
2442 }2490 }
2443 return finishThenElseBlock(2491 return finishThenElseBlock(
2444 mod,2492 mod,
2445 scope,
2446 rl,
2447 &loop_scope,2493 &loop_scope,
2494 rl,
2495 node,
2448 &then_scope,2496 &then_scope,
2449 &else_scope,2497 &else_scope,
2450 &condbr.positionals.then_body,2498 condbr,
2451 &condbr.positionals.else_body,2499 cond,
2452 then_src,2500 then_src,
2453 else_info.src,2501 else_info.src,
2454 then_result,2502 then_result,
src/zir.zig+99-24
...@@ -90,7 +90,7 @@ pub const Code = struct {...@@ -90,7 +90,7 @@ pub const Code = struct {
90 .arena = &arena.allocator,90 .arena = &arena.allocator,
91 .scope = scope,91 .scope = scope,
92 .code = code,92 .code = code,
93 .indent = 4,93 .indent = 2,
94 .param_count = param_count,94 .param_count = param_count,
95 };95 };
9696
...@@ -469,15 +469,13 @@ pub const Inst = struct {...@@ -469,15 +469,13 @@ pub const Inst = struct {
469 /// Uses the `bool_br` union field.469 /// Uses the `bool_br` union field.
470 bool_br_or,470 bool_br_or,
471 /// Return a value from a block.471 /// Return a value from a block.
472 /// Uses the `bin` union field: `lhs` is `Index` to the block (*not* `Ref`!),472 /// Uses the `break` union field.
473 /// `rhs` is operand.
474 /// Uses the source information from previous instruction.473 /// Uses the source information from previous instruction.
475 @"break",474 @"break",
476 /// Same as `break` but has source information in the form of a token, and475 /// Same as `break` but has source information in the form of an AST node, and
477 /// the operand is assumed to be the void value.476 /// the operand is assumed to be the void value.
478 /// Uses the `un_tok` union field.477 /// Uses the `break_void_node` union field.
479 /// Note that the block operand is a `Index`, not `Ref`.478 break_void_node,
480 break_void_tok,
481 /// Return a value from a block. This is a special form that is only valid479 /// Return a value from a block. This is a special form that is only valid
482 /// when there is exactly 1 break from a block (this one). This instruction480 /// when there is exactly 1 break from a block (this one). This instruction
483 /// allows using the return value from `Sema.analyzeBody`. The block is481 /// allows using the return value from `Sema.analyzeBody`. The block is
...@@ -997,7 +995,7 @@ pub const Inst = struct {...@@ -997,7 +995,7 @@ pub const Inst = struct {
997 => false,995 => false,
998996
999 .@"break",997 .@"break",
1000 .break_void_tok,998 .break_void_node,
1001 .break_flat,999 .break_flat,
1002 .condbr,1000 .condbr,
1003 .compile_error,1001 .compile_error,
...@@ -1023,10 +1021,9 @@ pub const Inst = struct {...@@ -1023,10 +1021,9 @@ pub const Inst = struct {
1023 /// This logic is implemented in `Sema.resolveRef`.1021 /// This logic is implemented in `Sema.resolveRef`.
1024 pub const Ref = u32;1022 pub const Ref = u32;
10251023
1026 /// For instructions whose payload fits into 8 bytes, this is used.1024 /// All instructions have an 8-byte payload, which is contained within
1027 /// When an instruction's payload does not fit, bin_op is used, and1025 /// this union. `Tag` determines which union field is active, as well as
1028 /// lhs and rhs refer to `Tag`-specific values, with one of the operands1026 /// how to interpret the data within.
1029 /// used to index into a separate array specific to that instruction.
1030 pub const Data = union {1027 pub const Data = union {
1031 /// Used for unary operators, with an AST node source location.1028 /// Used for unary operators, with an AST node source location.
1032 un_node: struct {1029 un_node: struct {
...@@ -1161,6 +1158,20 @@ pub const Inst = struct {...@@ -1161,6 +1158,20 @@ pub const Inst = struct {
1161 return .{ .node_offset = self.src_node };1158 return .{ .node_offset = self.src_node };
1162 }1159 }
1163 },1160 },
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
1165 // Make sure we don't accidentally add a field to make this union1176 // Make sure we don't accidentally add a field to make this union
1166 // bigger than expected. Note that in Debug builds, Zig is allowed1177 // bigger than expected. Note that in Debug builds, Zig is allowed
...@@ -1368,7 +1379,6 @@ const Writer = struct {...@@ -1368,7 +1379,6 @@ const Writer = struct {
1368 .break_flat,1379 .break_flat,
1369 => try self.writeUnNode(stream, inst),1380 => try self.writeUnNode(stream, inst),
13701381
1371 .break_void_tok,
1372 .is_non_null,1382 .is_non_null,
1373 .is_null,1383 .is_null,
1374 .is_non_null_ptr,1384 .is_non_null_ptr,
...@@ -1394,16 +1404,11 @@ const Writer = struct {...@@ -1394,16 +1404,11 @@ const Writer = struct {
1394 .int => try self.writeInt(stream, inst),1404 .int => try self.writeInt(stream, inst),
1395 .str => try self.writeStr(stream, inst),1405 .str => try self.writeStr(stream, inst),
1396 .elided => try stream.writeAll(")"),1406 .elided => try stream.writeAll(")"),
1407 .break_void_node => try self.writeBreakVoidNode(stream, inst),
13971408
1398 .@"asm",1409 .@"asm",
1399 .asm_volatile,1410 .asm_volatile,
1400 .block,
1401 .block_comptime,
1402 .call,
1403 .call_chkused,
1404 .call_compile_time,
1405 .compile_log,1411 .compile_log,
1406 .condbr,
1407 .elem_ptr_node,1412 .elem_ptr_node,
1408 .elem_val_node,1413 .elem_val_node,
1409 .field_ptr,1414 .field_ptr,
...@@ -1441,6 +1446,17 @@ const Writer = struct {...@@ -1441,6 +1446,17 @@ const Writer = struct {
1441 .xor,1446 .xor,
1442 => try self.writePlNodeBin(stream, inst),1447 => 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
1444 .as_node => try self.writeAs(stream, inst),1460 .as_node => try self.writeAs(stream, inst),
14451461
1446 .breakpoint,1462 .breakpoint,
...@@ -1531,7 +1547,8 @@ const Writer = struct {...@@ -1531,7 +1547,8 @@ const Writer = struct {
1531 inst: Inst.Index,1547 inst: Inst.Index,
1532 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {1548 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1533 const inst_data = self.code.instructions.items(.data)[inst].param_type;1549 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});
1535 }1552 }
15361553
1537 fn writePtrTypeSimple(1554 fn writePtrTypeSimple(
...@@ -1591,6 +1608,53 @@ const Writer = struct {...@@ -1591,6 +1608,53 @@ const Writer = struct {
1591 try self.writeSrc(stream, inst_data.src());1608 try self.writeSrc(stream, inst_data.src());
1592 }1609 }
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
1594 fn writeAs(self: *Writer, stream: anytype, inst: Inst.Index) !void {1658 fn writeAs(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1595 const inst_data = self.code.instructions.items(.data)[inst].pl_node;1659 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1596 const extra = self.code.extraData(Inst.As, inst_data.payload_index).data;1660 const extra = self.code.extraData(Inst.As, inst_data.payload_index).data;
...@@ -1671,6 +1735,13 @@ const Writer = struct {...@@ -1671,6 +1735,13 @@ const Writer = struct {
1671 return self.writeFnTypeCommon(stream, param_types, inst_data.return_type, var_args, cc);1735 return self.writeFnTypeCommon(stream, param_types, inst_data.return_type, var_args, cc);
1672 }1736 }
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
1674 fn writeUnreachable(self: *Writer, stream: anytype, inst: Inst.Index) !void {1745 fn writeUnreachable(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1675 const inst_data = self.code.instructions.items(.data)[inst].@"unreachable";1746 const inst_data = self.code.instructions.items(.data)[inst].@"unreachable";
1676 const safety_str = if (inst_data.safety) "safe" else "unsafe";1747 const safety_str = if (inst_data.safety) "safe" else "unsafe";
...@@ -1686,12 +1757,12 @@ const Writer = struct {...@@ -1686,12 +1757,12 @@ const Writer = struct {
1686 var_args: bool,1757 var_args: bool,
1687 cc: Inst.Ref,1758 cc: Inst.Ref,
1688 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {1759 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1689 try stream.writeAll("(");1760 try stream.writeAll("[");
1690 for (param_types) |param_type, i| {1761 for (param_types) |param_type, i| {
1691 if (i != 0) try stream.writeAll(", ");1762 if (i != 0) try stream.writeAll(", ");
1692 try self.writeInstRef(stream, param_type);1763 try self.writeInstRef(stream, param_type);
1693 }1764 }
1694 try stream.writeAll("), ");1765 try stream.writeAll("], ");
1695 try self.writeInstRef(stream, ret_ty);1766 try self.writeInstRef(stream, ret_ty);
1696 try self.writeOptionalInstRef(stream, ", cc=", cc);1767 try self.writeOptionalInstRef(stream, ", cc=", cc);
1697 try self.writeFlag(stream, ", var_args", var_args);1768 try self.writeFlag(stream, ", var_args", var_args);
...@@ -1707,7 +1778,7 @@ const Writer = struct {...@@ -1707,7 +1778,7 @@ const Writer = struct {
1707 try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)});1778 try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)});
1708 }1779 }
17091780
1710 fn writeInstRef(self: *Writer, stream: anytype, inst: Inst.Index) !void {1781 fn writeInstRef(self: *Writer, stream: anytype, inst: Inst.Ref) !void {
1711 var i: usize = inst;1782 var i: usize = inst;
17121783
1713 if (i < const_inst_list.len) {1784 if (i < const_inst_list.len) {
...@@ -1720,7 +1791,11 @@ const Writer = struct {...@@ -1720,7 +1791,11 @@ const Writer = struct {
1720 }1791 }
1721 i -= self.param_count;1792 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});
1724 }1799 }
17251800
1726 fn writeOptionalInstRef(1801 fn writeOptionalInstRef(