| author | |
| committer | |
| log | 588171c30b34426fbb07645aa2625e989f369eec |
| tree | 254ff4567279c496dd61a4a790c64c64a8d7e763 |
| parent | 06bb360dd296288db33844d682188e33116d7ab6 |
on the break instruction operands. This involves a new TZIR instruction,
br_block_flat, which represents a break instruction where the operand is
the result of a flat block. See the doc comments on the instructions for
more details.
How it works: when adding break instructions in semantic analysis, the
underlying allocation is slightly padded so that it is the size of a
br_block_flat instruction, which allows the break instruction to later
be converted without removing instructions inside the parent body. The
extra type coercion instructions go into the body of the br_block_flat,
and backends are responsible for dispatching the instruction correctly
(it should map to the same function calls for related instructions).5 files changed, 171 insertions(+), 39 deletions(-)
src/Module.zig+25-3| ... | @@ -671,14 +671,36 @@ pub const Scope = struct { | ... | @@ -671,14 +671,36 @@ pub const Scope = struct { |
| 671 | }; | 671 | }; |
| 672 | 672 | ||
| 673 | pub const Merges = struct { | 673 | pub const Merges = struct { |
| 674 | results: ArrayListUnmanaged(*Inst), | ||
| 675 | block_inst: *Inst.Block, | 674 | block_inst: *Inst.Block, |
| 675 | /// Separate array list from break_inst_list so that it can be passed directly | ||
| 676 | /// to resolvePeerTypes. | ||
| 677 | results: ArrayListUnmanaged(*Inst), | ||
| 678 | /// Keeps track of the break instructions so that the operand can be replaced | ||
| 679 | /// if we need to add type coercion at the end of block analysis. | ||
| 680 | /// Same indexes, capacity, length as `results`. | ||
| 681 | br_list: ArrayListUnmanaged(*Inst.Br), | ||
| 676 | }; | 682 | }; |
| 677 | 683 | ||
| 678 | /// For debugging purposes. | 684 | /// For debugging purposes. |
| 679 | pub fn dump(self: *Block, mod: Module) void { | 685 | pub fn dump(self: *Block, mod: Module) void { |
| 680 | zir.dumpBlock(mod, self); | 686 | zir.dumpBlock(mod, self); |
| 681 | } | 687 | } |
| 688 | |||
| 689 | pub fn makeSubBlock(parent: *Block) Block { | ||
| 690 | return .{ | ||
| 691 | .parent = parent, | ||
| 692 | .inst_table = parent.inst_table, | ||
| 693 | .func = parent.func, | ||
| 694 | .owner_decl = parent.owner_decl, | ||
| 695 | .src_decl = parent.src_decl, | ||
| 696 | .instructions = .{}, | ||
| 697 | .arena = parent.arena, | ||
| 698 | .label = null, | ||
| 699 | .inlining = parent.inlining, | ||
| 700 | .is_comptime = parent.is_comptime, | ||
| 701 | .branch_quota = parent.branch_quota, | ||
| 702 | }; | ||
| 703 | } | ||
| 682 | }; | 704 | }; |
| 683 | 705 | ||
| 684 | /// This is a temporary structure, references to it are valid only | 706 | /// This is a temporary structure, references to it are valid only |
| ... | @@ -2107,7 +2129,7 @@ pub fn addBr( | ... | @@ -2107,7 +2129,7 @@ pub fn addBr( |
| 2107 | src: usize, | 2129 | src: usize, |
| 2108 | target_block: *Inst.Block, | 2130 | target_block: *Inst.Block, |
| 2109 | operand: *Inst, | 2131 | operand: *Inst, |
| 2110 | ) !*Inst { | 2132 | ) !*Inst.Br { |
| 2111 | const inst = try scope_block.arena.create(Inst.Br); | 2133 | const inst = try scope_block.arena.create(Inst.Br); |
| 2112 | inst.* = .{ | 2134 | inst.* = .{ |
| 2113 | .base = .{ | 2135 | .base = .{ |
| ... | @@ -2119,7 +2141,7 @@ pub fn addBr( | ... | @@ -2119,7 +2141,7 @@ pub fn addBr( |
| 2119 | .block = target_block, | 2141 | .block = target_block, |
| 2120 | }; | 2142 | }; |
| 2121 | try scope_block.instructions.append(self.gpa, &inst.base); | 2143 | try scope_block.instructions.append(self.gpa, &inst.base); |
| 2122 | return &inst.base; | 2144 | return inst; |
| 2123 | } | 2145 | } |
| 2124 | 2146 | ||
| 2125 | pub fn addCondBr( | 2147 | pub fn addCondBr( |
src/codegen.zig+21-10| ... | @@ -844,6 +844,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -844,6 +844,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 844 | .bit_or => return self.genBitOr(inst.castTag(.bit_or).?), | 844 | .bit_or => return self.genBitOr(inst.castTag(.bit_or).?), |
| 845 | .block => return self.genBlock(inst.castTag(.block).?), | 845 | .block => return self.genBlock(inst.castTag(.block).?), |
| 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 | .breakpoint => return self.genBreakpoint(inst.src), | 848 | .breakpoint => return self.genBreakpoint(inst.src), |
| 848 | .brvoid => return self.genBrVoid(inst.castTag(.brvoid).?), | 849 | .brvoid => return self.genBrVoid(inst.castTag(.brvoid).?), |
| 849 | .bool_and => return self.genBoolOp(inst.castTag(.bool_and).?), | 850 | .bool_and => return self.genBoolOp(inst.castTag(.bool_and).?), |
| ... | @@ -2441,17 +2442,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2441,17 +2442,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2441 | } | 2442 | } |
| 2442 | } | 2443 | } |
| 2443 | 2444 | ||
| 2445 | fn genBrBlockFlat(self: *Self, parent_inst: *ir.Inst.BrBlockFlat) !MCValue { | ||
| 2446 | try self.genBody(parent_inst.body); | ||
| 2447 | const last = parent_inst.body.instructions[parent_inst.body.instructions.len - 1]; | ||
| 2448 | return self.br(parent_inst.base.src, parent_inst.block, last); | ||
| 2449 | } | ||
| 2450 | |||
| 2444 | fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue { | 2451 | fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue { |
| 2445 | if (inst.operand.ty.hasCodeGenBits()) { | 2452 | return self.br(inst.base.src, inst.block, inst.operand); |
| 2446 | const operand = try self.resolveInst(inst.operand); | ||
| 2447 | const block_mcv = @bitCast(MCValue, inst.block.codegen.mcv); | ||
| 2448 | if (block_mcv == .none) { | ||
| 2449 | inst.block.codegen.mcv = @bitCast(AnyMCValue, operand); | ||
| 2450 | } else { | ||
| 2451 | try self.setRegOrMem(inst.base.src, inst.block.base.ty, block_mcv, operand); | ||
| 2452 | } | ||
| 2453 | } | ||
| 2454 | return self.brVoid(inst.base.src, inst.block); | ||
| 2455 | } | 2453 | } |
| 2456 | 2454 | ||
| 2457 | fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue { | 2455 | fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue { |
| ... | @@ -2478,6 +2476,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2478,6 +2476,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2478 | } | 2476 | } |
| 2479 | } | 2477 | } |
| 2480 | 2478 | ||
| 2479 | fn br(self: *Self, src: usize, block: *ir.Inst.Block, operand: *ir.Inst) !MCValue { | ||
| 2480 | if (operand.ty.hasCodeGenBits()) { | ||
| 2481 | const operand_mcv = try self.resolveInst(operand); | ||
| 2482 | const block_mcv = @bitCast(MCValue, block.codegen.mcv); | ||
| 2483 | if (block_mcv == .none) { | ||
| 2484 | block.codegen.mcv = @bitCast(AnyMCValue, operand_mcv); | ||
| 2485 | } else { | ||
| 2486 | try self.setRegOrMem(src, block.base.ty, block_mcv, operand_mcv); | ||
| 2487 | } | ||
| 2488 | } | ||
| 2489 | return self.brVoid(src, block); | ||
| 2490 | } | ||
| 2491 | |||
| 2481 | fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue { | 2492 | fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue { |
| 2482 | // Emit a jump with a relocation. It will be patched up after the block ends. | 2493 | // Emit a jump with a relocation. It will be patched up after the block ends. |
| 2483 | try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1); | 2494 | try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1); |
src/ir.zig+31-1| ... | @@ -61,6 +61,13 @@ pub const Inst = struct { | ... | @@ -61,6 +61,13 @@ pub const Inst = struct { |
| 61 | bit_or, | 61 | bit_or, |
| 62 | block, | 62 | block, |
| 63 | br, | 63 | br, |
| 64 | /// Same as `br` except the operand is a list of instructions to be treated as | ||
| 65 | /// a flat block; that is there is only 1 break instruction from the block, and | ||
| 66 | /// it is implied to be after the last instruction, and the last instruction is | ||
| 67 | /// the break operand. | ||
| 68 | /// This instruction exists for late-stage semantic analysis patch ups, to | ||
| 69 | /// replace one br operand with multiple instructions, without moving anything else around. | ||
| 70 | br_block_flat, | ||
| 64 | breakpoint, | 71 | breakpoint, |
| 65 | brvoid, | 72 | brvoid, |
| 66 | call, | 73 | call, |
| ... | @@ -158,6 +165,7 @@ pub const Inst = struct { | ... | @@ -158,6 +165,7 @@ pub const Inst = struct { |
| 158 | .assembly => Assembly, | 165 | .assembly => Assembly, |
| 159 | .block => Block, | 166 | .block => Block, |
| 160 | .br => Br, | 167 | .br => Br, |
| 168 | .br_block_flat => BrBlockFlat, | ||
| 161 | .brvoid => BrVoid, | 169 | .brvoid => BrVoid, |
| 162 | .call => Call, | 170 | .call => Call, |
| 163 | .condbr => CondBr, | 171 | .condbr => CondBr, |
| ... | @@ -252,6 +260,7 @@ pub const Inst = struct { | ... | @@ -252,6 +260,7 @@ pub const Inst = struct { |
| 252 | return switch (base.tag) { | 260 | return switch (base.tag) { |
| 253 | .br => base.castTag(.br).?.block, | 261 | .br => base.castTag(.br).?.block, |
| 254 | .brvoid => base.castTag(.brvoid).?.block, | 262 | .brvoid => base.castTag(.brvoid).?.block, |
| 263 | .br_block_flat => base.castTag(.br_block_flat).?.block, | ||
| 255 | else => null, | 264 | else => null, |
| 256 | }; | 265 | }; |
| 257 | } | 266 | } |
| ... | @@ -355,6 +364,27 @@ pub const Inst = struct { | ... | @@ -355,6 +364,27 @@ pub const Inst = struct { |
| 355 | } | 364 | } |
| 356 | }; | 365 | }; |
| 357 | 366 | ||
| 367 | pub const convertable_br_size = std.math.max(@sizeOf(BrBlockFlat), @sizeOf(Br)); | ||
| 368 | pub const convertable_br_align = std.math.max(@alignOf(BrBlockFlat), @alignOf(Br)); | ||
| 369 | comptime { | ||
| 370 | assert(@byteOffsetOf(BrBlockFlat, "base") == @byteOffsetOf(Br, "base")); | ||
| 371 | } | ||
| 372 | |||
| 373 | pub const BrBlockFlat = struct { | ||
| 374 | pub const base_tag = Tag.br_block_flat; | ||
| 375 | |||
| 376 | base: Inst, | ||
| 377 | block: *Block, | ||
| 378 | body: Body, | ||
| 379 | |||
| 380 | pub fn operandCount(self: *const BrBlockFlat) usize { | ||
| 381 | return 0; | ||
| 382 | } | ||
| 383 | pub fn getOperand(self: *const BrBlockFlat, index: usize) ?*Inst { | ||
| 384 | return null; | ||
| 385 | } | ||
| 386 | }; | ||
| 387 | |||
| 358 | pub const Br = struct { | 388 | pub const Br = struct { |
| 359 | pub const base_tag = Tag.br; | 389 | pub const base_tag = Tag.br; |
| 360 | 390 | ||
| ... | @@ -363,7 +393,7 @@ pub const Inst = struct { | ... | @@ -363,7 +393,7 @@ pub const Inst = struct { |
| 363 | operand: *Inst, | 393 | operand: *Inst, |
| 364 | 394 | ||
| 365 | pub fn operandCount(self: *const Br) usize { | 395 | pub fn operandCount(self: *const Br) usize { |
| 366 | return 0; | 396 | return 1; |
| 367 | } | 397 | } |
| 368 | pub fn getOperand(self: *const Br, index: usize) ?*Inst { | 398 | pub fn getOperand(self: *const Br, index: usize) ?*Inst { |
| 369 | if (index == 0) | 399 | if (index == 0) |
src/zir.zig+26-2| ... | @@ -1634,6 +1634,12 @@ const DumpTzir = struct { | ... | @@ -1634,6 +1634,12 @@ const DumpTzir = struct { |
| 1634 | try dtz.findConst(br.operand); | 1634 | try dtz.findConst(br.operand); |
| 1635 | }, | 1635 | }, |
| 1636 | 1636 | ||
| 1637 | .br_block_flat => { | ||
| 1638 | const br_block_flat = inst.castTag(.br_block_flat).?; | ||
| 1639 | try dtz.findConst(&br_block_flat.block.base); | ||
| 1640 | try dtz.fetchInstsAndResolveConsts(br_block_flat.body); | ||
| 1641 | }, | ||
| 1642 | |||
| 1637 | .brvoid => { | 1643 | .brvoid => { |
| 1638 | const brvoid = inst.castTag(.brvoid).?; | 1644 | const brvoid = inst.castTag(.brvoid).?; |
| 1639 | try dtz.findConst(&brvoid.block.base); | 1645 | try dtz.findConst(&brvoid.block.base); |
| ... | @@ -1779,6 +1785,24 @@ const DumpTzir = struct { | ... | @@ -1779,6 +1785,24 @@ const DumpTzir = struct { |
| 1779 | } | 1785 | } |
| 1780 | }, | 1786 | }, |
| 1781 | 1787 | ||
| 1788 | .br_block_flat => { | ||
| 1789 | const br_block_flat = inst.castTag(.br_block_flat).?; | ||
| 1790 | const block_kinky = try dtz.writeInst(writer, &br_block_flat.block.base); | ||
| 1791 | if (block_kinky != null) { | ||
| 1792 | try writer.writeAll(", { // Instruction does not dominate all uses!\n"); | ||
| 1793 | } else { | ||
| 1794 | try writer.writeAll(", {\n"); | ||
| 1795 | } | ||
| 1796 | |||
| 1797 | const old_indent = dtz.indent; | ||
| 1798 | dtz.indent += 2; | ||
| 1799 | try dtz.dumpBody(br_block_flat.body, writer); | ||
| 1800 | dtz.indent = old_indent; | ||
| 1801 | |||
| 1802 | try writer.writeByteNTimes(' ', dtz.indent); | ||
| 1803 | try writer.writeAll("})\n"); | ||
| 1804 | }, | ||
| 1805 | |||
| 1782 | .brvoid => { | 1806 | .brvoid => { |
| 1783 | const brvoid = inst.castTag(.brvoid).?; | 1807 | const brvoid = inst.castTag(.brvoid).?; |
| 1784 | const kinky = try dtz.writeInst(writer, &brvoid.block.base); | 1808 | const kinky = try dtz.writeInst(writer, &brvoid.block.base); |
| ... | @@ -1792,7 +1816,7 @@ const DumpTzir = struct { | ... | @@ -1792,7 +1816,7 @@ const DumpTzir = struct { |
| 1792 | .block => { | 1816 | .block => { |
| 1793 | const block = inst.castTag(.block).?; | 1817 | const block = inst.castTag(.block).?; |
| 1794 | 1818 | ||
| 1795 | try writer.writeAll("\n"); | 1819 | try writer.writeAll("{\n"); |
| 1796 | 1820 | ||
| 1797 | const old_indent = dtz.indent; | 1821 | const old_indent = dtz.indent; |
| 1798 | dtz.indent += 2; | 1822 | dtz.indent += 2; |
| ... | @@ -1800,7 +1824,7 @@ const DumpTzir = struct { | ... | @@ -1800,7 +1824,7 @@ const DumpTzir = struct { |
| 1800 | dtz.indent = old_indent; | 1824 | dtz.indent = old_indent; |
| 1801 | 1825 | ||
| 1802 | try writer.writeByteNTimes(' ', dtz.indent); | 1826 | try writer.writeByteNTimes(' ', dtz.indent); |
| 1803 | try writer.writeAll(")\n"); | 1827 | try writer.writeAll("})\n"); |
| 1804 | }, | 1828 | }, |
| 1805 | 1829 | ||
| 1806 | .condbr => { | 1830 | .condbr => { |
src/zir_sema.zig+68-23| ... | @@ -664,20 +664,9 @@ fn zirBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: | ... | @@ -664,20 +664,9 @@ fn zirBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: |
| 664 | defer tracy.end(); | 664 | defer tracy.end(); |
| 665 | const parent_block = scope.cast(Scope.Block).?; | 665 | const parent_block = scope.cast(Scope.Block).?; |
| 666 | 666 | ||
| 667 | var child_block: Scope.Block = .{ | 667 | var child_block = parent_block.makeSubBlock(); |
| 668 | .parent = parent_block, | ||
| 669 | .inst_table = parent_block.inst_table, | ||
| 670 | .func = parent_block.func, | ||
| 671 | .owner_decl = parent_block.owner_decl, | ||
| 672 | .src_decl = parent_block.src_decl, | ||
| 673 | .instructions = .{}, | ||
| 674 | .arena = parent_block.arena, | ||
| 675 | .label = null, | ||
| 676 | .inlining = parent_block.inlining, | ||
| 677 | .is_comptime = parent_block.is_comptime or is_comptime, | ||
| 678 | .branch_quota = parent_block.branch_quota, | ||
| 679 | }; | ||
| 680 | defer child_block.instructions.deinit(mod.gpa); | 668 | defer child_block.instructions.deinit(mod.gpa); |
| 669 | child_block.is_comptime = child_block.is_comptime or is_comptime; | ||
| 681 | 670 | ||
| 682 | try analyzeBody(mod, &child_block, inst.positionals.body); | 671 | try analyzeBody(mod, &child_block, inst.positionals.body); |
| 683 | 672 | ||
| ... | @@ -728,6 +717,7 @@ fn zirBlock( | ... | @@ -728,6 +717,7 @@ fn zirBlock( |
| 728 | .zir_block = inst, | 717 | .zir_block = inst, |
| 729 | .merges = .{ | 718 | .merges = .{ |
| 730 | .results = .{}, | 719 | .results = .{}, |
| 720 | .br_list = .{}, | ||
| 731 | .block_inst = block_inst, | 721 | .block_inst = block_inst, |
| 732 | }, | 722 | }, |
| 733 | }), | 723 | }), |
| ... | @@ -739,6 +729,7 @@ fn zirBlock( | ... | @@ -739,6 +729,7 @@ fn zirBlock( |
| 739 | 729 | ||
| 740 | defer child_block.instructions.deinit(mod.gpa); | 730 | defer child_block.instructions.deinit(mod.gpa); |
| 741 | defer merges.results.deinit(mod.gpa); | 731 | defer merges.results.deinit(mod.gpa); |
| 732 | defer merges.br_list.deinit(mod.gpa); | ||
| 742 | 733 | ||
| 743 | try analyzeBody(mod, &child_block, inst.positionals.body); | 734 | try analyzeBody(mod, &child_block, inst.positionals.body); |
| 744 | 735 | ||
| ... | @@ -772,22 +763,53 @@ fn analyzeBlockBody( | ... | @@ -772,22 +763,53 @@ fn analyzeBlockBody( |
| 772 | const last_inst = child_block.instructions.items[last_inst_index]; | 763 | const last_inst = child_block.instructions.items[last_inst_index]; |
| 773 | if (last_inst.breakBlock()) |br_block| { | 764 | if (last_inst.breakBlock()) |br_block| { |
| 774 | if (br_block == merges.block_inst) { | 765 | if (br_block == merges.block_inst) { |
| 775 | // No need for a block instruction. We can put the new instructions directly into the parent block. | 766 | // No need for a block instruction. We can put the new instructions directly |
| 776 | // Here we omit the break instruction. | 767 | // into the parent block. Here we omit the break instruction. |
| 777 | const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]); | 768 | const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]); |
| 778 | try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); | 769 | try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); |
| 779 | return merges.results.items[0]; | 770 | return merges.results.items[0]; |
| 780 | } | 771 | } |
| 781 | } | 772 | } |
| 782 | } | 773 | } |
| 783 | // It should be impossible to have the number of results be > 1 in a comptime scope. | 774 | // It is impossible to have the number of results be > 1 in a comptime scope. |
| 784 | assert(!child_block.is_comptime); // We should have already got a compile error in the condbr condition. | 775 | assert(!child_block.is_comptime); // Should already got a compile error in the condbr condition. |
| 785 | 776 | ||
| 786 | // Need to set the type and emit the Block instruction. This allows machine code generation | 777 | // Need to set the type and emit the Block instruction. This allows machine code generation |
| 787 | // to emit a jump instruction to after the block when it encounters the break. | 778 | // to emit a jump instruction to after the block when it encounters the break. |
| 788 | try parent_block.instructions.append(mod.gpa, &merges.block_inst.base); | 779 | try parent_block.instructions.append(mod.gpa, &merges.block_inst.base); |
| 789 | merges.block_inst.base.ty = try mod.resolvePeerTypes(scope, merges.results.items); | 780 | const resolved_ty = try mod.resolvePeerTypes(scope, merges.results.items); |
| 790 | merges.block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) }; | 781 | merges.block_inst.base.ty = resolved_ty; |
| 782 | merges.block_inst.body = .{ | ||
| 783 | .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items), | ||
| 784 | }; | ||
| 785 | // Now that the block has its type resolved, we need to go back into all the break | ||
| 786 | // instructions, and insert type coercion on the operands. | ||
| 787 | for (merges.br_list.items) |br| { | ||
| 788 | if (br.operand.ty.eql(resolved_ty)) { | ||
| 789 | // No type coercion needed. | ||
| 790 | continue; | ||
| 791 | } | ||
| 792 | var coerce_block = parent_block.makeSubBlock(); | ||
| 793 | defer coerce_block.instructions.deinit(mod.gpa); | ||
| 794 | const coerced_operand = try mod.coerce(&coerce_block.base, resolved_ty, br.operand); | ||
| 795 | assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand); | ||
| 796 | // Here we depend on the br instruction having been over-allocated (if necessary) | ||
| 797 | // inide analyzeBreak so that it can be converted into a br_block_flat instruction. | ||
| 798 | const br_src = br.base.src; | ||
| 799 | const br_ty = br.base.ty; | ||
| 800 | const br_block_flat = @ptrCast(*Inst.BrBlockFlat, br); | ||
| 801 | br_block_flat.* = .{ | ||
| 802 | .base = .{ | ||
| 803 | .src = br_src, | ||
| 804 | .ty = br_ty, | ||
| 805 | .tag = .br_block_flat, | ||
| 806 | }, | ||
| 807 | .block = merges.block_inst, | ||
| 808 | .body = .{ | ||
| 809 | .instructions = try parent_block.arena.dupe(*Inst, coerce_block.instructions.items), | ||
| 810 | }, | ||
| 811 | }; | ||
| 812 | } | ||
| 791 | return &merges.block_inst.base; | 813 | return &merges.block_inst.base; |
| 792 | } | 814 | } |
| 793 | 815 | ||
| ... | @@ -827,9 +849,28 @@ fn analyzeBreak( | ... | @@ -827,9 +849,28 @@ fn analyzeBreak( |
| 827 | while (opt_block) |block| { | 849 | while (opt_block) |block| { |
| 828 | if (block.label) |*label| { | 850 | if (block.label) |*label| { |
| 829 | if (label.zir_block == zir_block) { | 851 | if (label.zir_block == zir_block) { |
| 830 | try label.merges.results.append(mod.gpa, operand); | ||
| 831 | const b = try mod.requireFunctionBlock(scope, src); | 852 | const b = try mod.requireFunctionBlock(scope, src); |
| 832 | return mod.addBr(b, src, label.merges.block_inst, operand); | 853 | // Here we add a br instruction, but we over-allocate a little bit |
| 854 | // (if necessary) to make it possible to convert the instruction into | ||
| 855 | // a br_block_flat instruction later. | ||
| 856 | const br = @ptrCast(*Inst.Br, try b.arena.alignedAlloc( | ||
| 857 | u8, | ||
| 858 | Inst.convertable_br_align, | ||
| 859 | Inst.convertable_br_size, | ||
| 860 | )); | ||
| 861 | br.* = .{ | ||
| 862 | .base = .{ | ||
| 863 | .tag = .br, | ||
| 864 | .ty = Type.initTag(.noreturn), | ||
| 865 | .src = src, | ||
| 866 | }, | ||
| 867 | .operand = operand, | ||
| 868 | .block = label.merges.block_inst, | ||
| 869 | }; | ||
| 870 | try b.instructions.append(mod.gpa, &br.base); | ||
| 871 | try label.merges.results.append(mod.gpa, operand); | ||
| 872 | try label.merges.br_list.append(mod.gpa, br); | ||
| 873 | return &br.base; | ||
| 833 | } | 874 | } |
| 834 | } | 875 | } |
| 835 | opt_block = block.parent; | 876 | opt_block = block.parent; |
| ... | @@ -980,6 +1021,7 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { | ... | @@ -980,6 +1021,7 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { |
| 980 | .casted_args = casted_args, | 1021 | .casted_args = casted_args, |
| 981 | .merges = .{ | 1022 | .merges = .{ |
| 982 | .results = .{}, | 1023 | .results = .{}, |
| 1024 | .br_list = .{}, | ||
| 983 | .block_inst = block_inst, | 1025 | .block_inst = block_inst, |
| 984 | }, | 1026 | }, |
| 985 | }; | 1027 | }; |
| ... | @@ -1004,6 +1046,7 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { | ... | @@ -1004,6 +1046,7 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { |
| 1004 | 1046 | ||
| 1005 | defer child_block.instructions.deinit(mod.gpa); | 1047 | defer child_block.instructions.deinit(mod.gpa); |
| 1006 | defer merges.results.deinit(mod.gpa); | 1048 | defer merges.results.deinit(mod.gpa); |
| 1049 | defer merges.br_list.deinit(mod.gpa); | ||
| 1007 | 1050 | ||
| 1008 | try mod.emitBackwardBranch(&child_block, inst.base.src); | 1051 | try mod.emitBackwardBranch(&child_block, inst.base.src); |
| 1009 | 1052 | ||
| ... | @@ -2194,7 +2237,8 @@ fn zirReturn(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst | ... | @@ -2194,7 +2237,8 @@ fn zirReturn(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst |
| 2194 | if (b.inlining) |inlining| { | 2237 | if (b.inlining) |inlining| { |
| 2195 | // We are inlining a function call; rewrite the `ret` as a `break`. | 2238 | // We are inlining a function call; rewrite the `ret` as a `break`. |
| 2196 | try inlining.merges.results.append(mod.gpa, operand); | 2239 | try inlining.merges.results.append(mod.gpa, operand); |
| 2197 | return mod.addBr(b, inst.base.src, inlining.merges.block_inst, operand); | 2240 | const br = try mod.addBr(b, inst.base.src, inlining.merges.block_inst, operand); |
| 2241 | return &br.base; | ||
| 2198 | } | 2242 | } |
| 2199 | 2243 | ||
| 2200 | return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand); | 2244 | return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand); |
| ... | @@ -2208,7 +2252,8 @@ fn zirReturnVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!* | ... | @@ -2208,7 +2252,8 @@ fn zirReturnVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!* |
| 2208 | // We are inlining a function call; rewrite the `retvoid` as a `breakvoid`. | 2252 | // We are inlining a function call; rewrite the `retvoid` as a `breakvoid`. |
| 2209 | const void_inst = try mod.constVoid(scope, inst.base.src); | 2253 | const void_inst = try mod.constVoid(scope, inst.base.src); |
| 2210 | try inlining.merges.results.append(mod.gpa, void_inst); | 2254 | try inlining.merges.results.append(mod.gpa, void_inst); |
| 2211 | return mod.addBr(b, inst.base.src, inlining.merges.block_inst, void_inst); | 2255 | const br = try mod.addBr(b, inst.base.src, inlining.merges.block_inst, void_inst); |
| 2256 | return &br.base; | ||
| 2212 | } | 2257 | } |
| 2213 | 2258 | ||
| 2214 | if (b.func) |func| { | 2259 | if (b.func) |func| { |