| ... | ... | @@ -691,6 +691,14 @@ const MockRegister1 = enum(u2) { |
| 691 | 691 | } |
| 692 | 692 | |
| 693 | 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 | }; |
| 695 | 703 | |
| 696 | 704 | const MockRegister2 = enum(u2) { |
| ... | ... | @@ -704,20 +712,62 @@ const MockRegister2 = enum(u2) { |
| 704 | 712 | } |
| 705 | 713 | |
| 706 | 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 | |
| 725 | const 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 | }; |
| 708 | 762 | |
| 709 | 763 | fn MockFunction(comptime Register: type) type { |
| 710 | 764 | return struct { |
| 711 | 765 | allocator: Allocator, |
| 712 | | register_manager: RegisterManagerT = .{}, |
| 766 | register_manager: Register.RM = .{}, |
| 713 | 767 | spilled: std.ArrayListUnmanaged(Register) = .{}, |
| 714 | 768 | |
| 715 | 769 | const Self = @This(); |
| 716 | 770 | |
| 717 | | const RegisterManagerT = RegisterManager(Self, Register, &Register.allocatable_registers); |
| 718 | | |
| 719 | | pub const reg_class: RegisterManagerT.RegisterBitSet = math.maxInt(RegisterManagerT.RegisterBitSet); |
| 720 | | |
| 721 | 771 | pub fn deinit(self: *Self) void { |
| 722 | 772 | self.spilled.deinit(self.allocator); |
| 723 | 773 | } |
| ... | ... | @@ -738,6 +788,7 @@ fn MockFunction(comptime Register: type) type { |
| 738 | 788 | |
| 739 | 789 | const MockFunction1 = MockFunction(MockRegister1); |
| 740 | 790 | const MockFunction2 = MockFunction(MockRegister2); |
| 791 | const MockFunction3 = MockFunction(MockRegister3); |
| 741 | 792 | |
| 742 | 793 | test "default state" { |
| 743 | 794 | const allocator = std.testing.allocator; |
| ... | ... | @@ -762,20 +813,11 @@ test "tryAllocReg: no spilling" { |
| 762 | 813 | defer function.deinit(); |
| 763 | 814 | |
| 764 | 815 | 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)); |
| 779 | 821 | |
| 780 | 822 | try expect(function.register_manager.isRegAllocated(.r2)); |
| 781 | 823 | try expect(function.register_manager.isRegAllocated(.r3)); |
| ... | ... | @@ -800,30 +842,18 @@ test "allocReg: spilling" { |
| 800 | 842 | defer function.deinit(); |
| 801 | 843 | |
| 802 | 844 | const mock_instruction: Air.Inst.Index = 1; |
| 803 | | const reg_class = MockFunction1.reg_class; |
| 845 | const gp = MockRegister1.gp; |
| 804 | 846 | |
| 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)); |
| 813 | 849 | |
| 814 | 850 | // 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)); |
| 819 | 852 | try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); |
| 820 | 853 | |
| 821 | 854 | // No spilling necessary |
| 822 | 855 | 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)); |
| 827 | 857 | try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); |
| 828 | 858 | |
| 829 | 859 | // Locked registers |
| ... | ... | @@ -832,10 +862,7 @@ test "allocReg: spilling" { |
| 832 | 862 | const lock = function.register_manager.lockReg(.r2); |
| 833 | 863 | defer if (lock) |reg| function.register_manager.unlockReg(reg); |
| 834 | 864 | |
| 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)); |
| 839 | 866 | } |
| 840 | 867 | try expect(!function.register_manager.lockedRegsExist()); |
| 841 | 868 | } |
| ... | ... | @@ -848,13 +875,13 @@ test "tryAllocRegs" { |
| 848 | 875 | }; |
| 849 | 876 | defer function.deinit(); |
| 850 | 877 | |
| 851 | | const reg_class = MockFunction2.reg_class; |
| 878 | const gp = MockRegister2.gp; |
| 852 | 879 | |
| 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).?); |
| 858 | 885 | |
| 859 | 886 | try expect(function.register_manager.isRegAllocated(.r0)); |
| 860 | 887 | try expect(function.register_manager.isRegAllocated(.r1)); |
| ... | ... | @@ -869,11 +896,11 @@ test "tryAllocRegs" { |
| 869 | 896 | const lock = function.register_manager.lockReg(.r1); |
| 870 | 897 | defer if (lock) |reg| function.register_manager.unlockReg(reg); |
| 871 | 898 | |
| 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).?); |
| 877 | 904 | } |
| 878 | 905 | try expect(!function.register_manager.lockedRegsExist()); |
| 879 | 906 | |
| ... | ... | @@ -893,7 +920,7 @@ test "allocRegs: normal usage" { |
| 893 | 920 | }; |
| 894 | 921 | defer function.deinit(); |
| 895 | 922 | |
| 896 | | const reg_class = MockFunction2.reg_class; |
| 923 | const gp = MockRegister2.gp; |
| 897 | 924 | |
| 898 | 925 | { |
| 899 | 926 | const result_reg: MockRegister2 = .r1; |
| ... | ... | @@ -914,7 +941,7 @@ test "allocRegs: normal usage" { |
| 914 | 941 | const lock = function.register_manager.lockReg(result_reg); |
| 915 | 942 | defer if (lock) |reg| function.register_manager.unlockReg(reg); |
| 916 | 943 | |
| 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 | 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 | 956 | }; |
| 930 | 957 | defer function.deinit(); |
| 931 | 958 | |
| 932 | | const reg_class = MockFunction2.reg_class; |
| 959 | const gp = MockRegister2.gp; |
| 933 | 960 | |
| 934 | 961 | { |
| 935 | 962 | const result_reg: MockRegister2 = .r1; |
| ... | ... | @@ -938,12 +965,12 @@ test "allocRegs: selectively reducing register pressure" { |
| 938 | 965 | |
| 939 | 966 | // Here, we don't defer unlock because we manually unlock |
| 940 | 967 | // 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); |
| 942 | 969 | |
| 943 | 970 | try function.genAdd(result_reg, regs[0], regs[1]); |
| 944 | 971 | function.register_manager.unlockReg(lock.?); |
| 945 | 972 | |
| 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 | 974 | try function.genAdd(result_reg, result_reg, extra_summand_reg); |
| 948 | 975 | } |
| 949 | 976 | } |
| ... | ... | @@ -974,3 +1001,39 @@ test "getReg" { |
| 974 | 1001 | try expect(!function.register_manager.isRegFree(.r3)); |
| 975 | 1002 | try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r3}, function.spilled.items); |
| 976 | 1003 | } |
| 1004 | |
| 1005 | test "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 | } |