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) {...@@ -691,6 +691,14 @@ const MockRegister1 = enum(u2) {
691 }691 }
692692
693 const allocatable_registers = [_]MockRegister1{ .r2, .r3 };693 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);
694};702};
695703
696const MockRegister2 = enum(u2) {704const MockRegister2 = enum(u2) {
...@@ -704,20 +712,62 @@ const MockRegister2 = enum(u2) {...@@ -704,20 +712,62 @@ const MockRegister2 = enum(u2) {
704 }712 }
705713
706 const allocatable_registers = [_]MockRegister2{ .r0, .r1, .r2, .r3 };714 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;
707};761};
708762
709fn MockFunction(comptime Register: type) type {763fn MockFunction(comptime Register: type) type {
710 return struct {764 return struct {
711 allocator: Allocator,765 allocator: Allocator,
712 register_manager: RegisterManagerT = .{},766 register_manager: Register.RM = .{},
713 spilled: std.ArrayListUnmanaged(Register) = .{},767 spilled: std.ArrayListUnmanaged(Register) = .{},
714768
715 const Self = @This();769 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
721 pub fn deinit(self: *Self) void {771 pub fn deinit(self: *Self) void {
722 self.spilled.deinit(self.allocator);772 self.spilled.deinit(self.allocator);
723 }773 }
...@@ -738,6 +788,7 @@ fn MockFunction(comptime Register: type) type {...@@ -738,6 +788,7 @@ fn MockFunction(comptime Register: type) type {
738788
739const MockFunction1 = MockFunction(MockRegister1);789const MockFunction1 = MockFunction(MockRegister1);
740const MockFunction2 = MockFunction(MockRegister2);790const MockFunction2 = MockFunction(MockRegister2);
791const MockFunction3 = MockFunction(MockRegister3);
741792
742test "default state" {793test "default state" {
743 const allocator = std.testing.allocator;794 const allocator = std.testing.allocator;
...@@ -762,20 +813,11 @@ test "tryAllocReg: no spilling" {...@@ -762,20 +813,11 @@ test "tryAllocReg: no spilling" {
762 defer function.deinit();813 defer function.deinit();
763814
764 const mock_instruction: Air.Inst.Index = 1;815 const mock_instruction: Air.Inst.Index = 1;
765 const reg_class = MockFunction1.reg_class;816 const gp = MockRegister1.gp;
766817
767 try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(818 try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(mock_instruction, gp));
768 mock_instruction,819 try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(mock_instruction, gp));
769 reg_class,820 try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(mock_instruction, gp));
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 ));
779821
780 try expect(function.register_manager.isRegAllocated(.r2));822 try expect(function.register_manager.isRegAllocated(.r2));
781 try expect(function.register_manager.isRegAllocated(.r3));823 try expect(function.register_manager.isRegAllocated(.r3));
...@@ -800,30 +842,18 @@ test "allocReg: spilling" {...@@ -800,30 +842,18 @@ test "allocReg: spilling" {
800 defer function.deinit();842 defer function.deinit();
801843
802 const mock_instruction: Air.Inst.Index = 1;844 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(847 try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction, gp));
806 mock_instruction,848 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, gp));
807 reg_class,
808 ));
809 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(
810 mock_instruction,
811 reg_class,
812 ));
813849
814 // Spill a register850 // Spill a register
815 try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(851 try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction, gp));
816 mock_instruction,
817 reg_class,
818 ));
819 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);852 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);
820853
821 // No spilling necessary854 // No spilling necessary
822 function.register_manager.freeReg(.r3);855 function.register_manager.freeReg(.r3);
823 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(856 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, gp));
824 mock_instruction,
825 reg_class,
826 ));
827 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);857 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);
828858
829 // Locked registers859 // Locked registers
...@@ -832,10 +862,7 @@ test "allocReg: spilling" {...@@ -832,10 +862,7 @@ test "allocReg: spilling" {
832 const lock = function.register_manager.lockReg(.r2);862 const lock = function.register_manager.lockReg(.r2);
833 defer if (lock) |reg| function.register_manager.unlockReg(reg);863 defer if (lock) |reg| function.register_manager.unlockReg(reg);
834864
835 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(865 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, gp));
836 mock_instruction,
837 reg_class,
838 ));
839 }866 }
840 try expect(!function.register_manager.lockedRegsExist());867 try expect(!function.register_manager.lockedRegsExist());
841}868}
...@@ -848,13 +875,13 @@ test "tryAllocRegs" {...@@ -848,13 +875,13 @@ test "tryAllocRegs" {
848 };875 };
849 defer function.deinit();876 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(880 try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{
854 3,881 null,
855 .{ null, null, null },882 null,
856 reg_class,883 null,
857 ).?);884 }, gp).?);
858885
859 try expect(function.register_manager.isRegAllocated(.r0));886 try expect(function.register_manager.isRegAllocated(.r0));
860 try expect(function.register_manager.isRegAllocated(.r1));887 try expect(function.register_manager.isRegAllocated(.r1));
...@@ -869,11 +896,11 @@ test "tryAllocRegs" {...@@ -869,11 +896,11 @@ test "tryAllocRegs" {
869 const lock = function.register_manager.lockReg(.r1);896 const lock = function.register_manager.lockReg(.r1);
870 defer if (lock) |reg| function.register_manager.unlockReg(reg);897 defer if (lock) |reg| function.register_manager.unlockReg(reg);
871898
872 try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(899 try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{
873 3,900 null,
874 .{ null, null, null },901 null,
875 reg_class,902 null,
876 ).?);903 }, gp).?);
877 }904 }
878 try expect(!function.register_manager.lockedRegsExist());905 try expect(!function.register_manager.lockedRegsExist());
879906
...@@ -893,7 +920,7 @@ test "allocRegs: normal usage" {...@@ -893,7 +920,7 @@ test "allocRegs: normal usage" {
893 };920 };
894 defer function.deinit();921 defer function.deinit();
895922
896 const reg_class = MockFunction2.reg_class;923 const gp = MockRegister2.gp;
897924
898 {925 {
899 const result_reg: MockRegister2 = .r1;926 const result_reg: MockRegister2 = .r1;
...@@ -914,7 +941,7 @@ test "allocRegs: normal usage" {...@@ -914,7 +941,7 @@ test "allocRegs: normal usage" {
914 const lock = function.register_manager.lockReg(result_reg);941 const lock = function.register_manager.lockReg(result_reg);
915 defer if (lock) |reg| function.register_manager.unlockReg(reg);942 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);
918 try function.genAdd(result_reg, regs[0], regs[1]);945 try function.genAdd(result_reg, regs[0], regs[1]);
919 }946 }
920}947}
...@@ -929,7 +956,7 @@ test "allocRegs: selectively reducing register pressure" {...@@ -929,7 +956,7 @@ test "allocRegs: selectively reducing register pressure" {
929 };956 };
930 defer function.deinit();957 defer function.deinit();
931958
932 const reg_class = MockFunction2.reg_class;959 const gp = MockRegister2.gp;
933960
934 {961 {
935 const result_reg: MockRegister2 = .r1;962 const result_reg: MockRegister2 = .r1;
...@@ -938,12 +965,12 @@ test "allocRegs: selectively reducing register pressure" {...@@ -938,12 +965,12 @@ test "allocRegs: selectively reducing register pressure" {
938965
939 // Here, we don't defer unlock because we manually unlock966 // Here, we don't defer unlock because we manually unlock
940 // after genAdd967 // 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
943 try function.genAdd(result_reg, regs[0], regs[1]);970 try function.genAdd(result_reg, regs[0], regs[1]);
944 function.register_manager.unlockReg(lock.?);971 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);
947 try function.genAdd(result_reg, result_reg, extra_summand_reg);974 try function.genAdd(result_reg, result_reg, extra_summand_reg);
948 }975 }
949}976}
...@@ -974,3 +1001,39 @@ test "getReg" {...@@ -974,3 +1001,39 @@ test "getReg" {
974 try expect(!function.register_manager.isRegFree(.r3));1001 try expect(!function.register_manager.isRegFree(.r3));
975 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r3}, function.spilled.items);1002 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r3}, function.spilled.items);
976}1003}
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}