| ... | @@ -2232,7 +2232,13 @@ fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2232,7 +2232,13 @@ fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { |
| 2232 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 2232 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2233 | } | 2233 | } |
| 2234 | | 2234 | |
| 2235 | fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { | 2235 | fn reuseOperand( |
| | 2236 | self: *Self, |
| | 2237 | inst: Air.Inst.Index, |
| | 2238 | operand: Air.Inst.Ref, |
| | 2239 | op_index: Liveness.OperandInt, |
| | 2240 | mcv: MCValue, |
| | 2241 | ) bool { |
| 2236 | if (!self.liveness.operandDies(inst, op_index)) | 2242 | if (!self.liveness.operandDies(inst, op_index)) |
| 2237 | return false; | 2243 | return false; |
| 2238 | | 2244 | |
| ... | @@ -2580,39 +2586,206 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2580,39 +2586,206 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2580 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 2586 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2581 | } | 2587 | } |
| 2582 | | 2588 | |
| 2583 | /// Allocates a new register. If Inst in non-null, additionally tracks | 2589 | /// An argument to a Mir instruction which is read (and possibly also |
| 2584 | /// this register and the corresponding int and removes all previous | 2590 | /// written to) by the respective instruction |
| 2585 | /// tracking. Does not do the actual moving (that is handled by | 2591 | const ReadArg = struct { |
| 2586 | /// genSetReg). | 2592 | ty: Type, |
| 2587 | fn prepareNewRegForMoving( | 2593 | bind: Bind, |
| | 2594 | class: RegisterManager.RegisterBitSet, |
| | 2595 | reg: *Register, |
| | 2596 | |
| | 2597 | const Bind = union(enum) { |
| | 2598 | inst: Air.Inst.Ref, |
| | 2599 | mcv: MCValue, |
| | 2600 | |
| | 2601 | fn resolveToMcv(bind: Bind, function: *Self) InnerError!MCValue { |
| | 2602 | return switch (bind) { |
| | 2603 | .inst => |inst| try function.resolveInst(inst), |
| | 2604 | .mcv => |mcv| mcv, |
| | 2605 | }; |
| | 2606 | } |
| | 2607 | }; |
| | 2608 | }; |
| | 2609 | |
| | 2610 | /// An argument to a Mir instruction which is written to (but not read |
| | 2611 | /// from) by the respective instruction |
| | 2612 | const WriteArg = struct { |
| | 2613 | ty: Type, |
| | 2614 | bind: Bind, |
| | 2615 | class: RegisterManager.RegisterBitSet, |
| | 2616 | reg: *Register, |
| | 2617 | |
| | 2618 | const Bind = union(enum) { |
| | 2619 | reg: Register, |
| | 2620 | none: void, |
| | 2621 | }; |
| | 2622 | }; |
| | 2623 | |
| | 2624 | /// Holds all data necessary for enabling the potential reuse of |
| | 2625 | /// operand registers as destinations |
| | 2626 | const ReuseMetadata = struct { |
| | 2627 | corresponding_inst: Air.Inst.Index, |
| | 2628 | |
| | 2629 | /// Maps every element index of read_args to the corresponding |
| | 2630 | /// index in the Air instruction |
| | 2631 | /// |
| | 2632 | /// When the order of read_args corresponds exactly to the order |
| | 2633 | /// of the inputs of the Air instruction, this would be e.g. |
| | 2634 | /// &.{ 0, 1 }. However, when the order is not the same or some |
| | 2635 | /// inputs to the Air instruction are omitted (e.g. when they can |
| | 2636 | /// be represented as immediates to the Mir instruction), |
| | 2637 | /// operand_mapping should reflect that fact. |
| | 2638 | operand_mapping: []const Liveness.OperandInt, |
| | 2639 | }; |
| | 2640 | |
| | 2641 | /// Allocate a set of registers for use as arguments for a Mir |
| | 2642 | /// instruction |
| | 2643 | /// |
| | 2644 | /// If the Mir instruction these registers are allocated for |
| | 2645 | /// corresponds exactly to a single Air instruction, populate |
| | 2646 | /// reuse_metadata in order to enable potential reuse of an operand as |
| | 2647 | /// the destination (provided that that operand dies in this |
| | 2648 | /// instruction). |
| | 2649 | /// |
| | 2650 | /// Reusing an operand register as destination is the only time two |
| | 2651 | /// arguments may share the same register. In all other cases, |
| | 2652 | /// allocRegs guarantees that a register will never be allocated to |
| | 2653 | /// more than one argument. |
| | 2654 | /// |
| | 2655 | /// Furthermore, allocReg guarantees that all arguments which are |
| | 2656 | /// already bound to registers before calling allocRegs will not |
| | 2657 | /// change their register binding. This is done by locking these |
| | 2658 | /// registers. |
| | 2659 | fn allocRegs( |
| 2588 | self: *Self, | 2660 | self: *Self, |
| 2589 | track_inst: ?Air.Inst.Index, | 2661 | read_args: []const ReadArg, |
| 2590 | register_class: RegisterManager.RegisterBitSet, | 2662 | write_args: []const WriteArg, |
| 2591 | mcv: MCValue, | 2663 | reuse_metadata: ?ReuseMetadata, |
| 2592 | ) !Register { | 2664 | ) InnerError!void { |
| 2593 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 2665 | // Air instructions have either one output or none (cmp) |
| 2594 | const reg = try self.register_manager.allocReg(track_inst, register_class); | 2666 | assert(!(reuse_metadata != null and write_args.len > 1)); // see note above |
| | 2667 | |
| | 2668 | // The operand mapping is a 1:1 mapping of read args to their |
| | 2669 | // corresponding operand index in the Air instruction |
| | 2670 | assert(!(reuse_metadata != null and reuse_metadata.?.operand_mapping.len != read_args.len)); // see note above |
| | 2671 | |
| | 2672 | const locks = try self.gpa.alloc(?RegisterLock, read_args.len + write_args.len); |
| | 2673 | defer self.gpa.free(locks); |
| | 2674 | const read_locks = locks[0..read_args.len]; |
| | 2675 | const write_locks = locks[read_args.len..]; |
| | 2676 | |
| | 2677 | std.mem.set(?RegisterLock, locks, null); |
| | 2678 | defer for (locks) |lock| { |
| | 2679 | if (lock) |locked_reg| self.register_manager.unlockReg(locked_reg); |
| | 2680 | }; |
| 2595 | | 2681 | |
| 2596 | if (track_inst) |inst| { | 2682 | // When we reuse a read_arg as a destination, the corresponding |
| 2597 | // Overwrite the MCValue associated with this inst | 2683 | // MCValue of the read_arg will be set to .dead. In that case, we |
| 2598 | branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); | 2684 | // skip allocating this read_arg. |
| | 2685 | var reused_read_arg: ?usize = null; |
| 2599 | | 2686 | |
| 2600 | // If the previous MCValue occupied some space we track, we | 2687 | // Lock all args which are already allocated to registers |
| 2601 | // need to make sure it is marked as free now. | 2688 | for (read_args) |arg, i| { |
| 2602 | switch (mcv) { | 2689 | const mcv = try arg.bind.resolveToMcv(self); |
| 2603 | .cpsr_flags => { | 2690 | if (mcv == .register) { |
| 2604 | assert(self.cpsr_flags_inst.? == inst); | 2691 | read_locks[i] = self.register_manager.lockReg(mcv.register); |
| 2605 | self.cpsr_flags_inst = null; | | |
| 2606 | }, | | |
| 2607 | .register => |prev_reg| { | | |
| 2608 | assert(!self.register_manager.isRegFree(prev_reg)); | | |
| 2609 | self.register_manager.freeReg(prev_reg); | | |
| 2610 | }, | | |
| 2611 | else => {}, | | |
| 2612 | } | 2692 | } |
| 2613 | } | 2693 | } |
| 2614 | | 2694 | |
| 2615 | return reg; | 2695 | for (write_args) |arg, i| { |
| | 2696 | if (arg.bind == .reg) { |
| | 2697 | write_locks[i] = self.register_manager.lockReg(arg.bind.reg); |
| | 2698 | } |
| | 2699 | } |
| | 2700 | |
| | 2701 | // Allocate registers for all args which aren't allocated to |
| | 2702 | // registers yet |
| | 2703 | for (read_args) |arg, i| { |
| | 2704 | const mcv = try arg.bind.resolveToMcv(self); |
| | 2705 | if (mcv == .register) { |
| | 2706 | arg.reg.* = mcv.register; |
| | 2707 | } else { |
| | 2708 | const track_inst: ?Air.Inst.Index = switch (arg.bind) { |
| | 2709 | .inst => |inst| Air.refToIndex(inst).?, |
| | 2710 | else => null, |
| | 2711 | }; |
| | 2712 | arg.reg.* = try self.register_manager.allocReg(track_inst, arg.class); |
| | 2713 | read_locks[i] = self.register_manager.lockReg(arg.reg.*); |
| | 2714 | } |
| | 2715 | } |
| | 2716 | |
| | 2717 | if (reuse_metadata != null and write_args.len > 0) { |
| | 2718 | const inst = reuse_metadata.?.corresponding_inst; |
| | 2719 | const operand_mapping = reuse_metadata.?.operand_mapping; |
| | 2720 | const arg = write_args[0]; |
| | 2721 | if (arg.bind == .reg) { |
| | 2722 | arg.reg.* = arg.bind.reg; |
| | 2723 | } else { |
| | 2724 | reuse_operand: for (read_args) |read_arg, i| { |
| | 2725 | if (read_arg.bind == .inst) { |
| | 2726 | const operand = read_arg.bind.inst; |
| | 2727 | const mcv = try self.resolveInst(operand); |
| | 2728 | if (mcv == .register and |
| | 2729 | std.meta.eql(arg.class, read_arg.class) and |
| | 2730 | self.reuseOperand(inst, operand, operand_mapping[i], mcv)) |
| | 2731 | { |
| | 2732 | arg.reg.* = mcv.register; |
| | 2733 | write_locks[0] = null; |
| | 2734 | reused_read_arg = i; |
| | 2735 | break :reuse_operand; |
| | 2736 | } |
| | 2737 | } |
| | 2738 | } else { |
| | 2739 | arg.reg.* = try self.register_manager.allocReg(inst, arg.class); |
| | 2740 | write_locks[0] = self.register_manager.lockReg(arg.reg.*); |
| | 2741 | } |
| | 2742 | } |
| | 2743 | } else { |
| | 2744 | for (write_args) |arg, i| { |
| | 2745 | if (arg.bind == .reg) { |
| | 2746 | arg.reg.* = arg.bind.reg; |
| | 2747 | } else { |
| | 2748 | arg.reg.* = try self.register_manager.allocReg(null, arg.class); |
| | 2749 | write_locks[i] = self.register_manager.lockReg(arg.reg.*); |
| | 2750 | } |
| | 2751 | } |
| | 2752 | } |
| | 2753 | |
| | 2754 | // For all read_args which need to be moved from non-register to |
| | 2755 | // register, perform the move |
| | 2756 | for (read_args) |arg, i| { |
| | 2757 | if (reused_read_arg) |j| { |
| | 2758 | // Check whether this read_arg was reused |
| | 2759 | if (i == j) continue; |
| | 2760 | } |
| | 2761 | |
| | 2762 | const mcv = try arg.bind.resolveToMcv(self); |
| | 2763 | if (mcv != .register) { |
| | 2764 | if (arg.bind == .inst) { |
| | 2765 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| | 2766 | const inst = Air.refToIndex(arg.bind.inst).?; |
| | 2767 | |
| | 2768 | // Overwrite the MCValue associated with this inst |
| | 2769 | branch.inst_table.putAssumeCapacity(inst, .{ .register = arg.reg.* }); |
| | 2770 | |
| | 2771 | // If the previous MCValue occupied some space we track, we |
| | 2772 | // need to make sure it is marked as free now. |
| | 2773 | switch (mcv) { |
| | 2774 | .cpsr_flags => { |
| | 2775 | assert(self.cpsr_flags_inst.? == inst); |
| | 2776 | self.cpsr_flags_inst = null; |
| | 2777 | }, |
| | 2778 | .register => |prev_reg| { |
| | 2779 | assert(!self.register_manager.isRegFree(prev_reg)); |
| | 2780 | self.register_manager.freeReg(prev_reg); |
| | 2781 | }, |
| | 2782 | else => {}, |
| | 2783 | } |
| | 2784 | } |
| | 2785 | |
| | 2786 | try self.genSetReg(arg.ty, arg.reg.*, mcv); |
| | 2787 | } |
| | 2788 | } |
| 2616 | } | 2789 | } |
| 2617 | | 2790 | |
| 2618 | /// Don't call this function directly. Use binOp instead. | 2791 | /// Don't call this function directly. Use binOp instead. |
| ... | @@ -2632,50 +2805,33 @@ fn binOpRegister( | ... | @@ -2632,50 +2805,33 @@ fn binOpRegister( |
| 2632 | rhs_ty: Type, | 2805 | rhs_ty: Type, |
| 2633 | metadata: ?BinOpMetadata, | 2806 | metadata: ?BinOpMetadata, |
| 2634 | ) !MCValue { | 2807 | ) !MCValue { |
| 2635 | const lhs_is_register = lhs == .register; | 2808 | var lhs_reg: Register = undefined; |
| 2636 | const rhs_is_register = rhs == .register; | 2809 | var rhs_reg: Register = undefined; |
| | 2810 | var dest_reg: Register = undefined; |
| 2637 | | 2811 | |
| 2638 | const lhs_lock: ?RegisterLock = if (lhs_is_register) | 2812 | const lhs_bind = if (metadata) |md| |
| 2639 | self.register_manager.lockReg(lhs.register) | 2813 | ReadArg.Bind{ .inst = md.lhs } |
| 2640 | else | 2814 | else |
| 2641 | null; | 2815 | ReadArg.Bind{ .mcv = lhs }; |
| 2642 | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); | 2816 | const rhs_bind = if (metadata) |md| |
| 2643 | | 2817 | ReadArg.Bind{ .inst = md.rhs } |
| 2644 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { | 2818 | else |
| 2645 | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { | 2819 | ReadArg.Bind{ .mcv = rhs }; |
| 2646 | break :inst Air.refToIndex(md.lhs).?; | 2820 | const read_args = [_]ReadArg{ |
| 2647 | } else null; | 2821 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, |
| 2648 | | 2822 | .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, |
| 2649 | break :blk try self.prepareNewRegForMoving(track_inst, gp, lhs); | | |
| 2650 | }; | 2823 | }; |
| 2651 | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); | 2824 | const write_args = [_]WriteArg{ |
| 2652 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); | 2825 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, |
| 2653 | | | |
| 2654 | const rhs_reg = if (rhs_is_register) rhs.register else blk: { | | |
| 2655 | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { | | |
| 2656 | break :inst Air.refToIndex(md.rhs).?; | | |
| 2657 | } else null; | | |
| 2658 | | | |
| 2659 | break :blk try self.prepareNewRegForMoving(track_inst, gp, rhs); | | |
| 2660 | }; | 2826 | }; |
| 2661 | const new_rhs_lock = self.register_manager.lockReg(rhs_reg); | 2827 | try self.allocRegs( |
| 2662 | defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); | 2828 | &read_args, |
| 2663 | | 2829 | if (mir_tag == .cmp) &.{} else &write_args, |
| 2664 | const dest_reg = switch (mir_tag) { | 2830 | if (metadata) |md| .{ |
| 2665 | .cmp => undefined, // cmp has no destination regardless | 2831 | .corresponding_inst = md.inst, |
| 2666 | else => if (metadata) |md| blk: { | 2832 | .operand_mapping = &.{ 0, 1 }, |
| 2667 | if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) { | 2833 | } else null, |
| 2668 | break :blk lhs_reg; | 2834 | ); |
| 2669 | } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) { | | |
| 2670 | break :blk rhs_reg; | | |
| 2671 | } else { | | |
| 2672 | break :blk try self.register_manager.allocReg(md.inst, gp); | | |
| 2673 | } | | |
| 2674 | } else try self.register_manager.allocReg(null, gp), | | |
| 2675 | }; | | |
| 2676 | | | |
| 2677 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | | |
| 2678 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); | | |
| 2679 | | 2835 | |
| 2680 | const mir_data: Mir.Inst.Data = switch (mir_tag) { | 2836 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 2681 | .add, | 2837 | .add, |
| ... | @@ -2741,43 +2897,33 @@ fn binOpImmediate( | ... | @@ -2741,43 +2897,33 @@ fn binOpImmediate( |
| 2741 | lhs_and_rhs_swapped: bool, | 2897 | lhs_and_rhs_swapped: bool, |
| 2742 | metadata: ?BinOpMetadata, | 2898 | metadata: ?BinOpMetadata, |
| 2743 | ) !MCValue { | 2899 | ) !MCValue { |
| 2744 | const lhs_is_register = lhs == .register; | 2900 | var lhs_reg: Register = undefined; |
| | 2901 | var dest_reg: Register = undefined; |
| 2745 | | 2902 | |
| 2746 | const lhs_lock: ?RegisterLock = if (lhs_is_register) | 2903 | const lhs_bind = blk: { |
| 2747 | self.register_manager.lockReg(lhs.register) | 2904 | if (metadata) |md| { |
| 2748 | else | 2905 | const inst = if (lhs_and_rhs_swapped) md.rhs else md.lhs; |
| 2749 | null; | 2906 | break :blk ReadArg.Bind{ .inst = inst }; |
| 2750 | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); | 2907 | } else { |
| 2751 | | 2908 | break :blk ReadArg.Bind{ .mcv = lhs }; |
| 2752 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { | 2909 | } |
| 2753 | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { | 2910 | }; |
| 2754 | break :inst Air.refToIndex( | | |
| 2755 | if (lhs_and_rhs_swapped) md.rhs else md.lhs, | | |
| 2756 | ).?; | | |
| 2757 | } else null; | | |
| 2758 | | 2911 | |
| 2759 | break :blk try self.prepareNewRegForMoving(track_inst, gp, lhs); | 2912 | const read_args = [_]ReadArg{ |
| | 2913 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, |
| 2760 | }; | 2914 | }; |
| 2761 | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); | 2915 | const write_args = [_]WriteArg{ |
| 2762 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); | 2916 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, |
| 2763 | | | |
| 2764 | const dest_reg = switch (mir_tag) { | | |
| 2765 | .cmp => undefined, // cmp has no destination reg | | |
| 2766 | else => if (metadata) |md| blk: { | | |
| 2767 | if (lhs_is_register and self.reuseOperand( | | |
| 2768 | md.inst, | | |
| 2769 | if (lhs_and_rhs_swapped) md.rhs else md.lhs, | | |
| 2770 | if (lhs_and_rhs_swapped) 1 else 0, | | |
| 2771 | lhs, | | |
| 2772 | )) { | | |
| 2773 | break :blk lhs_reg; | | |
| 2774 | } else { | | |
| 2775 | break :blk try self.register_manager.allocReg(md.inst, gp); | | |
| 2776 | } | | |
| 2777 | } else try self.register_manager.allocReg(null, gp), | | |
| 2778 | }; | 2917 | }; |
| 2779 | | 2918 | const operand_mapping: []const Liveness.OperandInt = if (lhs_and_rhs_swapped) &.{1} else &.{0}; |
| 2780 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | 2919 | try self.allocRegs( |
| | 2920 | &read_args, |
| | 2921 | if (mir_tag == .cmp) &.{} else &write_args, |
| | 2922 | if (metadata) |md| .{ |
| | 2923 | .corresponding_inst = md.inst, |
| | 2924 | .operand_mapping = operand_mapping, |
| | 2925 | } else null, |
| | 2926 | ); |
| 2781 | | 2927 | |
| 2782 | const mir_data: Mir.Inst.Data = switch (mir_tag) { | 2928 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 2783 | .add, | 2929 | .add, |
| ... | @@ -2983,33 +3129,27 @@ fn binOp( | ... | @@ -2983,33 +3129,27 @@ fn binOp( |
| 2983 | if (std.math.isPowerOfTwo(imm)) { | 3129 | if (std.math.isPowerOfTwo(imm)) { |
| 2984 | const log2 = std.math.log2_int(u32, imm); | 3130 | const log2 = std.math.log2_int(u32, imm); |
| 2985 | | 3131 | |
| 2986 | const lhs_is_register = lhs == .register; | 3132 | var lhs_reg: Register = undefined; |
| | 3133 | var dest_reg: Register = undefined; |
| 2987 | | 3134 | |
| 2988 | const lhs_lock: ?RegisterLock = if (lhs_is_register) | 3135 | const lhs_bind = if (metadata) |md| |
| 2989 | self.register_manager.lockReg(lhs.register) | 3136 | ReadArg.Bind{ .inst = md.lhs } |
| 2990 | else | 3137 | else |
| 2991 | null; | 3138 | ReadArg.Bind{ .mcv = lhs }; |
| 2992 | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); | 3139 | const read_args = [_]ReadArg{ |
| 2993 | | 3140 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, |
| 2994 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { | 3141 | }; |
| 2995 | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { | 3142 | const write_args = [_]WriteArg{ |
| 2996 | break :inst Air.refToIndex(md.lhs).?; | 3143 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, |
| 2997 | } else null; | | |
| 2998 | | | |
| 2999 | break :blk try self.prepareNewRegForMoving(track_inst, gp, lhs); | | |
| 3000 | }; | 3144 | }; |
| 3001 | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); | 3145 | try self.allocRegs( |
| 3002 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); | 3146 | &read_args, |
| 3003 | | 3147 | &write_args, |
| 3004 | const dest_reg = if (metadata) |md| blk: { | 3148 | if (metadata) |md| .{ |
| 3005 | if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) { | 3149 | .corresponding_inst = md.inst, |
| 3006 | break :blk lhs_reg; | 3150 | .operand_mapping = &.{0}, |
| 3007 | } else { | 3151 | } else null, |
| 3008 | break :blk try self.register_manager.allocReg(md.inst, gp); | 3152 | ); |
| 3009 | } | | |
| 3010 | } else try self.register_manager.allocReg(null, gp); | | |
| 3011 | | | |
| 3012 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | | |
| 3013 | | 3153 | |
| 3014 | try self.truncRegister(lhs_reg, dest_reg, int_info.signedness, log2); | 3154 | try self.truncRegister(lhs_reg, dest_reg, int_info.signedness, log2); |
| 3015 | return MCValue{ .register = dest_reg }; | 3155 | return MCValue{ .register = dest_reg }; |