authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-20 21:18:17+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-21 08:40:47+02:00
log800edb03b5dca7998941eaba016d57a5512bf337
treef379b6daf2437ff9946788035c6e6c99c5873446
parent1a92264b3d3f3e14f9a3b724fccefb5f5a38346f

regalloc: test allocating from multiple register claases


1 files changed, 119 insertions(+), 56 deletions(-)

src/register_manager.zig+119-56
......@@ -691,6 +691,14 @@ const MockRegister1 = enum(u2) {
691691 }
692692
693693 const allocatable_registers = [_]MockRegister1{ .r2, .r3 };
694
695 const RM = RegisterManager(
696 MockFunction1,
697 MockRegister1,
698 &MockRegister1.allocatable_registers,
699 );
700
701 const gp: RM.RegisterBitSet = std.math.maxInt(RM.RegisterBitSet);
694702};
695703
696704const MockRegister2 = enum(u2) {
......@@ -704,20 +712,62 @@ const MockRegister2 = enum(u2) {
704712 }
705713
706714 const allocatable_registers = [_]MockRegister2{ .r0, .r1, .r2, .r3 };
715
716 const RM = RegisterManager(
717 MockFunction2,
718 MockRegister2,
719 &MockRegister2.allocatable_registers,
720 );
721
722 const gp: RM.RegisterBitSet = std.math.maxInt(RM.RegisterBitSet);
723};
724
725const MockRegister3 = enum(u3) {
726 r0,
727 r1,
728 r2,
729 r3,
730 x0,
731 x1,
732 x2,
733 x3,
734
735 pub fn id(reg: MockRegister3) u3 {
736 return switch (@enumToInt(reg)) {
737 0...3 => @as(u3, @truncate(u2, @enumToInt(reg))),
738 4...7 => @enumToInt(reg),
739 };
740 }
741
742 pub fn enc(reg: MockRegister3) u2 {
743 return @truncate(u2, @enumToInt(reg));
744 }
745
746 const gp_regs = [_]MockRegister3{ .r0, .r1, .r2, .r3 };
747 const ext_regs = [_]MockRegister3{ .x0, .x1, .x2, .x3 };
748 const allocatable_registers = gp_regs ++ ext_regs;
749
750 const RM = RegisterManager(
751 MockFunction3,
752 MockRegister3,
753 &MockRegister3.allocatable_registers,
754 );
755
756 const gp: RM.RegisterBitSet = @as(RM.RegisterBitSet, std.math.maxInt(std.meta.Int(
757 .unsigned,
758 gp_regs.len,
759 )));
760 const ext: RM.RegisterBitSet = std.math.maxInt(RM.RegisterBitSet) - gp;
707761};
708762
709763fn MockFunction(comptime Register: type) type {
710764 return struct {
711765 allocator: Allocator,
712 register_manager: RegisterManagerT = .{},
766 register_manager: Register.RM = .{},
713767 spilled: std.ArrayListUnmanaged(Register) = .{},
714768
715769 const Self = @This();
716770
717 const RegisterManagerT = RegisterManager(Self, Register, &Register.allocatable_registers);
718
719 pub const reg_class: RegisterManagerT.RegisterBitSet = math.maxInt(RegisterManagerT.RegisterBitSet);
720
721771 pub fn deinit(self: *Self) void {
722772 self.spilled.deinit(self.allocator);
723773 }
......@@ -738,6 +788,7 @@ fn MockFunction(comptime Register: type) type {
738788
739789const MockFunction1 = MockFunction(MockRegister1);
740790const MockFunction2 = MockFunction(MockRegister2);
791const MockFunction3 = MockFunction(MockRegister3);
741792
742793test "default state" {
743794 const allocator = std.testing.allocator;
......@@ -762,20 +813,11 @@ test "tryAllocReg: no spilling" {
762813 defer function.deinit();
763814
764815 const mock_instruction: Air.Inst.Index = 1;
765 const reg_class = MockFunction1.reg_class;
766
767 try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(
768 mock_instruction,
769 reg_class,
770 ));
771 try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(
772 mock_instruction,
773 reg_class,
774 ));
775 try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(
776 mock_instruction,
777 reg_class,
778 ));
816 const gp = MockRegister1.gp;
817
818 try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(mock_instruction, gp));
819 try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(mock_instruction, gp));
820 try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(mock_instruction, gp));
779821
780822 try expect(function.register_manager.isRegAllocated(.r2));
781823 try expect(function.register_manager.isRegAllocated(.r3));
......@@ -800,30 +842,18 @@ test "allocReg: spilling" {
800842 defer function.deinit();
801843
802844 const mock_instruction: Air.Inst.Index = 1;
803 const reg_class = MockFunction1.reg_class;
845 const gp = MockRegister1.gp;
804846
805 try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(
806 mock_instruction,
807 reg_class,
808 ));
809 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(
810 mock_instruction,
811 reg_class,
812 ));
847 try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction, gp));
848 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, gp));
813849
814850 // Spill a register
815 try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(
816 mock_instruction,
817 reg_class,
818 ));
851 try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction, gp));
819852 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);
820853
821854 // No spilling necessary
822855 function.register_manager.freeReg(.r3);
823 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(
824 mock_instruction,
825 reg_class,
826 ));
856 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, gp));
827857 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);
828858
829859 // Locked registers
......@@ -832,10 +862,7 @@ test "allocReg: spilling" {
832862 const lock = function.register_manager.lockReg(.r2);
833863 defer if (lock) |reg| function.register_manager.unlockReg(reg);
834864
835 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(
836 mock_instruction,
837 reg_class,
838 ));
865 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, gp));
839866 }
840867 try expect(!function.register_manager.lockedRegsExist());
841868}
......@@ -848,13 +875,13 @@ test "tryAllocRegs" {
848875 };
849876 defer function.deinit();
850877
851 const reg_class = MockFunction2.reg_class;
878 const gp = MockRegister2.gp;
852879
853 try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(
854 3,
855 .{ null, null, null },
856 reg_class,
857 ).?);
880 try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{
881 null,
882 null,
883 null,
884 }, gp).?);
858885
859886 try expect(function.register_manager.isRegAllocated(.r0));
860887 try expect(function.register_manager.isRegAllocated(.r1));
......@@ -869,11 +896,11 @@ test "tryAllocRegs" {
869896 const lock = function.register_manager.lockReg(.r1);
870897 defer if (lock) |reg| function.register_manager.unlockReg(reg);
871898
872 try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(
873 3,
874 .{ null, null, null },
875 reg_class,
876 ).?);
899 try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{
900 null,
901 null,
902 null,
903 }, gp).?);
877904 }
878905 try expect(!function.register_manager.lockedRegsExist());
879906
......@@ -893,7 +920,7 @@ test "allocRegs: normal usage" {
893920 };
894921 defer function.deinit();
895922
896 const reg_class = MockFunction2.reg_class;
923 const gp = MockRegister2.gp;
897924
898925 {
899926 const result_reg: MockRegister2 = .r1;
......@@ -914,7 +941,7 @@ test "allocRegs: normal usage" {
914941 const lock = function.register_manager.lockReg(result_reg);
915942 defer if (lock) |reg| function.register_manager.unlockReg(reg);
916943
917 const regs = try function.register_manager.allocRegs(2, .{ null, null }, reg_class);
944 const regs = try function.register_manager.allocRegs(2, .{ null, null }, gp);
918945 try function.genAdd(result_reg, regs[0], regs[1]);
919946 }
920947}
......@@ -929,7 +956,7 @@ test "allocRegs: selectively reducing register pressure" {
929956 };
930957 defer function.deinit();
931958
932 const reg_class = MockFunction2.reg_class;
959 const gp = MockRegister2.gp;
933960
934961 {
935962 const result_reg: MockRegister2 = .r1;
......@@ -938,12 +965,12 @@ test "allocRegs: selectively reducing register pressure" {
938965
939966 // Here, we don't defer unlock because we manually unlock
940967 // after genAdd
941 const regs = try function.register_manager.allocRegs(2, .{ null, null }, reg_class);
968 const regs = try function.register_manager.allocRegs(2, .{ null, null }, gp);
942969
943970 try function.genAdd(result_reg, regs[0], regs[1]);
944971 function.register_manager.unlockReg(lock.?);
945972
946 const extra_summand_reg = try function.register_manager.allocReg(null, reg_class);
973 const extra_summand_reg = try function.register_manager.allocReg(null, gp);
947974 try function.genAdd(result_reg, result_reg, extra_summand_reg);
948975 }
949976}
......@@ -974,3 +1001,39 @@ test "getReg" {
9741001 try expect(!function.register_manager.isRegFree(.r3));
9751002 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r3}, function.spilled.items);
9761003}
1004
1005test "allocReg with multiple, non-overlapping register classes" {
1006 const allocator = std.testing.allocator;
1007
1008 var function = MockFunction3{
1009 .allocator = allocator,
1010 };
1011 defer function.deinit();
1012
1013 const gp = MockRegister3.gp;
1014 const ext = MockRegister3.ext;
1015
1016 const gp_reg = try function.register_manager.allocReg(null, gp);
1017
1018 try expect(function.register_manager.isRegAllocated(.r0));
1019 try expect(!function.register_manager.isRegAllocated(.x0));
1020
1021 const ext_reg = try function.register_manager.allocReg(null, ext);
1022
1023 try expect(function.register_manager.isRegAllocated(.r0));
1024 try expect(!function.register_manager.isRegAllocated(.r1));
1025 try expect(function.register_manager.isRegAllocated(.x0));
1026 try expect(!function.register_manager.isRegAllocated(.x1));
1027 try expect(gp_reg.enc() == ext_reg.enc());
1028
1029 const ext_lock = function.register_manager.lockRegAssumeUnused(ext_reg);
1030 defer function.register_manager.unlockReg(ext_lock);
1031
1032 const ext_reg2 = try function.register_manager.allocReg(null, ext);
1033
1034 try expect(function.register_manager.isRegAllocated(.r0));
1035 try expect(function.register_manager.isRegAllocated(.x0));
1036 try expect(!function.register_manager.isRegAllocated(.r1));
1037 try expect(function.register_manager.isRegAllocated(.x1));
1038 try expect(ext_reg2.enc() == MockRegister3.r1.enc());
1039}