authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-08-19 12:39:39+02:00
committergravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-09-09 19:17:16+02:00
log0414ef591a0cb42629d7efb5912612f689ea8910
tree7218b1f338a69b7f967ca99e3946a6bf05bd85cb
parent28cc3639476fae72bae3836e8776966386915142
signaturelock-open Commit is signed but in an unrecognized format.

stage2 ARM: introduce allocRegs

This new register allocation mechanism which is designed to be more generic and flexible will replace binOp.

1 files changed, 265 insertions(+), 125 deletions(-)

src/arch/arm/CodeGen.zig+265-125
...@@ -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}
22342234
2235fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {2235fn 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;
22382244
...@@ -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}
25822588
2583/// Allocates a new register. If Inst in non-null, additionally tracks2589/// An argument to a Mir instruction which is read (and possibly also
2584/// this register and the corresponding int and removes all previous2590/// written to) by the respective instruction
2585/// tracking. Does not do the actual moving (that is handled by2591const ReadArg = struct {
2586/// genSetReg).2592 ty: Type,
2587fn 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
2612const 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
2626const 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.
2659fn 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 };
25952681
2596 if (track_inst) |inst| {2682 // When we reuse a read_arg as a destination, the corresponding
2597 // Overwrite the MCValue associated with this inst2683 // 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;
25992686
2600 // If the previous MCValue occupied some space we track, we2687 // 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 }
26142694
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}
26172790
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;
26372811
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 else2814 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|
26432817 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 },
26482822 .{ .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,
26632829 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 regardless2831 .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);
26792835
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;
27452902
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 else2905 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 {
27512908 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;
27582911
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 };
27792918 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 );
27812927
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);
29853131
2986 const lhs_is_register = lhs == .register;3132 var lhs_reg: Register = undefined;
3133 var dest_reg: Register = undefined;
29873134
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 else3137 else
2991 null;3138 ReadArg.Bind{ .mcv = lhs };
2992 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);3139 const read_args = [_]ReadArg{
29933140 .{ .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,
30033147 &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);
30133153
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 };