| author | |
| committer | |
| log | d24be85be88737db8399b492931647056c547614 |
| tree | e6079e363a2b5bcdc1b100b1149c313b13197f41 |
| parent | 568f333681e6ecf8c60c5bbe04ea1e494d966d48 |
5 files changed, 270 insertions(+), 135 deletions(-)
BRANCH_TODO+1-1| ... | ... | @@ -18,7 +18,6 @@ Merge TODO list: |
| 18 | 18 | |
| 19 | 19 | Performance optimizations to look into: |
| 20 | 20 | * 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 | 21 | * look into not storing the field name of field access as a string in zir |
| 23 | 22 | instructions. or, look into introducing interning to string_bytes (local |
| 24 | 23 | 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 | 30 | function ZIR. |
| 32 | 31 | * enum literals can use small strings |
| 33 | 32 | * 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 | 1307 | /// Note that this returns a `zir.Inst.Index` not a ref. |
| 1308 | 1308 | /// Leaves the `payload_index` field undefined. |
| 1309 | 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 | 1311 | const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); |
| 1311 | 1312 | try gz.zir_code.instructions.append(gz.zir_code.gpa, .{ |
| 1312 | 1313 | .tag = .condbr, |
| ... | ... | @@ -1315,6 +1316,7 @@ pub const Scope = struct { |
| 1315 | 1316 | .payload_index = undefined, |
| 1316 | 1317 | } }, |
| 1317 | 1318 | }); |
| 1319 | gz.instructions.appendAssumeCapacity(new_index); | |
| 1318 | 1320 | return new_index; |
| 1319 | 1321 | } |
| 1320 | 1322 | |
| ... | ... | @@ -1398,6 +1400,14 @@ pub const WipZirCode = struct { |
| 1398 | 1400 | return result; |
| 1399 | 1401 | } |
| 1400 | 1402 | |
| 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 | 1411 | pub fn deinit(wzc: *WipZirCode) void { |
| 1402 | 1412 | wzc.instructions.deinit(wzc.gpa); |
| 1403 | 1413 | wzc.extra.deinit(wzc.gpa); |
| ... | ... | @@ -2290,6 +2300,7 @@ fn astgenAndSemaFn( |
| 2290 | 2300 | .decl = decl, |
| 2291 | 2301 | .arena = &decl_arena.allocator, |
| 2292 | 2302 | .gpa = mod.gpa, |
| 2303 | .ref_start_index = @intCast(u32, zir.const_inst_list.len + param_count), | |
| 2293 | 2304 | }; |
| 2294 | 2305 | defer wip_zir_code.deinit(); |
| 2295 | 2306 | |
| ... | ... | @@ -3199,6 +3210,9 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void { |
| 3199 | 3210 | }; |
| 3200 | 3211 | defer inner_block.instructions.deinit(mod.gpa); |
| 3201 | 3212 | |
| 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 | 3216 | func.state = .in_progress; |
| 3203 | 3217 | log.debug("set {s} to in_progress", .{decl.name}); |
| 3204 | 3218 |
src/Sema.zig+13-15| ... | ... | @@ -218,7 +218,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde |
| 218 | 218 | // tail call them here. |
| 219 | 219 | .condbr => return sema.zirCondbr(block, inst), |
| 220 | 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 | 222 | .break_flat => return sema.code.instructions.items(.data)[inst].un_node.operand, |
| 223 | 223 | .compile_error => return sema.zirCompileError(block, inst), |
| 224 | 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 | 955 | const tracy = trace(@src()); |
| 956 | 956 | defer tracy.end(); |
| 957 | 957 | |
| 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); | |
| 962 | 961 | } |
| 963 | 962 | |
| 964 | fn zirBreakVoidTok(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index { | |
| 963 | fn zirBreakVoidNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index { | |
| 965 | 964 | const tracy = trace(@src()); |
| 966 | 965 | defer tracy.end(); |
| 967 | 966 | |
| 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; | |
| 970 | 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 | } |
| 973 | 971 | |
| 974 | 972 | fn analyzeBreak( |
| ... | ... | @@ -982,7 +980,6 @@ fn analyzeBreak( |
| 982 | 980 | while (true) { |
| 983 | 981 | if (block.label) |*label| { |
| 984 | 982 | if (label.zir_block == zir_block) { |
| 985 | try sema.requireFunctionBlock(block, src); | |
| 986 | 983 | // Here we add a br instruction, but we over-allocate a little bit |
| 987 | 984 | // (if necessary) to make it possible to convert the instruction into |
| 988 | 985 | // a br_block_flat instruction later. |
| ... | ... | @@ -1000,7 +997,7 @@ fn analyzeBreak( |
| 1000 | 997 | .operand = operand, |
| 1001 | 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 | 1001 | try label.merges.results.append(sema.gpa, operand); |
| 1005 | 1002 | try label.merges.br_list.append(sema.gpa, br); |
| 1006 | 1003 | return always_noreturn; |
| ... | ... | @@ -2613,10 +2610,11 @@ fn zirCmp( |
| 2613 | 2610 | const tracy = trace(@src()); |
| 2614 | 2611 | defer tracy.end(); |
| 2615 | 2612 | |
| 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); | |
| 2620 | 2618 | |
| 2621 | 2619 | const is_equality_cmp = switch (op) { |
| 2622 | 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 | 428 | .if_simple => return ifExpr(mod, scope, rl, node, tree.ifSimple(node)), |
| 429 | 429 | .@"if" => return ifExpr(mod, scope, rl, node, tree.ifFull(node)), |
| 430 | 430 | |
| 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)), | |
| 434 | 434 | |
| 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)), | |
| 437 | 437 | |
| 438 | 438 | // TODO handling these separately would actually be simpler & have fewer branches |
| 439 | 439 | // once we have a ZIR instruction for each of these 3 cases. |
| ... | ... | @@ -956,7 +956,7 @@ fn labeledBlockExpr( |
| 956 | 956 | // The code took advantage of the result location as a pointer. |
| 957 | 957 | // Turn the break instruction operands into void. |
| 958 | 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 | 961 | // TODO technically not needed since we changed the tag to break_void but |
| 962 | 962 | // would be better still to elide the ones that are in this list. |
| ... | ... | @@ -1169,7 +1169,7 @@ fn blockExprStmts( |
| 1169 | 1169 | .compile_log, |
| 1170 | 1170 | .ensure_err_payload_void, |
| 1171 | 1171 | .@"break", |
| 1172 | .break_void_tok, | |
| 1172 | .break_void_node, | |
| 1173 | 1173 | .break_flat, |
| 1174 | 1174 | .condbr, |
| 1175 | 1175 | .compile_error, |
| ... | ... | @@ -1749,13 +1749,13 @@ fn orelseCatchExpr( |
| 1749 | 1749 | |
| 1750 | 1750 | return finishThenElseBlock( |
| 1751 | 1751 | mod, |
| 1752 | scope, | |
| 1753 | rl, | |
| 1754 | 1752 | &block_scope, |
| 1753 | rl, | |
| 1754 | node, | |
| 1755 | 1755 | &then_scope, |
| 1756 | 1756 | &else_scope, |
| 1757 | &condbr.positionals.then_body, | |
| 1758 | &condbr.positionals.else_body, | |
| 1757 | condbr, | |
| 1758 | cond, | |
| 1759 | 1759 | src, |
| 1760 | 1760 | src, |
| 1761 | 1761 | then_result, |
| ... | ... | @@ -1767,75 +1767,87 @@ fn orelseCatchExpr( |
| 1767 | 1767 | |
| 1768 | 1768 | fn finishThenElseBlock( |
| 1769 | 1769 | mod: *Module, |
| 1770 | parent_scope: *Scope, | |
| 1771 | rl: ResultLoc, | |
| 1772 | 1770 | block_scope: *Scope.GenZir, |
| 1771 | rl: ResultLoc, | |
| 1772 | node: ast.Node.Index, | |
| 1773 | 1773 | then_scope: *Scope.GenZir, |
| 1774 | 1774 | 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, | |
| 1779 | 1779 | 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, | |
| 1783 | 1783 | ) InnerError!zir.Inst.Ref { |
| 1784 | 1784 | // We now have enough information to decide whether the result instruction should |
| 1785 | 1785 | // be communicated via result location pointer or break instructions. |
| 1786 | 1786 | const strat = rlStrategy(rl, block_scope); |
| 1787 | const wzc = block_scope.zir_code; | |
| 1787 | 1788 | switch (strat.tag) { |
| 1788 | 1789 | .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 | } }, | |
| 1792 | 1797 | }); |
| 1793 | 1798 | } |
| 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 | } }, | |
| 1803 | 1807 | }); |
| 1804 | 1808 | } |
| 1805 | 1809 | 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; | |
| 1809 | 1812 | }, |
| 1810 | 1813 | .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 | } }, | |
| 1815 | 1821 | }); |
| 1816 | 1822 | } |
| 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 | } }, | |
| 1822 | 1831 | }); |
| 1823 | 1832 | } |
| 1824 | 1833 | } 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 | } }, | |
| 1827 | 1840 | }); |
| 1828 | 1841 | } |
| 1829 | 1842 | 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); | |
| 1832 | 1844 | } else { |
| 1833 | try then_scope.setBlockBody(then_body); | |
| 1834 | try else_scope.setBlockBody(else_body); | |
| 1845 | try setCondBrPayload(condbr, cond, then_scope, else_scope); | |
| 1835 | 1846 | } |
| 1847 | const block_ref = wzc.ref_start_index + main_block; | |
| 1836 | 1848 | 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), | |
| 1839 | 1851 | } |
| 1840 | 1852 | }, |
| 1841 | 1853 | } |
| ... | ... | @@ -1951,18 +1963,18 @@ fn simpleBinOp( |
| 1951 | 1963 | mod: *Module, |
| 1952 | 1964 | scope: *Scope, |
| 1953 | 1965 | rl: ResultLoc, |
| 1954 | infix_node: ast.Node.Index, | |
| 1966 | node: ast.Node.Index, | |
| 1955 | 1967 | op_inst_tag: zir.Inst.Tag, |
| 1956 | 1968 | ) InnerError!zir.Inst.Ref { |
| 1957 | const tree = scope.tree(); | |
| 1969 | const gz = scope.getGenZir(); | |
| 1970 | const tree = gz.tree(); | |
| 1958 | 1971 | const node_datas = tree.nodes.items(.data); |
| 1959 | 1972 | |
| 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), | |
| 1964 | 1976 | }); |
| 1965 | return rvalue(mod, scope, rl, result, infix_node); | |
| 1977 | return rvalue(mod, scope, rl, result, node); | |
| 1966 | 1978 | } |
| 1967 | 1979 | |
| 1968 | 1980 | fn boolBinOp( |
| ... | ... | @@ -2000,7 +2012,6 @@ fn ifExpr( |
| 2000 | 2012 | node: ast.Node.Index, |
| 2001 | 2013 | if_full: ast.full.If, |
| 2002 | 2014 | ) InnerError!zir.Inst.Ref { |
| 2003 | if (true) @panic("TODO update for zir-memory-layout"); | |
| 2004 | 2015 | const parent_gz = scope.getGenZir(); |
| 2005 | 2016 | var block_scope: Scope.GenZir = .{ |
| 2006 | 2017 | .parent = scope, |
| ... | ... | @@ -2011,8 +2022,6 @@ fn ifExpr( |
| 2011 | 2022 | setBlockResultLoc(&block_scope, rl); |
| 2012 | 2023 | defer block_scope.instructions.deinit(mod.gpa); |
| 2013 | 2024 | |
| 2014 | const tree = parent_gz.tree(); | |
| 2015 | ||
| 2016 | 2025 | const cond = c: { |
| 2017 | 2026 | // TODO https://github.com/ziglang/zig/issues/7929 |
| 2018 | 2027 | if (if_full.error_token) |error_token| { |
| ... | ... | @@ -2031,11 +2040,9 @@ fn ifExpr( |
| 2031 | 2040 | try parent_gz.instructions.append(mod.gpa, block); |
| 2032 | 2041 | try block_scope.setBlockBody(block); |
| 2033 | 2042 | |
| 2034 | const then_src = token_starts[tree.lastToken(if_full.ast.then_expr)]; | |
| 2035 | 2043 | var then_scope: Scope.GenZir = .{ |
| 2036 | 2044 | .parent = scope, |
| 2037 | .decl = block_scope.decl, | |
| 2038 | .arena = block_scope.arena, | |
| 2045 | .zir_code = parent_gz.zir_code, | |
| 2039 | 2046 | .force_comptime = block_scope.force_comptime, |
| 2040 | 2047 | .instructions = .{}, |
| 2041 | 2048 | }; |
| ... | ... | @@ -2052,36 +2059,38 @@ fn ifExpr( |
| 2052 | 2059 | |
| 2053 | 2060 | var else_scope: Scope.GenZir = .{ |
| 2054 | 2061 | .parent = scope, |
| 2055 | .decl = block_scope.decl, | |
| 2056 | .arena = block_scope.arena, | |
| 2062 | .zir_code = parent_gz.zir_code, | |
| 2057 | 2063 | .force_comptime = block_scope.force_comptime, |
| 2058 | 2064 | .instructions = .{}, |
| 2059 | 2065 | }; |
| 2060 | 2066 | defer else_scope.instructions.deinit(mod.gpa); |
| 2061 | 2067 | |
| 2062 | 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 | 2073 | block_scope.break_count += 1; |
| 2065 | 2074 | const sub_scope = &else_scope.base; |
| 2066 | 2075 | break :blk .{ |
| 2067 | .src = token_starts[tree.lastToken(else_node)], | |
| 2076 | .src = else_node, | |
| 2068 | 2077 | .result = try expr(mod, sub_scope, block_scope.break_result_loc, else_node), |
| 2069 | 2078 | }; |
| 2070 | 2079 | } 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, | |
| 2073 | 2082 | }; |
| 2074 | 2083 | |
| 2075 | 2084 | return finishThenElseBlock( |
| 2076 | 2085 | mod, |
| 2077 | scope, | |
| 2078 | rl, | |
| 2079 | 2086 | &block_scope, |
| 2087 | rl, | |
| 2088 | node, | |
| 2080 | 2089 | &then_scope, |
| 2081 | 2090 | &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, | |
| 2085 | 2094 | else_info.src, |
| 2086 | 2095 | then_result, |
| 2087 | 2096 | else_info.result, |
| ... | ... | @@ -2090,25 +2099,63 @@ fn ifExpr( |
| 2090 | 2099 | ); |
| 2091 | 2100 | } |
| 2092 | 2101 | |
| 2093 | /// Expects to find exactly 1 .store_to_block_ptr instruction. | |
| 2094 | fn 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; | |
| 2102 | fn 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. | |
| 2125 | fn 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 | } |
| 2107 | 2153 | |
| 2108 | 2154 | fn whileExpr( |
| 2109 | 2155 | mod: *Module, |
| 2110 | 2156 | scope: *Scope, |
| 2111 | 2157 | rl: ResultLoc, |
| 2158 | node: ast.Node.Index, | |
| 2112 | 2159 | while_full: ast.full.While, |
| 2113 | 2160 | ) InnerError!zir.Inst.Ref { |
| 2114 | 2161 | if (true) @panic("TODO update for zir-memory-layout"); |
| ... | ... | @@ -2245,13 +2292,13 @@ fn whileExpr( |
| 2245 | 2292 | } |
| 2246 | 2293 | return finishThenElseBlock( |
| 2247 | 2294 | mod, |
| 2248 | scope, | |
| 2249 | rl, | |
| 2250 | 2295 | &loop_scope, |
| 2296 | rl, | |
| 2297 | node, | |
| 2251 | 2298 | &then_scope, |
| 2252 | 2299 | &else_scope, |
| 2253 | &condbr.positionals.then_body, | |
| 2254 | &condbr.positionals.else_body, | |
| 2300 | condbr, | |
| 2301 | cond, | |
| 2255 | 2302 | then_src, |
| 2256 | 2303 | else_info.src, |
| 2257 | 2304 | then_result, |
| ... | ... | @@ -2265,6 +2312,7 @@ fn forExpr( |
| 2265 | 2312 | mod: *Module, |
| 2266 | 2313 | scope: *Scope, |
| 2267 | 2314 | rl: ResultLoc, |
| 2315 | node: ast.Node.Index, | |
| 2268 | 2316 | for_full: ast.full.While, |
| 2269 | 2317 | ) InnerError!zir.Inst.Ref { |
| 2270 | 2318 | if (true) @panic("TODO update for zir-memory-layout"); |
| ... | ... | @@ -2442,13 +2490,13 @@ fn forExpr( |
| 2442 | 2490 | } |
| 2443 | 2491 | return finishThenElseBlock( |
| 2444 | 2492 | mod, |
| 2445 | scope, | |
| 2446 | rl, | |
| 2447 | 2493 | &loop_scope, |
| 2494 | rl, | |
| 2495 | node, | |
| 2448 | 2496 | &then_scope, |
| 2449 | 2497 | &else_scope, |
| 2450 | &condbr.positionals.then_body, | |
| 2451 | &condbr.positionals.else_body, | |
| 2498 | condbr, | |
| 2499 | cond, | |
| 2452 | 2500 | then_src, |
| 2453 | 2501 | else_info.src, |
| 2454 | 2502 | then_result, |
src/zir.zig+99-24| ... | ... | @@ -90,7 +90,7 @@ pub const Code = struct { |
| 90 | 90 | .arena = &arena.allocator, |
| 91 | 91 | .scope = scope, |
| 92 | 92 | .code = code, |
| 93 | .indent = 4, | |
| 93 | .indent = 2, | |
| 94 | 94 | .param_count = param_count, |
| 95 | 95 | }; |
| 96 | 96 | |
| ... | ... | @@ -469,15 +469,13 @@ pub const Inst = struct { |
| 469 | 469 | /// Uses the `bool_br` union field. |
| 470 | 470 | bool_br_or, |
| 471 | 471 | /// 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. | |
| 474 | 473 | /// Uses the source information from previous instruction. |
| 475 | 474 | @"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 | |
| 477 | 476 | /// 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, | |
| 481 | 479 | /// Return a value from a block. This is a special form that is only valid |
| 482 | 480 | /// when there is exactly 1 break from a block (this one). This instruction |
| 483 | 481 | /// allows using the return value from `Sema.analyzeBody`. The block is |
| ... | ... | @@ -997,7 +995,7 @@ pub const Inst = struct { |
| 997 | 995 | => false, |
| 998 | 996 | |
| 999 | 997 | .@"break", |
| 1000 | .break_void_tok, | |
| 998 | .break_void_node, | |
| 1001 | 999 | .break_flat, |
| 1002 | 1000 | .condbr, |
| 1003 | 1001 | .compile_error, |
| ... | ... | @@ -1023,10 +1021,9 @@ pub const Inst = struct { |
| 1023 | 1021 | /// This logic is implemented in `Sema.resolveRef`. |
| 1024 | 1022 | pub const Ref = u32; |
| 1025 | 1023 | |
| 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. | |
| 1030 | 1027 | pub const Data = union { |
| 1031 | 1028 | /// Used for unary operators, with an AST node source location. |
| 1032 | 1029 | un_node: struct { |
| ... | ... | @@ -1161,6 +1158,20 @@ pub const Inst = struct { |
| 1161 | 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 | }, | |
| 1164 | 1175 | |
| 1165 | 1176 | // Make sure we don't accidentally add a field to make this union |
| 1166 | 1177 | // bigger than expected. Note that in Debug builds, Zig is allowed |
| ... | ... | @@ -1368,7 +1379,6 @@ const Writer = struct { |
| 1368 | 1379 | .break_flat, |
| 1369 | 1380 | => try self.writeUnNode(stream, inst), |
| 1370 | 1381 | |
| 1371 | .break_void_tok, | |
| 1372 | 1382 | .is_non_null, |
| 1373 | 1383 | .is_null, |
| 1374 | 1384 | .is_non_null_ptr, |
| ... | ... | @@ -1394,16 +1404,11 @@ const Writer = struct { |
| 1394 | 1404 | .int => try self.writeInt(stream, inst), |
| 1395 | 1405 | .str => try self.writeStr(stream, inst), |
| 1396 | 1406 | .elided => try stream.writeAll(")"), |
| 1407 | .break_void_node => try self.writeBreakVoidNode(stream, inst), | |
| 1397 | 1408 | |
| 1398 | 1409 | .@"asm", |
| 1399 | 1410 | .asm_volatile, |
| 1400 | .block, | |
| 1401 | .block_comptime, | |
| 1402 | .call, | |
| 1403 | .call_chkused, | |
| 1404 | .call_compile_time, | |
| 1405 | 1411 | .compile_log, |
| 1406 | .condbr, | |
| 1407 | 1412 | .elem_ptr_node, |
| 1408 | 1413 | .elem_val_node, |
| 1409 | 1414 | .field_ptr, |
| ... | ... | @@ -1441,6 +1446,17 @@ const Writer = struct { |
| 1441 | 1446 | .xor, |
| 1442 | 1447 | => try self.writePlNodeBin(stream, inst), |
| 1443 | 1448 | |
| 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 | 1460 | .as_node => try self.writeAs(stream, inst), |
| 1445 | 1461 | |
| 1446 | 1462 | .breakpoint, |
| ... | ... | @@ -1531,7 +1547,8 @@ const Writer = struct { |
| 1531 | 1547 | inst: Inst.Index, |
| 1532 | 1548 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { |
| 1533 | 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 | } |
| 1536 | 1553 | |
| 1537 | 1554 | fn writePtrTypeSimple( |
| ... | ... | @@ -1591,6 +1608,53 @@ const Writer = struct { |
| 1591 | 1608 | try self.writeSrc(stream, inst_data.src()); |
| 1592 | 1609 | } |
| 1593 | 1610 | |
| 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 | 1658 | fn writeAs(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 1595 | 1659 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 1596 | 1660 | const extra = self.code.extraData(Inst.As, inst_data.payload_index).data; |
| ... | ... | @@ -1671,6 +1735,13 @@ const Writer = struct { |
| 1671 | 1735 | return self.writeFnTypeCommon(stream, param_types, inst_data.return_type, var_args, cc); |
| 1672 | 1736 | } |
| 1673 | 1737 | |
| 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 | 1745 | fn writeUnreachable(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 1675 | 1746 | const inst_data = self.code.instructions.items(.data)[inst].@"unreachable"; |
| 1676 | 1747 | const safety_str = if (inst_data.safety) "safe" else "unsafe"; |
| ... | ... | @@ -1686,12 +1757,12 @@ const Writer = struct { |
| 1686 | 1757 | var_args: bool, |
| 1687 | 1758 | cc: Inst.Ref, |
| 1688 | 1759 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { |
| 1689 | try stream.writeAll("("); | |
| 1760 | try stream.writeAll("["); | |
| 1690 | 1761 | for (param_types) |param_type, i| { |
| 1691 | 1762 | if (i != 0) try stream.writeAll(", "); |
| 1692 | 1763 | try self.writeInstRef(stream, param_type); |
| 1693 | 1764 | } |
| 1694 | try stream.writeAll("), "); | |
| 1765 | try stream.writeAll("], "); | |
| 1695 | 1766 | try self.writeInstRef(stream, ret_ty); |
| 1696 | 1767 | try self.writeOptionalInstRef(stream, ", cc=", cc); |
| 1697 | 1768 | try self.writeFlag(stream, ", var_args", var_args); |
| ... | ... | @@ -1707,7 +1778,7 @@ const Writer = struct { |
| 1707 | 1778 | try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)}); |
| 1708 | 1779 | } |
| 1709 | 1780 | |
| 1710 | fn writeInstRef(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 1781 | fn writeInstRef(self: *Writer, stream: anytype, inst: Inst.Ref) !void { | |
| 1711 | 1782 | var i: usize = inst; |
| 1712 | 1783 | |
| 1713 | 1784 | if (i < const_inst_list.len) { |
| ... | ... | @@ -1720,7 +1791,11 @@ const Writer = struct { |
| 1720 | 1791 | } |
| 1721 | 1792 | i -= self.param_count; |
| 1722 | 1793 | |
| 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 | } |
| 1725 | 1800 | |
| 1726 | 1801 | fn writeOptionalInstRef( |