authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-24 20:23:37-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-31 21:09:22-07:00
log6c8985fceeeb6314143691570cf0c7a42521e590
treea9fb25c5b54b4c2664606d668f247dd863a80bc6
parent588171c30b34426fbb07645aa2625e989f369eec

astgen: rework labeled blocks


6 files changed, 200 insertions(+), 112 deletions(-)

src/Module.zig+17-6
...@@ -717,7 +717,7 @@ pub const Scope = struct {...@@ -717,7 +717,7 @@ pub const Scope = struct {
717 label: ?Label = null,717 label: ?Label = null,
718 break_block: ?*zir.Inst.Block = null,718 break_block: ?*zir.Inst.Block = null,
719 continue_block: ?*zir.Inst.Block = null,719 continue_block: ?*zir.Inst.Block = null,
720 /// only valid if label != null or (continue_block and break_block) != null720 /// Only valid when setBlockResultLoc is called.
721 break_result_loc: astgen.ResultLoc = undefined,721 break_result_loc: astgen.ResultLoc = undefined,
722 /// When a block has a pointer result location, here it is.722 /// When a block has a pointer result location, here it is.
723 rl_ptr: ?*zir.Inst = null,723 rl_ptr: ?*zir.Inst = null,
...@@ -726,6 +726,17 @@ pub const Scope = struct {...@@ -726,6 +726,17 @@ pub const Scope = struct {
726 /// whether to rely on break instructions or writing to the result726 /// whether to rely on break instructions or writing to the result
727 /// pointer for the result instruction.727 /// pointer for the result instruction.
728 rvalue_rl_count: usize = 0,728 rvalue_rl_count: usize = 0,
729 /// Keeps track of how many break instructions there are. When astgen is finished
730 /// with a block, it can check this against rvalue_rl_count to find out whether
731 /// the break instructions should be downgraded to break_void.
732 break_count: usize = 0,
733 /// Tracks `break :foo bar` instructions so they can possibly be elided later if
734 /// the labeled block ends up not needing a result location pointer.
735 labeled_breaks: std.ArrayListUnmanaged(*zir.Inst.Break) = .{},
736 /// Tracks `store_to_block_ptr` instructions that correspond to break instructions
737 /// so they can possibly be elided later if the labeled block ends up not needing
738 /// a result location pointer.
739 labeled_store_to_block_ptr_list: std.ArrayListUnmanaged(*zir.Inst.BinOp) = .{},
729740
730 pub const Label = struct {741 pub const Label = struct {
731 token: ast.TokenIndex,742 token: ast.TokenIndex,
...@@ -3495,18 +3506,18 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic...@@ -3495,18 +3506,18 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic
3495 };3506 };
34963507
3497 const ok_body: ir.Body = .{3508 const ok_body: ir.Body = .{
3498 .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the brvoid.3509 .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the br_void.
3499 };3510 };
3500 const brvoid = try parent_block.arena.create(Inst.BrVoid);3511 const br_void = try parent_block.arena.create(Inst.BrVoid);
3501 brvoid.* = .{3512 br_void.* = .{
3502 .base = .{3513 .base = .{
3503 .tag = .brvoid,3514 .tag = .br_void,
3504 .ty = Type.initTag(.noreturn),3515 .ty = Type.initTag(.noreturn),
3505 .src = ok.src,3516 .src = ok.src,
3506 },3517 },
3507 .block = block_inst,3518 .block = block_inst,
3508 };3519 };
3509 ok_body.instructions[0] = &brvoid.base;3520 ok_body.instructions[0] = &br_void.base;
35103521
3511 var fail_block: Scope.Block = .{3522 var fail_block: Scope.Block = .{
3512 .parent = parent_block,3523 .parent = parent_block,
src/astgen.zig+160-89
...@@ -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};
4257
43pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst {58pub 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}
350365
351fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {366fn 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;
357373
...@@ -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 };
379395
380 if (node.getRHS()) |rhs| {396 const rhs = node.getRHS() orelse {
381 // Most result location types can be forwarded directly; however397 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);
549574
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 }
554580
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);
557582
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}
560614
561fn blockExprStmts(mod: *Module, parent_scope: *Scope, node: *ast.Node, statements: []*ast.Node) !void {615fn 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();
563622
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);
16701728
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 });
16841744
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_scope1754 // 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);
17231756
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/else1759 // 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_ptr1760 // 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_scope1775 // 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);
17431777
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 };
17511785
1752 // We now have enough information to decide whether the result instruction should1786 // 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 the1789 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}
33783383
3384fn 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
3419fn 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
3379pub fn addZirInstTag(3450pub fn addZirInstTag(
3380 mod: *Module,3451 mod: *Module,
3381 scope: *Scope,3452 scope: *Scope,
src/codegen.zig+5-5
...@@ -846,7 +846,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -846,7 +846,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
846 .br => return self.genBr(inst.castTag(.br).?),846 .br => return self.genBr(inst.castTag(.br).?),
847 .br_block_flat => return self.genBrBlockFlat(inst.castTag(.br_block_flat).?),847 .br_block_flat => return self.genBrBlockFlat(inst.castTag(.br_block_flat).?),
848 .breakpoint => return self.genBreakpoint(inst.src),848 .breakpoint => return self.genBreakpoint(inst.src),
849 .brvoid => return self.genBrVoid(inst.castTag(.brvoid).?),849 .br_void => return self.genBrVoid(inst.castTag(.br_void).?),
850 .bool_and => return self.genBoolOp(inst.castTag(.bool_and).?),850 .bool_and => return self.genBoolOp(inst.castTag(.bool_and).?),
851 .bool_or => return self.genBoolOp(inst.castTag(.bool_or).?),851 .bool_or => return self.genBoolOp(inst.castTag(.bool_or).?),
852 .call => return self.genCall(inst.castTag(.call).?),852 .call => return self.genCall(inst.castTag(.call).?),
...@@ -2442,10 +2442,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2442,10 +2442,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2442 }2442 }
2443 }2443 }
24442444
2445 fn genBrBlockFlat(self: *Self, parent_inst: *ir.Inst.BrBlockFlat) !MCValue {2445 fn genBrBlockFlat(self: *Self, inst: *ir.Inst.BrBlockFlat) !MCValue {
2446 try self.genBody(parent_inst.body);2446 try self.genBody(inst.body);
2447 const last = parent_inst.body.instructions[parent_inst.body.instructions.len - 1];2447 const last = inst.body.instructions[inst.body.instructions.len - 1];
2448 return self.br(parent_inst.base.src, parent_inst.block, last);2448 return self.br(inst.base.src, inst.block, last);
2449 }2449 }
24502450
2451 fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {2451 fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {
src/ir.zig+4-4
...@@ -69,7 +69,7 @@ pub const Inst = struct {...@@ -69,7 +69,7 @@ pub const Inst = struct {
69 /// replace one br operand with multiple instructions, without moving anything else around.69 /// replace one br operand with multiple instructions, without moving anything else around.
70 br_block_flat,70 br_block_flat,
71 breakpoint,71 breakpoint,
72 brvoid,72 br_void,
73 call,73 call,
74 cmp_lt,74 cmp_lt,
75 cmp_lte,75 cmp_lte,
...@@ -166,7 +166,7 @@ pub const Inst = struct {...@@ -166,7 +166,7 @@ pub const Inst = struct {
166 .block => Block,166 .block => Block,
167 .br => Br,167 .br => Br,
168 .br_block_flat => BrBlockFlat,168 .br_block_flat => BrBlockFlat,
169 .brvoid => BrVoid,169 .br_void => BrVoid,
170 .call => Call,170 .call => Call,
171 .condbr => CondBr,171 .condbr => CondBr,
172 .constant => Constant,172 .constant => Constant,
...@@ -259,7 +259,7 @@ pub const Inst = struct {...@@ -259,7 +259,7 @@ pub const Inst = struct {
259 pub fn breakBlock(base: *Inst) ?*Block {259 pub fn breakBlock(base: *Inst) ?*Block {
260 return switch (base.tag) {260 return switch (base.tag) {
261 .br => base.castTag(.br).?.block,261 .br => base.castTag(.br).?.block,
262 .brvoid => base.castTag(.brvoid).?.block,262 .br_void => base.castTag(.br_void).?.block,
263 .br_block_flat => base.castTag(.br_block_flat).?.block,263 .br_block_flat => base.castTag(.br_block_flat).?.block,
264 else => null,264 else => null,
265 };265 };
...@@ -403,7 +403,7 @@ pub const Inst = struct {...@@ -403,7 +403,7 @@ pub const Inst = struct {
403 };403 };
404404
405 pub const BrVoid = struct {405 pub const BrVoid = struct {
406 pub const base_tag = Tag.brvoid;406 pub const base_tag = Tag.br_void;
407407
408 base: Inst,408 base: Inst,
409 block: *Block,409 block: *Block,
src/zir.zig+11-8
...@@ -264,8 +264,7 @@ pub const Inst = struct {...@@ -264,8 +264,7 @@ pub const Inst = struct {
264 /// Write a value to a pointer. For loading, see `deref`.264 /// Write a value to a pointer. For loading, see `deref`.
265 store,265 store,
266 /// Same as `store` but the type of the value being stored will be used to infer266 /// Same as `store` but the type of the value being stored will be used to infer
267 /// the block type. The LHS is a block instruction, whose result location is267 /// the block type. The LHS is the pointer to store to.
268 /// being stored to.
269 store_to_block_ptr,268 store_to_block_ptr,
270 /// Same as `store` but the type of the value being stored will be used to infer269 /// Same as `store` but the type of the value being stored will be used to infer
271 /// the pointer type.270 /// the pointer type.
...@@ -343,6 +342,8 @@ pub const Inst = struct {...@@ -343,6 +342,8 @@ pub const Inst = struct {
343 /// Only checks that `lhs >= rhs` if they are ints, everything else is342 /// Only checks that `lhs >= rhs` if they are ints, everything else is
344 /// validated by the .switch instruction.343 /// validated by the .switch instruction.
345 switch_range,344 switch_range,
345 /// Does nothing; returns a void value.
346 void_value,
346347
347 pub fn Type(tag: Tag) type {348 pub fn Type(tag: Tag) type {
348 return switch (tag) {349 return switch (tag) {
...@@ -355,6 +356,7 @@ pub const Inst = struct {...@@ -355,6 +356,7 @@ pub const Inst = struct {
355 .ret_type,356 .ret_type,
356 .unreachable_unsafe,357 .unreachable_unsafe,
357 .unreachable_safe,358 .unreachable_safe,
359 .void_value,
358 => NoOp,360 => NoOp,
359361
360 .alloc,362 .alloc,
...@@ -611,6 +613,7 @@ pub const Inst = struct {...@@ -611,6 +613,7 @@ pub const Inst = struct {
611 .enum_type,613 .enum_type,
612 .union_type,614 .union_type,
613 .struct_type,615 .struct_type,
616 .void_value,
614 => false,617 => false,
615618
616 .@"break",619 .@"break",
...@@ -1640,9 +1643,9 @@ const DumpTzir = struct {...@@ -1640,9 +1643,9 @@ const DumpTzir = struct {
1640 try dtz.fetchInstsAndResolveConsts(br_block_flat.body);1643 try dtz.fetchInstsAndResolveConsts(br_block_flat.body);
1641 },1644 },
16421645
1643 .brvoid => {1646 .br_void => {
1644 const brvoid = inst.castTag(.brvoid).?;1647 const br_void = inst.castTag(.br_void).?;
1645 try dtz.findConst(&brvoid.block.base);1648 try dtz.findConst(&br_void.block.base);
1646 },1649 },
16471650
1648 .block => {1651 .block => {
...@@ -1803,9 +1806,9 @@ const DumpTzir = struct {...@@ -1803,9 +1806,9 @@ const DumpTzir = struct {
1803 try writer.writeAll("})\n");1806 try writer.writeAll("})\n");
1804 },1807 },
18051808
1806 .brvoid => {1809 .br_void => {
1807 const brvoid = inst.castTag(.brvoid).?;1810 const br_void = inst.castTag(.br_void).?;
1808 const kinky = try dtz.writeInst(writer, &brvoid.block.base);1811 const kinky = try dtz.writeInst(writer, &br_void.block.base);
1809 if (kinky) |_| {1812 if (kinky) |_| {
1810 try writer.writeAll(") // Instruction does not dominate all uses!\n");1813 try writer.writeAll(") // Instruction does not dominate all uses!\n");
1811 } else {1814 } else {
src/zir_sema.zig+3
...@@ -155,6 +155,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!...@@ -155,6 +155,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
155 .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?),155 .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?),
156 .bool_and => return zirBoolOp(mod, scope, old_inst.castTag(.bool_and).?),156 .bool_and => return zirBoolOp(mod, scope, old_inst.castTag(.bool_and).?),
157 .bool_or => return zirBoolOp(mod, scope, old_inst.castTag(.bool_or).?),157 .bool_or => return zirBoolOp(mod, scope, old_inst.castTag(.bool_or).?),
158 .void_value => return mod.constVoid(scope, old_inst.src),
158159
159 .container_field_named,160 .container_field_named,
160 .container_field_typed,161 .container_field_typed,
...@@ -447,6 +448,8 @@ fn zirStoreToBlockPtr(...@@ -447,6 +448,8 @@ fn zirStoreToBlockPtr(
447 const ptr = try resolveInst(mod, scope, inst.positionals.lhs);448 const ptr = try resolveInst(mod, scope, inst.positionals.lhs);
448 const value = try resolveInst(mod, scope, inst.positionals.rhs);449 const value = try resolveInst(mod, scope, inst.positionals.rhs);
449 const ptr_ty = try mod.simplePtrType(scope, inst.base.src, value.ty, true, .One);450 const ptr_ty = try mod.simplePtrType(scope, inst.base.src, value.ty, true, .One);
451 // TODO detect when this store should be done at compile-time. For example,
452 // if expressions should force it when the condition is compile-time known.
450 const b = try mod.requireRuntimeBlock(scope, inst.base.src);453 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
451 const bitcasted_ptr = try mod.addUnOp(b, inst.base.src, ptr_ty, .bitcast, ptr);454 const bitcasted_ptr = try mod.addUnOp(b, inst.base.src, ptr_ty, .bitcast, ptr);
452 return mod.storePtr(scope, inst.base.src, bitcasted_ptr, value);455 return mod.storePtr(scope, inst.base.src, bitcasted_ptr, value);