| ... | @@ -582,7 +582,7 @@ pub const Result = union(enum) { | ... | @@ -582,7 +582,7 @@ pub const Result = union(enum) { |
| 582 | }; | 582 | }; |
| 583 | | 583 | |
| 584 | /// Hashmap to store generated `WValue` for each `Air.Inst.Ref` | 584 | /// Hashmap to store generated `WValue` for each `Air.Inst.Ref` |
| 585 | pub const ValueTable = std.AutoHashMapUnmanaged(Air.Inst.Ref, WValue); | 585 | pub const ValueTable = std.AutoArrayHashMapUnmanaged(Air.Inst.Ref, WValue); |
| 586 | | 586 | |
| 587 | const Self = @This(); | 587 | const Self = @This(); |
| 588 | | 588 | |
| ... | @@ -598,8 +598,13 @@ liveness: Liveness, | ... | @@ -598,8 +598,13 @@ liveness: Liveness, |
| 598 | gpa: mem.Allocator, | 598 | gpa: mem.Allocator, |
| 599 | debug_output: codegen.DebugInfoOutput, | 599 | debug_output: codegen.DebugInfoOutput, |
| 600 | mod_fn: *const Module.Fn, | 600 | mod_fn: *const Module.Fn, |
| | 601 | /// Contains a list of current branches. |
| | 602 | /// When we return from a branch, the branch will be popped from this list, |
| | 603 | /// which means branches can only contain references from within its own branch, |
| | 604 | /// or a branch higher (lower index) in the tree. |
| | 605 | branches: std.ArrayListUnmanaged(Branch) = .{}, |
| 601 | /// Table to save `WValue`'s generated by an `Air.Inst` | 606 | /// Table to save `WValue`'s generated by an `Air.Inst` |
| 602 | values: ValueTable, | 607 | // values: ValueTable, |
| 603 | /// Mapping from Air.Inst.Index to block ids | 608 | /// Mapping from Air.Inst.Index to block ids |
| 604 | blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct { | 609 | blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct { |
| 605 | label: u32, | 610 | label: u32, |
| ... | @@ -682,7 +687,11 @@ const InnerError = error{ | ... | @@ -682,7 +687,11 @@ const InnerError = error{ |
| 682 | }; | 687 | }; |
| 683 | | 688 | |
| 684 | pub fn deinit(self: *Self) void { | 689 | pub fn deinit(self: *Self) void { |
| 685 | self.values.deinit(self.gpa); | 690 | for (self.branches.items) |*branch| { |
| | 691 | branch.deinit(self.gpa); |
| | 692 | } |
| | 693 | self.branches.deinit(self.gpa); |
| | 694 | // self.values.deinit(self.gpa); |
| 686 | self.blocks.deinit(self.gpa); | 695 | self.blocks.deinit(self.gpa); |
| 687 | self.locals.deinit(self.gpa); | 696 | self.locals.deinit(self.gpa); |
| 688 | self.mir_instructions.deinit(self.gpa); | 697 | self.mir_instructions.deinit(self.gpa); |
| ... | @@ -705,11 +714,21 @@ fn fail(self: *Self, comptime fmt: []const u8, args: anytype) InnerError { | ... | @@ -705,11 +714,21 @@ fn fail(self: *Self, comptime fmt: []const u8, args: anytype) InnerError { |
| 705 | /// Resolves the `WValue` for the given instruction `inst` | 714 | /// Resolves the `WValue` for the given instruction `inst` |
| 706 | /// When the given instruction has a `Value`, it returns a constant instead | 715 | /// When the given instruction has a `Value`, it returns a constant instead |
| 707 | fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue { | 716 | fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue { |
| 708 | const gop = try self.values.getOrPut(self.gpa, ref); | 717 | var branch_index = self.branches.items.len; |
| 709 | if (gop.found_existing) return gop.value_ptr.*; | 718 | while (branch_index > 0) : (branch_index -= 1) { |
| | 719 | const branch = self.branches.items[branch_index - 1]; |
| | 720 | if (branch.values.get(ref)) |value| { |
| | 721 | return value; |
| | 722 | } |
| | 723 | } |
| 710 | | 724 | |
| 711 | // when we did not find an existing instruction, it | 725 | // when we did not find an existing instruction, it |
| 712 | // means we must generate it from a constant. | 726 | // means we must generate it from a constant. |
| | 727 | // We always store constants in the most outer branch as they must never |
| | 728 | // be removed. The most outer branch is always at index 0. |
| | 729 | const gop = try self.branches.items[0].values.getOrPut(self.gpa, ref); |
| | 730 | assert(!gop.found_existing); |
| | 731 | |
| 713 | const val = self.air.value(ref).?; | 732 | const val = self.air.value(ref).?; |
| 714 | const ty = self.air.typeOf(ref); | 733 | const ty = self.air.typeOf(ref); |
| 715 | if (!ty.hasRuntimeBitsIgnoreComptime() and !ty.isInt() and !ty.isError()) { | 734 | if (!ty.hasRuntimeBitsIgnoreComptime() and !ty.isInt() and !ty.isError()) { |
| ... | @@ -745,7 +764,8 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: WValue, operands: []cons | ... | @@ -745,7 +764,8 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: WValue, operands: []cons |
| 745 | // results of `none` can never be referenced. | 764 | // results of `none` can never be referenced. |
| 746 | if (result != .none) { | 765 | if (result != .none) { |
| 747 | assert(result != .stack); // it's illegal to store a stack value as we cannot track its position | 766 | assert(result != .stack); // it's illegal to store a stack value as we cannot track its position |
| 748 | self.values.putAssumeCapacityNoClobber(Air.indexToRef(inst), result); | 767 | const branch = self.currentBranch(); |
| | 768 | branch.values.putAssumeCapacityNoClobber(Air.indexToRef(inst), result); |
| 749 | } | 769 | } |
| 750 | | 770 | |
| 751 | if (builtin.mode == .Debug) { | 771 | if (builtin.mode == .Debug) { |
| ... | @@ -753,6 +773,18 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: WValue, operands: []cons | ... | @@ -753,6 +773,18 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: WValue, operands: []cons |
| 753 | } | 773 | } |
| 754 | } | 774 | } |
| 755 | | 775 | |
| | 776 | const Branch = struct { |
| | 777 | values: ValueTable = .{}, |
| | 778 | |
| | 779 | fn deinit(branch: *Branch, gpa: Allocator) void { |
| | 780 | branch.values.deinit(gpa); |
| | 781 | } |
| | 782 | }; |
| | 783 | |
| | 784 | inline fn currentBranch(self: *Self) *Branch { |
| | 785 | return &self.branches.items[self.branches.items.len - 1]; |
| | 786 | } |
| | 787 | |
| 756 | const BigTomb = struct { | 788 | const BigTomb = struct { |
| 757 | gen: *Self, | 789 | gen: *Self, |
| 758 | inst: Air.Inst.Index, | 790 | inst: Air.Inst.Index, |
| ... | @@ -768,7 +800,7 @@ const BigTomb = struct { | ... | @@ -768,7 +800,7 @@ const BigTomb = struct { |
| 768 | fn finishAir(bt: *BigTomb, result: WValue) void { | 800 | fn finishAir(bt: *BigTomb, result: WValue) void { |
| 769 | assert(result != .stack); | 801 | assert(result != .stack); |
| 770 | if (result != .none) { | 802 | if (result != .none) { |
| 771 | bt.gen.values.putAssumeCapacityNoClobber(Air.indexToRef(bt.inst), result); | 803 | bt.gen.currentBranch().values.putAssumeCapacityNoClobber(Air.indexToRef(bt.inst), result); |
| 772 | } | 804 | } |
| 773 | | 805 | |
| 774 | if (builtin.mode == .Debug) { | 806 | if (builtin.mode == .Debug) { |
| ... | @@ -778,7 +810,7 @@ const BigTomb = struct { | ... | @@ -778,7 +810,7 @@ const BigTomb = struct { |
| 778 | }; | 810 | }; |
| 779 | | 811 | |
| 780 | fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb { | 812 | fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb { |
| 781 | try self.values.ensureUnusedCapacity(self.gpa, @intCast(u32, operand_count + 1)); | 813 | try self.currentBranch().values.ensureUnusedCapacity(self.gpa, @intCast(u32, operand_count + 1)); |
| 782 | return BigTomb{ | 814 | return BigTomb{ |
| 783 | .gen = self, | 815 | .gen = self, |
| 784 | .inst = inst, | 816 | .inst = inst, |
| ... | @@ -789,7 +821,10 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT | ... | @@ -789,7 +821,10 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT |
| 789 | fn processDeath(self: *Self, ref: Air.Inst.Ref) void { | 821 | fn processDeath(self: *Self, ref: Air.Inst.Ref) void { |
| 790 | const inst = Air.refToIndex(ref) orelse return; | 822 | const inst = Air.refToIndex(ref) orelse return; |
| 791 | if (self.air.instructions.items(.tag)[inst] == .constant) return; | 823 | if (self.air.instructions.items(.tag)[inst] == .constant) return; |
| 792 | const value = self.values.getPtr(ref) orelse return; | 824 | // Branches are currently only allowed to free locals allocated |
| | 825 | // within their own branch. |
| | 826 | // TODO: Upon branch consolidation free any locals if needed. |
| | 827 | const value = self.currentBranch().values.getPtr(ref) orelse return; |
| 793 | if (value.* != .local) return; | 828 | if (value.* != .local) return; |
| 794 | std.debug.print("Decreasing reference for ref: %{?d}\n", .{Air.refToIndex(ref)}); | 829 | std.debug.print("Decreasing reference for ref: %{?d}\n", .{Air.refToIndex(ref)}); |
| 795 | value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer | 830 | value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer |
| ... | @@ -924,17 +959,28 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void { | ... | @@ -924,17 +959,28 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void { |
| 924 | /// returns the given `operand` itself instead. | 959 | /// returns the given `operand` itself instead. |
| 925 | fn reuseOperand(self: *Self, ref: Air.Inst.Ref, operand: WValue) WValue { | 960 | fn reuseOperand(self: *Self, ref: Air.Inst.Ref, operand: WValue) WValue { |
| 926 | if (operand != .local and operand != .stack_offset) return operand; | 961 | if (operand != .local and operand != .stack_offset) return operand; |
| 927 | var copy = operand; | 962 | var new_value = operand; |
| 928 | switch (copy) { | 963 | switch (new_value) { |
| 929 | .local => |*local| local.references += 1, | 964 | .local => |*local| local.references += 1, |
| 930 | .stack_offset => |*stack_offset| stack_offset.references += 1, | 965 | .stack_offset => |*stack_offset| stack_offset.references += 1, |
| 931 | else => unreachable, | 966 | else => unreachable, |
| 932 | } | 967 | } |
| 933 | | 968 | const old_value = self.getResolvedInst(ref); |
| 934 | const gop = self.values.getOrPutAssumeCapacity(ref); | 969 | old_value.* = new_value; |
| 935 | assert(gop.found_existing); | 970 | return new_value; |
| 936 | gop.value_ptr.* = copy; | 971 | } |
| 937 | return copy; | 972 | |
| | 973 | /// From a reference, returns its resolved `WValue`. |
| | 974 | /// It's illegal to provide a `Air.Inst.Ref` that hasn't been resolved yet. |
| | 975 | fn getResolvedInst(self: *Self, ref: Air.Inst.Ref) *WValue { |
| | 976 | var index = self.branches.items.len; |
| | 977 | while (index > 0) : (index -= 1) { |
| | 978 | const branch = self.branches.items[index - 1]; |
| | 979 | if (branch.values.getPtr(ref)) |value| { |
| | 980 | return value; |
| | 981 | } |
| | 982 | } |
| | 983 | unreachable; // developer-error: This can only be called on resolved instructions. Use `resolveInst` instead. |
| 938 | } | 984 | } |
| 939 | | 985 | |
| 940 | /// Creates one locals for a given `Type`. | 986 | /// Creates one locals for a given `Type`. |
| ... | @@ -1035,7 +1081,7 @@ pub fn generate( | ... | @@ -1035,7 +1081,7 @@ pub fn generate( |
| 1035 | .gpa = bin_file.allocator, | 1081 | .gpa = bin_file.allocator, |
| 1036 | .air = air, | 1082 | .air = air, |
| 1037 | .liveness = liveness, | 1083 | .liveness = liveness, |
| 1038 | .values = .{}, | 1084 | // .values = .{}, |
| 1039 | .code = code, | 1085 | .code = code, |
| 1040 | .decl_index = func.owner_decl, | 1086 | .decl_index = func.owner_decl, |
| 1041 | .decl = bin_file.options.module.?.declPtr(func.owner_decl), | 1087 | .decl = bin_file.options.module.?.declPtr(func.owner_decl), |
| ... | @@ -1070,8 +1116,13 @@ fn genFunc(self: *Self) InnerError!void { | ... | @@ -1070,8 +1116,13 @@ fn genFunc(self: *Self) InnerError!void { |
| 1070 | | 1116 | |
| 1071 | try self.addTag(.dbg_prologue_end); | 1117 | try self.addTag(.dbg_prologue_end); |
| 1072 | | 1118 | |
| | 1119 | try self.branches.append(self.gpa, .{}); |
| 1073 | // Generate MIR for function body | 1120 | // Generate MIR for function body |
| 1074 | try self.genBody(self.air.getMainBody()); | 1121 | try self.genBody(self.air.getMainBody()); |
| | 1122 | |
| | 1123 | // clean up outer branch |
| | 1124 | _ = self.branches.pop(); |
| | 1125 | |
| 1075 | // In case we have a return value, but the last instruction is a noreturn (such as a while loop) | 1126 | // In case we have a return value, but the last instruction is a noreturn (such as a while loop) |
| 1076 | // we emit an unreachable instruction to tell the stack validator that part will never be reached. | 1127 | // we emit an unreachable instruction to tell the stack validator that part will never be reached. |
| 1077 | if (func_type.returns.len != 0 and self.air.instructions.len > 0) { | 1128 | if (func_type.returns.len != 0 and self.air.instructions.len > 0) { |
| ... | @@ -1837,7 +1888,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) InnerError!void { | ... | @@ -1837,7 +1888,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 1837 | fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | 1888 | fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 1838 | for (body) |inst| { | 1889 | for (body) |inst| { |
| 1839 | const old_bookkeeping_value = self.air_bookkeeping; | 1890 | const old_bookkeeping_value = self.air_bookkeeping; |
| 1840 | try self.values.ensureUnusedCapacity(self.gpa, Liveness.bpi); | 1891 | try self.currentBranch().values.ensureUnusedCapacity(self.gpa, Liveness.bpi); |
| 1841 | try self.genInst(inst); | 1892 | try self.genInst(inst); |
| 1842 | | 1893 | |
| 1843 | if (builtin.mode == .Debug and self.air_bookkeeping < old_bookkeeping_value + 1) { | 1894 | if (builtin.mode == .Debug and self.air_bookkeeping < old_bookkeeping_value + 1) { |
| ... | @@ -2779,7 +2830,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2779,7 +2830,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 2779 | const extra = self.air.extraData(Air.CondBr, pl_op.payload); | 2830 | const extra = self.air.extraData(Air.CondBr, pl_op.payload); |
| 2780 | const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len]; | 2831 | const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len]; |
| 2781 | const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; | 2832 | const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; |
| 2782 | // const liveness_condbr = self.liveness.getCondBr(inst); | 2833 | const liveness_condbr = self.liveness.getCondBr(inst); |
| 2783 | | 2834 | |
| 2784 | // result type is always noreturn, so use `block_empty` as type. | 2835 | // result type is always noreturn, so use `block_empty` as type. |
| 2785 | try self.startBlock(.block, wasm.block_empty); | 2836 | try self.startBlock(.block, wasm.block_empty); |
| ... | @@ -2791,15 +2842,70 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2791,15 +2842,70 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 2791 | // and continue with the then codepath | 2842 | // and continue with the then codepath |
| 2792 | try self.addLabel(.br_if, 0); | 2843 | try self.addLabel(.br_if, 0); |
| 2793 | | 2844 | |
| | 2845 | try self.branches.ensureUnusedCapacity(self.gpa, 2); |
| | 2846 | |
| | 2847 | const else_stack = self.branches.addOneAssumeCapacity(); |
| | 2848 | else_stack.* = .{}; |
| | 2849 | defer else_stack.deinit(self.gpa); |
| | 2850 | |
| | 2851 | try self.currentBranch().values.ensureUnusedCapacity(self.gpa, @intCast(u32, liveness_condbr.else_deaths.len)); |
| | 2852 | for (liveness_condbr.else_deaths) |death| { |
| | 2853 | self.processDeath(Air.indexToRef(death)); |
| | 2854 | std.debug.print("Death inst: %{d}\n", .{death}); |
| | 2855 | } |
| 2794 | try self.genBody(else_body); | 2856 | try self.genBody(else_body); |
| 2795 | try self.endBlock(); | 2857 | try self.endBlock(); |
| | 2858 | else_stack.* = self.branches.pop(); |
| 2796 | | 2859 | |
| 2797 | // Outer block that matches the condition | 2860 | // Outer block that matches the condition |
| | 2861 | const then_stack = self.branches.addOneAssumeCapacity(); |
| | 2862 | then_stack.* = .{}; |
| | 2863 | defer then_stack.deinit(self.gpa); |
| | 2864 | |
| | 2865 | try self.currentBranch().values.ensureUnusedCapacity(self.gpa, @intCast(u32, liveness_condbr.then_deaths.len)); |
| | 2866 | for (liveness_condbr.then_deaths) |death| { |
| | 2867 | self.processDeath(Air.indexToRef(death)); |
| | 2868 | std.debug.print("Death inst: %{d}\n", .{death}); |
| | 2869 | } |
| 2798 | try self.genBody(then_body); | 2870 | try self.genBody(then_body); |
| | 2871 | then_stack.* = self.branches.pop(); |
| | 2872 | |
| | 2873 | try self.canonicaliseBranches(then_stack, else_stack); |
| 2799 | | 2874 | |
| | 2875 | // TODO: Branch consilidation to process deaths from branches |
| 2800 | self.finishAir(inst, .none, &.{}); | 2876 | self.finishAir(inst, .none, &.{}); |
| 2801 | } | 2877 | } |
| 2802 | | 2878 | |
| | 2879 | fn canonicaliseBranches(self: *Self, canon_branch: *Branch, target_branch: *Branch) !void { |
| | 2880 | const parent = self.currentBranch(); |
| | 2881 | |
| | 2882 | const target_slice = target_branch.values.entries.slice(); |
| | 2883 | const target_keys = target_slice.items(.key); |
| | 2884 | const target_values = target_slice.items(.value); |
| | 2885 | |
| | 2886 | try parent.values.ensureUnusedCapacity(self.gpa, target_branch.values.count()); |
| | 2887 | for (target_keys) |key, index| { |
| | 2888 | const value = target_values[index]; |
| | 2889 | const canon_value = if (canon_branch.values.fetchSwapRemove(key)) |canon_entry| { |
| | 2890 | // try parent.values.putAssumeCapacity(key, canon_entry.value); |
| | 2891 | _ = canon_entry; |
| | 2892 | // _ = result_value; |
| | 2893 | @panic("HMMMM THIS occurs"); |
| | 2894 | // break :result_value canon_entry.value; |
| | 2895 | } else value; |
| | 2896 | |
| | 2897 | parent.values.putAssumeCapacity(key, canon_value); |
| | 2898 | } |
| | 2899 | |
| | 2900 | try parent.values.ensureUnusedCapacity(self.gpa, canon_branch.values.count()); |
| | 2901 | const canon_slice = canon_branch.values.entries.slice(); |
| | 2902 | const canon_keys = canon_slice.items(.key); |
| | 2903 | const canon_values = canon_slice.items(.value); |
| | 2904 | for (canon_keys) |key, index| { |
| | 2905 | parent.values.putAssumeCapacity(key, canon_values[index]); |
| | 2906 | } |
| | 2907 | } |
| | 2908 | |
| 2803 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void { | 2909 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void { |
| 2804 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 2910 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2805 | if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); | 2911 | if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); |
| ... | @@ -3176,6 +3282,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3176,6 +3282,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3176 | break :blk target_ty.intInfo(self.target).signedness; | 3282 | break :blk target_ty.intInfo(self.target).signedness; |
| 3177 | }; | 3283 | }; |
| 3178 | | 3284 | |
| | 3285 | try self.branches.ensureUnusedCapacity(self.gpa, case_list.items.len + @boolToInt(has_else_body)); |
| 3179 | for (case_list.items) |case| { | 3286 | for (case_list.items) |case| { |
| 3180 | // when sparse, we use if/else-chain, so emit conditional checks | 3287 | // when sparse, we use if/else-chain, so emit conditional checks |
| 3181 | if (is_sparse) { | 3288 | if (is_sparse) { |
| ... | @@ -3211,13 +3318,18 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3211,13 +3318,18 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3211 | try self.endBlock(); | 3318 | try self.endBlock(); |
| 3212 | } | 3319 | } |
| 3213 | } | 3320 | } |
| | 3321 | // try self.branches.items |
| | 3322 | self.branches.appendAssumeCapacity(.{}); |
| 3214 | try self.genBody(case.body); | 3323 | try self.genBody(case.body); |
| 3215 | try self.endBlock(); | 3324 | try self.endBlock(); |
| | 3325 | _ = self.branches.pop(); |
| 3216 | } | 3326 | } |
| 3217 | | 3327 | |
| 3218 | if (has_else_body) { | 3328 | if (has_else_body) { |
| | 3329 | self.branches.appendAssumeCapacity(.{}); |
| 3219 | try self.genBody(else_body); | 3330 | try self.genBody(else_body); |
| 3220 | try self.endBlock(); | 3331 | try self.endBlock(); |
| | 3332 | _ = self.branches.pop(); |
| 3221 | } | 3333 | } |
| 3222 | self.finishAir(inst, .none, &.{}); | 3334 | self.finishAir(inst, .none, &.{}); |
| 3223 | } | 3335 | } |
| ... | @@ -3722,7 +3834,7 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3722,7 +3834,7 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 3722 | const result = switch (operand) { | 3834 | const result = switch (operand) { |
| 3723 | // for stack offset, return a pointer to this offset. | 3835 | // for stack offset, return a pointer to this offset. |
| 3724 | .stack_offset => try self.buildPointerOffset(operand, 0, .new), | 3836 | .stack_offset => try self.buildPointerOffset(operand, 0, .new), |
| 3725 | else => operand, | 3837 | else => self.reuseOperand(un_op, operand), |
| 3726 | }; | 3838 | }; |
| 3727 | self.finishAir(inst, result, &.{un_op}); | 3839 | self.finishAir(inst, result, &.{un_op}); |
| 3728 | } | 3840 | } |