| ... | @@ -38,6 +38,21 @@ pub const ResultLoc = union(enum) { | ... | @@ -38,6 +38,21 @@ pub const ResultLoc = union(enum) { |
| 38 | /// is inferred based on peer type resolution for a `zir.Inst.Block`. | 38 | /// is inferred based on peer type resolution for a `zir.Inst.Block`. |
| 39 | /// The result instruction from the expression must be ignored. | 39 | /// The result instruction from the expression must be ignored. |
| 40 | block_ptr: *Module.Scope.GenZIR, | 40 | block_ptr: *Module.Scope.GenZIR, |
| | 41 | |
| | 42 | pub const Strategy = struct { |
| | 43 | elide_store_to_block_ptr_instructions: bool, |
| | 44 | tag: Tag, |
| | 45 | |
| | 46 | pub const Tag = enum { |
| | 47 | /// Both branches will use break_void; result location is used to communicate the |
| | 48 | /// result instruction. |
| | 49 | break_void, |
| | 50 | /// Use break statements to pass the block result value, and call rvalue() at |
| | 51 | /// the end depending on rl. Also elide the store_to_block_ptr instructions |
| | 52 | /// depending on rl. |
| | 53 | break_operand, |
| | 54 | }; |
| | 55 | }; |
| 41 | }; | 56 | }; |
| 42 | | 57 | |
| 43 | pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst { | 58 | pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst { |
| ... | @@ -348,10 +363,11 @@ pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *as | ... | @@ -348,10 +363,11 @@ pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *as |
| 348 | return &block.base; | 363 | return &block.base; |
| 349 | } | 364 | } |
| 350 | | 365 | |
| 351 | fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst { | 366 | fn breakExpr( |
| 352 | if (true) { | 367 | mod: *Module, |
| 353 | @panic("TODO reimplement this"); | 368 | parent_scope: *Scope, |
| 354 | } | 369 | node: *ast.Node.ControlFlowExpression, |
| | 370 | ) InnerError!*zir.Inst { |
| 355 | const tree = parent_scope.tree(); | 371 | const tree = parent_scope.tree(); |
| 356 | const src = tree.token_locs[node.ltoken].start; | 372 | const src = tree.token_locs[node.ltoken].start; |
| 357 | | 373 | |
| ... | @@ -377,25 +393,31 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr | ... | @@ -377,25 +393,31 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr |
| 377 | continue; | 393 | continue; |
| 378 | }; | 394 | }; |
| 379 | | 395 | |
| 380 | if (node.getRHS()) |rhs| { | 396 | const rhs = node.getRHS() orelse { |
| 381 | // Most result location types can be forwarded directly; however | 397 | return addZirInstTag(mod, parent_scope, src, .break_void, .{ |
| 382 | // if we need to write to a pointer which has an inferred type, | | |
| 383 | // proper type inference requires peer type resolution on the block's | | |
| 384 | // break operand expressions. | | |
| 385 | const branch_rl: ResultLoc = switch (gen_zir.break_result_loc) { | | |
| 386 | .discard, .none, .ty, .ptr, .ref => gen_zir.break_result_loc, | | |
| 387 | .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block_inst }, | | |
| 388 | }; | | |
| 389 | const operand = try expr(mod, parent_scope, branch_rl, rhs); | | |
| 390 | return try addZIRInst(mod, parent_scope, src, zir.Inst.Break, .{ | | |
| 391 | .block = block_inst, | 398 | .block = block_inst, |
| 392 | .operand = operand, | 399 | }); |
| 393 | }, .{}); | 400 | }; |
| 394 | } else { | 401 | gen_zir.break_count += 1; |
| 395 | return try addZIRInst(mod, parent_scope, src, zir.Inst.BreakVoid, .{ | 402 | const prev_rvalue_rl_count = gen_zir.rvalue_rl_count; |
| 396 | .block = block_inst, | 403 | const operand = try expr(mod, parent_scope, gen_zir.break_result_loc, rhs); |
| 397 | }, .{}); | 404 | const have_store_to_block = gen_zir.rvalue_rl_count != prev_rvalue_rl_count; |
| | 405 | const br = try addZirInstTag(mod, parent_scope, src, .@"break", .{ |
| | 406 | .block = block_inst, |
| | 407 | .operand = operand, |
| | 408 | }); |
| | 409 | if (gen_zir.break_result_loc == .block_ptr) { |
| | 410 | try gen_zir.labeled_breaks.append(mod.gpa, br.castTag(.@"break").?); |
| | 411 | |
| | 412 | if (have_store_to_block) { |
| | 413 | const inst_list = parent_scope.cast(Scope.GenZIR).?.instructions.items; |
| | 414 | const last_inst = inst_list[inst_list.len - 2]; |
| | 415 | const store_inst = last_inst.castTag(.store_to_block_ptr).?; |
| | 416 | assert(store_inst.positionals.lhs == gen_zir.rl_ptr.?); |
| | 417 | try gen_zir.labeled_store_to_block_ptr_list.append(mod.gpa, store_inst); |
| | 418 | } |
| 398 | } | 419 | } |
| | 420 | return br; |
| 399 | }, | 421 | }, |
| 400 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, | 422 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, |
| 401 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, | 423 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, |
| ... | @@ -538,7 +560,6 @@ fn labeledBlockExpr( | ... | @@ -538,7 +560,6 @@ fn labeledBlockExpr( |
| 538 | .decl = parent_scope.ownerDecl().?, | 560 | .decl = parent_scope.ownerDecl().?, |
| 539 | .arena = gen_zir.arena, | 561 | .arena = gen_zir.arena, |
| 540 | .instructions = .{}, | 562 | .instructions = .{}, |
| 541 | .break_result_loc = rl, | | |
| 542 | // TODO @as here is working around a stage1 miscompilation bug :( | 563 | // TODO @as here is working around a stage1 miscompilation bug :( |
| 543 | .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ | 564 | .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ |
| 544 | .token = block_node.label, | 565 | .token = block_node.label, |
| ... | @@ -546,19 +567,57 @@ fn labeledBlockExpr( | ... | @@ -546,19 +567,57 @@ fn labeledBlockExpr( |
| 546 | }), | 567 | }), |
| 547 | }; | 568 | }; |
| 548 | defer block_scope.instructions.deinit(mod.gpa); | 569 | defer block_scope.instructions.deinit(mod.gpa); |
| | 570 | defer block_scope.labeled_breaks.deinit(mod.gpa); |
| | 571 | defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa); |
| | 572 | |
| | 573 | setBlockResultLoc(&block_scope, rl); |
| 549 | | 574 | |
| 550 | try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements()); | 575 | try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements()); |
| | 576 | |
| 551 | if (!block_scope.label.?.used) { | 577 | if (!block_scope.label.?.used) { |
| 552 | return mod.fail(parent_scope, tree.token_locs[block_node.label].start, "unused block label", .{}); | 578 | return mod.fail(parent_scope, tree.token_locs[block_node.label].start, "unused block label", .{}); |
| 553 | } | 579 | } |
| 554 | | 580 | |
| 555 | block_inst.positionals.body.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items); | | |
| 556 | try gen_zir.instructions.append(mod.gpa, &block_inst.base); | 581 | try gen_zir.instructions.append(mod.gpa, &block_inst.base); |
| 557 | | 582 | |
| 558 | return &block_inst.base; | 583 | const strat = rlStrategy(rl, &block_scope); |
| | 584 | switch (strat.tag) { |
| | 585 | .break_void => { |
| | 586 | // The code took advantage of the result location as a pointer. |
| | 587 | // Turn the break instructions into break_void instructions. |
| | 588 | for (block_scope.labeled_breaks.items) |br| { |
| | 589 | br.base.tag = .break_void; |
| | 590 | } |
| | 591 | // TODO technically not needed since we changed the tag to break_void but |
| | 592 | // would be better still to elide the ones that are in this list. |
| | 593 | try copyBodyNoEliding(&block_inst.positionals.body, block_scope); |
| | 594 | |
| | 595 | return &block_inst.base; |
| | 596 | }, |
| | 597 | .break_operand => { |
| | 598 | // All break operands are values that did not use the result location pointer. |
| | 599 | if (strat.elide_store_to_block_ptr_instructions) { |
| | 600 | for (block_scope.labeled_store_to_block_ptr_list.items) |inst| { |
| | 601 | inst.base.tag = .void_value; |
| | 602 | } |
| | 603 | // TODO technically not needed since we changed the tag to void_value but |
| | 604 | // would be better still to elide the ones that are in this list. |
| | 605 | } |
| | 606 | try copyBodyNoEliding(&block_inst.positionals.body, block_scope); |
| | 607 | switch (rl) { |
| | 608 | .ref => return &block_inst.base, |
| | 609 | else => return rvalue(mod, parent_scope, rl, &block_inst.base), |
| | 610 | } |
| | 611 | }, |
| | 612 | } |
| 559 | } | 613 | } |
| 560 | | 614 | |
| 561 | fn blockExprStmts(mod: *Module, parent_scope: *Scope, node: *ast.Node, statements: []*ast.Node) !void { | 615 | fn blockExprStmts( |
| | 616 | mod: *Module, |
| | 617 | parent_scope: *Scope, |
| | 618 | node: *ast.Node, |
| | 619 | statements: []*ast.Node, |
| | 620 | ) !void { |
| 562 | const tree = parent_scope.tree(); | 621 | const tree = parent_scope.tree(); |
| 563 | | 622 | |
| 564 | var block_arena = std.heap.ArenaAllocator.init(mod.gpa); | 623 | var block_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| ... | @@ -1659,7 +1718,6 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn | ... | @@ -1659,7 +1718,6 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn |
| 1659 | cond_kind = .{ .err_union = null }; | 1718 | cond_kind = .{ .err_union = null }; |
| 1660 | } | 1719 | } |
| 1661 | } | 1720 | } |
| 1662 | const block_branch_count = 2; // then and else | | |
| 1663 | var block_scope: Scope.GenZIR = .{ | 1721 | var block_scope: Scope.GenZIR = .{ |
| 1664 | .parent = scope, | 1722 | .parent = scope, |
| 1665 | .decl = scope.ownerDecl().?, | 1723 | .decl = scope.ownerDecl().?, |
| ... | @@ -1668,6 +1726,8 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn | ... | @@ -1668,6 +1726,8 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn |
| 1668 | }; | 1726 | }; |
| 1669 | defer block_scope.instructions.deinit(mod.gpa); | 1727 | defer block_scope.instructions.deinit(mod.gpa); |
| 1670 | | 1728 | |
| | 1729 | setBlockResultLoc(&block_scope, rl); |
| | 1730 | |
| 1671 | const tree = scope.tree(); | 1731 | const tree = scope.tree(); |
| 1672 | const if_src = tree.token_locs[if_node.if_token].start; | 1732 | const if_src = tree.token_locs[if_node.if_token].start; |
| 1673 | const cond = try cond_kind.cond(mod, &block_scope, if_src, if_node.condition); | 1733 | const cond = try cond_kind.cond(mod, &block_scope, if_src, if_node.condition); |
| ... | @@ -1682,33 +1742,6 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn | ... | @@ -1682,33 +1742,6 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn |
| 1682 | .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), | 1742 | .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), |
| 1683 | }); | 1743 | }); |
| 1684 | | 1744 | |
| 1685 | // Depending on whether the result location is a pointer or value, different | | |
| 1686 | // ZIR needs to be generated. In the former case we rely on storing to the | | |
| 1687 | // pointer to communicate the result, and use breakvoid; in the latter case | | |
| 1688 | // the block break instructions will have the result values. | | |
| 1689 | // One more complication: when the result location is a pointer, we detect | | |
| 1690 | // the scenario where the result location is not consumed. In this case | | |
| 1691 | // we emit ZIR for the block break instructions to have the result values, | | |
| 1692 | // and then rvalue() on that to pass the value to the result location. | | |
| 1693 | const branch_rl: ResultLoc = switch (rl) { | | |
| 1694 | .discard, .none, .ty, .ptr, .ref => rl, | | |
| 1695 | | | |
| 1696 | .inferred_ptr => |ptr| blk: { | | |
| 1697 | block_scope.rl_ptr = &ptr.base; | | |
| 1698 | break :blk .{ .block_ptr = &block_scope }; | | |
| 1699 | }, | | |
| 1700 | | | |
| 1701 | .bitcasted_ptr => |ptr| blk: { | | |
| 1702 | block_scope.rl_ptr = &ptr.base; | | |
| 1703 | break :blk .{ .block_ptr = &block_scope }; | | |
| 1704 | }, | | |
| 1705 | | | |
| 1706 | .block_ptr => |parent_block_scope| blk: { | | |
| 1707 | block_scope.rl_ptr = parent_block_scope.rl_ptr.?; | | |
| 1708 | break :blk .{ .block_ptr = &block_scope }; | | |
| 1709 | }, | | |
| 1710 | }; | | |
| 1711 | | | |
| 1712 | const then_src = tree.token_locs[if_node.body.lastToken()].start; | 1745 | const then_src = tree.token_locs[if_node.body.lastToken()].start; |
| 1713 | var then_scope: Scope.GenZIR = .{ | 1746 | var then_scope: Scope.GenZIR = .{ |
| 1714 | .parent = scope, | 1747 | .parent = scope, |
| ... | @@ -1721,7 +1754,8 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn | ... | @@ -1721,7 +1754,8 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn |
| 1721 | // declare payload to the then_scope | 1754 | // declare payload to the then_scope |
| 1722 | const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, if_node.payload); | 1755 | const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, if_node.payload); |
| 1723 | | 1756 | |
| 1724 | const then_result = try expr(mod, then_sub_scope, branch_rl, if_node.body); | 1757 | block_scope.break_count += 1; |
| | 1758 | const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, if_node.body); |
| 1725 | // We hold off on the break instructions as well as copying the then/else | 1759 | // We hold off on the break instructions as well as copying the then/else |
| 1726 | // instructions into place until we know whether to keep store_to_block_ptr | 1760 | // instructions into place until we know whether to keep store_to_block_ptr |
| 1727 | // instructions or not. | 1761 | // instructions or not. |
| ... | @@ -1741,47 +1775,18 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn | ... | @@ -1741,47 +1775,18 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn |
| 1741 | // declare payload to the then_scope | 1775 | // declare payload to the then_scope |
| 1742 | else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload); | 1776 | else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload); |
| 1743 | | 1777 | |
| 1744 | break :blk try expr(mod, else_sub_scope, branch_rl, else_node.body); | 1778 | block_scope.break_count += 1; |
| | 1779 | break :blk try expr(mod, else_sub_scope, block_scope.break_result_loc, else_node.body); |
| 1745 | } else blk: { | 1780 | } else blk: { |
| 1746 | else_src = tree.token_locs[if_node.lastToken()].start; | 1781 | else_src = tree.token_locs[if_node.lastToken()].start; |
| 1747 | else_sub_scope = &else_scope.base; | 1782 | else_sub_scope = &else_scope.base; |
| 1748 | block_scope.rvalue_rl_count += 1; | | |
| 1749 | break :blk null; | 1783 | break :blk null; |
| 1750 | }; | 1784 | }; |
| 1751 | | 1785 | |
| 1752 | // We now have enough information to decide whether the result instruction should | 1786 | // We now have enough information to decide whether the result instruction should |
| 1753 | // be communicated via result location pointer or break instructions. | 1787 | // be communicated via result location pointer or break instructions. |
| 1754 | const Strategy = enum { | 1788 | const strat = rlStrategy(rl, &block_scope); |
| 1755 | /// Both branches will use break_void; result location is used to communicate the | 1789 | switch (strat.tag) { |
| 1756 | /// result instruction. | | |
| 1757 | break_void, | | |
| 1758 | /// Use break statements to pass the block result value, and call rvalue() at | | |
| 1759 | /// the end depending on rl. Also elide the store_to_block_ptr instructions | | |
| 1760 | /// depending on rl. | | |
| 1761 | break_operand, | | |
| 1762 | }; | | |
| 1763 | var elide_store_to_block_ptr_instructions = false; | | |
| 1764 | const strategy: Strategy = switch (rl) { | | |
| 1765 | // In this branch there will not be any store_to_block_ptr instructions. | | |
| 1766 | .discard, .none, .ty, .ref => .break_operand, | | |
| 1767 | // The pointer got passed through to the sub-expressions, so we will use | | |
| 1768 | // break_void here. | | |
| 1769 | // In this branch there will not be any store_to_block_ptr instructions. | | |
| 1770 | .ptr => .break_void, | | |
| 1771 | .inferred_ptr, .bitcasted_ptr, .block_ptr => blk: { | | |
| 1772 | if (block_scope.rvalue_rl_count == 2) { | | |
| 1773 | // Neither prong of the if consumed the result location, so we can | | |
| 1774 | // use break instructions to create an rvalue. | | |
| 1775 | elide_store_to_block_ptr_instructions = true; | | |
| 1776 | break :blk Strategy.break_operand; | | |
| 1777 | } else { | | |
| 1778 | // Allow the store_to_block_ptr instructions to remain so that | | |
| 1779 | // semantic analysis can turn them into bitcasts. | | |
| 1780 | break :blk Strategy.break_void; | | |
| 1781 | } | | |
| 1782 | }, | | |
| 1783 | }; | | |
| 1784 | switch (strategy) { | | |
| 1785 | .break_void => { | 1790 | .break_void => { |
| 1786 | if (!then_result.tag.isNoReturn()) { | 1791 | if (!then_result.tag.isNoReturn()) { |
| 1787 | _ = try addZirInstTag(mod, then_sub_scope, then_src, .break_void, .{ | 1792 | _ = try addZirInstTag(mod, then_sub_scope, then_src, .break_void, .{ |
| ... | @@ -1799,7 +1804,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn | ... | @@ -1799,7 +1804,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn |
| 1799 | .block = block, | 1804 | .block = block, |
| 1800 | }); | 1805 | }); |
| 1801 | } | 1806 | } |
| 1802 | assert(!elide_store_to_block_ptr_instructions); | 1807 | assert(!strat.elide_store_to_block_ptr_instructions); |
| 1803 | try copyBodyNoEliding(&condbr.positionals.then_body, then_scope); | 1808 | try copyBodyNoEliding(&condbr.positionals.then_body, then_scope); |
| 1804 | try copyBodyNoEliding(&condbr.positionals.else_body, else_scope); | 1809 | try copyBodyNoEliding(&condbr.positionals.else_body, else_scope); |
| 1805 | return &block.base; | 1810 | return &block.base; |
| ... | @@ -1823,7 +1828,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn | ... | @@ -1823,7 +1828,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn |
| 1823 | .block = block, | 1828 | .block = block, |
| 1824 | }); | 1829 | }); |
| 1825 | } | 1830 | } |
| 1826 | if (elide_store_to_block_ptr_instructions) { | 1831 | if (strat.elide_store_to_block_ptr_instructions) { |
| 1827 | try copyBodyWithElidedStoreBlockPtr(&condbr.positionals.then_body, then_scope); | 1832 | try copyBodyWithElidedStoreBlockPtr(&condbr.positionals.then_body, then_scope); |
| 1828 | try copyBodyWithElidedStoreBlockPtr(&condbr.positionals.else_body, else_scope); | 1833 | try copyBodyWithElidedStoreBlockPtr(&condbr.positionals.else_body, else_scope); |
| 1829 | } else { | 1834 | } else { |
| ... | @@ -3376,6 +3381,72 @@ fn rvalueVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, resul | ... | @@ -3376,6 +3381,72 @@ fn rvalueVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, resul |
| 3376 | return rvalue(mod, scope, rl, void_inst); | 3381 | return rvalue(mod, scope, rl, void_inst); |
| 3377 | } | 3382 | } |
| 3378 | | 3383 | |
| | 3384 | fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZIR) ResultLoc.Strategy { |
| | 3385 | var elide_store_to_block_ptr_instructions = false; |
| | 3386 | switch (rl) { |
| | 3387 | // In this branch there will not be any store_to_block_ptr instructions. |
| | 3388 | .discard, .none, .ty, .ref => return .{ |
| | 3389 | .tag = .break_operand, |
| | 3390 | .elide_store_to_block_ptr_instructions = false, |
| | 3391 | }, |
| | 3392 | // The pointer got passed through to the sub-expressions, so we will use |
| | 3393 | // break_void here. |
| | 3394 | // In this branch there will not be any store_to_block_ptr instructions. |
| | 3395 | .ptr => return .{ |
| | 3396 | .tag = .break_void, |
| | 3397 | .elide_store_to_block_ptr_instructions = false, |
| | 3398 | }, |
| | 3399 | .inferred_ptr, .bitcasted_ptr, .block_ptr => { |
| | 3400 | if (block_scope.rvalue_rl_count == block_scope.break_count) { |
| | 3401 | // Neither prong of the if consumed the result location, so we can |
| | 3402 | // use break instructions to create an rvalue. |
| | 3403 | return .{ |
| | 3404 | .tag = .break_operand, |
| | 3405 | .elide_store_to_block_ptr_instructions = true, |
| | 3406 | }; |
| | 3407 | } else { |
| | 3408 | // Allow the store_to_block_ptr instructions to remain so that |
| | 3409 | // semantic analysis can turn them into bitcasts. |
| | 3410 | return .{ |
| | 3411 | .tag = .break_void, |
| | 3412 | .elide_store_to_block_ptr_instructions = false, |
| | 3413 | }; |
| | 3414 | } |
| | 3415 | }, |
| | 3416 | } |
| | 3417 | } |
| | 3418 | |
| | 3419 | fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void { |
| | 3420 | // Depending on whether the result location is a pointer or value, different |
| | 3421 | // ZIR needs to be generated. In the former case we rely on storing to the |
| | 3422 | // pointer to communicate the result, and use breakvoid; in the latter case |
| | 3423 | // the block break instructions will have the result values. |
| | 3424 | // One more complication: when the result location is a pointer, we detect |
| | 3425 | // the scenario where the result location is not consumed. In this case |
| | 3426 | // we emit ZIR for the block break instructions to have the result values, |
| | 3427 | // and then rvalue() on that to pass the value to the result location. |
| | 3428 | switch (parent_rl) { |
| | 3429 | .discard, .none, .ty, .ptr, .ref => { |
| | 3430 | block_scope.break_result_loc = parent_rl; |
| | 3431 | }, |
| | 3432 | |
| | 3433 | .inferred_ptr => |ptr| { |
| | 3434 | block_scope.rl_ptr = &ptr.base; |
| | 3435 | block_scope.break_result_loc = .{ .block_ptr = block_scope }; |
| | 3436 | }, |
| | 3437 | |
| | 3438 | .bitcasted_ptr => |ptr| { |
| | 3439 | block_scope.rl_ptr = &ptr.base; |
| | 3440 | block_scope.break_result_loc = .{ .block_ptr = block_scope }; |
| | 3441 | }, |
| | 3442 | |
| | 3443 | .block_ptr => |parent_block_scope| { |
| | 3444 | block_scope.rl_ptr = parent_block_scope.rl_ptr.?; |
| | 3445 | block_scope.break_result_loc = .{ .block_ptr = block_scope }; |
| | 3446 | }, |
| | 3447 | } |
| | 3448 | } |
| | 3449 | |
| 3379 | pub fn addZirInstTag( | 3450 | pub fn addZirInstTag( |
| 3380 | mod: *Module, | 3451 | mod: *Module, |
| 3381 | scope: *Scope, | 3452 | scope: *Scope, |