xref: /llvm-project/llvm/unittests/CodeGen/GlobalISel/LegalizerTest.cpp (revision d76202d3e3572aca7e36db90cfd5ba703bc4d415)
1 //===- LegalizerTest.cpp --------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "GISelMITest.h"
10 #include "llvm/CodeGen/GlobalISel/Legalizer.h"
11 
12 using namespace LegalizeActions;
13 using namespace LegalizeMutations;
14 using namespace LegalityPredicates;
15 
16 namespace {
17 
18 ::testing::AssertionResult isNullMIPtr(const MachineInstr *MI) {
19   if (MI == nullptr)
20     return ::testing::AssertionSuccess();
21   std::string MIBuffer;
22   raw_string_ostream MISStream(MIBuffer);
23   MI->print(MISStream, /*IsStandalone=*/true, /*SkipOpers=*/false,
24             /*SkipDebugLoc=*/false, /*AddNewLine=*/false);
25   return ::testing::AssertionFailure()
26          << "unable to legalize instruction: " << MISStream.str();
27 }
28 
29 DefineLegalizerInfo(ALegalizer, {
30   auto p0 = LLT::pointer(0, 64);
31   auto v2s8 = LLT::vector(2, 8);
32   auto v2s16 = LLT::vector(2, 16);
33   getActionDefinitionsBuilder(G_LOAD)
34       .legalForTypesWithMemDesc({{s16, p0, 8, 8}})
35       .scalarize(0)
36       .clampScalar(0, s16, s16);
37   getActionDefinitionsBuilder(G_PTR_ADD).legalFor({{p0, s64}});
38   getActionDefinitionsBuilder(G_CONSTANT).legalFor({s32, s64});
39   getActionDefinitionsBuilder(G_BUILD_VECTOR)
40       .legalFor({{v2s16, s16}})
41       .clampScalar(1, s16, s16);
42   getActionDefinitionsBuilder(G_BUILD_VECTOR_TRUNC).legalFor({{v2s8, s16}});
43   getActionDefinitionsBuilder(G_ANYEXT).legalFor({{s32, s16}});
44   getActionDefinitionsBuilder(G_ZEXT).legalFor({{s32, s16}});
45   getActionDefinitionsBuilder(G_SEXT).legalFor({{s32, s16}});
46   getActionDefinitionsBuilder(G_AND).legalFor({s32});
47   getActionDefinitionsBuilder(G_SEXT_INREG).lower();
48   getActionDefinitionsBuilder(G_ASHR).legalFor({{s32, s32}});
49   getActionDefinitionsBuilder(G_SHL).legalFor({{s32, s32}});
50 })
51 
52 TEST_F(GISelMITest, BasicLegalizerTest) {
53   StringRef MIRString = R"(
54     %vptr:_(p0) = COPY $x4
55     %v:_(<2 x s8>) = G_LOAD %vptr:_(p0) :: (load 2, align 1)
56     $h4 = COPY %v:_(<2 x s8>)
57   )";
58   setUp(MIRString.rtrim(' '));
59   if (!TM)
60     return;
61 
62   ALegalizerInfo LI(MF->getSubtarget());
63 
64   Legalizer::MFResult Result =
65       Legalizer::legalizeMachineFunction(*MF, LI, {}, B);
66 
67   EXPECT_TRUE(isNullMIPtr(Result.FailedOn));
68   EXPECT_TRUE(Result.Changed);
69 
70   StringRef CheckString = R"(
71     CHECK:      %vptr:_(p0) = COPY $x4
72     CHECK-NEXT: [[LOAD_0:%[0-9]+]]:_(s16) = G_LOAD %vptr:_(p0) :: (load 1)
73     CHECK-NEXT: [[OFFSET_1:%[0-9]+]]:_(s64) = G_CONSTANT i64 1
74     CHECK-NEXT: [[VPTR_1:%[0-9]+]]:_(p0) = G_PTR_ADD %vptr:_, [[OFFSET_1]]:_(s64)
75     CHECK-NEXT: [[LOAD_1:%[0-9]+]]:_(s16) = G_LOAD [[VPTR_1]]:_(p0) :: (load 1)
76     CHECK-NEXT: [[V0:%[0-9]+]]:_(s16) = COPY [[LOAD_0]]:_(s16)
77     CHECK-NEXT: [[V1:%[0-9]+]]:_(s16) = COPY [[LOAD_1]]:_(s16)
78     CHECK-NEXT: %v:_(<2 x s8>) = G_BUILD_VECTOR_TRUNC [[V0]]:_(s16), [[V1]]:_(s16)
79     CHECK-NEXT: $h4 = COPY %v:_(<2 x s8>)
80   )";
81 
82   EXPECT_TRUE(CheckMachineFunction(*MF, CheckString)) << *MF;
83 }
84 
85 // Making sure the legalization finishes successfully w/o failure to combine
86 // away all the legalization artifacts regardless of the order of their
87 // creation.
88 TEST_F(GISelMITest, UnorderedArtifactCombiningTest) {
89   StringRef MIRString = R"(
90     %vptr:_(p0) = COPY $x4
91     %v:_(<2 x s8>) = G_LOAD %vptr:_(p0) :: (load 2, align 1)
92     %v0:_(s8), %v1:_(s8) = G_UNMERGE_VALUES %v:_(<2 x s8>)
93     %v0_ext:_(s16) = G_ANYEXT %v0:_(s8)
94     $h4 = COPY %v0_ext:_(s16)
95   )";
96   setUp(MIRString.rtrim(' '));
97   if (!TM)
98     return;
99 
100   ALegalizerInfo LI(MF->getSubtarget());
101 
102   // The events here unfold as follows:
103   // 1. First, the function is scanned pre-forming the worklist of artifacts:
104   //
105   //      UNMERGE  (1): pushed into the worklist first, will be processed last.
106   //         |
107   //       ANYEXT  (2)
108   //
109   // 2. Second, the load is scalarized, and then its destination is widened,
110   //    forming the following chain of legalization artifacts:
111   //
112   //        TRUNC  (4): created last, will be processed first.
113   //         |
114   // BUILD_VECTOR  (3)
115   //         |
116   //      UNMERGE  (1): pushed into the worklist first, will be processed last.
117   //         |
118   //       ANYEXT  (2)
119   //
120   // 3. Third, the artifacts are attempted to be combined in pairs, looking
121   //    through the def-use chain from the roots towards the leafs, visiting the
122   //    roots in order they happen to be in the worklist:
123   //      (4) - (trunc):                  can not be combined;
124   //      (3) - (build_vector (trunc)):   can not be combined;
125   //      (2) - (anyext (unmerge)):       can not be combined;
126   //      (1) - (unmerge (build_vector)): combined and eliminated;
127   //
128   //    leaving the function in the following state:
129   //
130   //        TRUNC  (1): moved to non-artifact instructions worklist first.
131   //         |
132   //       ANYEXT  (2): also moved to non-artifact instructions worklist.
133   //
134   //    Every other instruction is successfully legalized in full.
135   //    If combining (unmerge (build_vector)) does not re-insert every artifact
136   //    that had its def-use chain modified (shortened) into the artifact
137   //    worklist (here it's just ANYEXT), the process moves on onto the next
138   //    outer loop iteration of the top-level legalization algorithm here, w/o
139   //    performing all the artifact combines possible. Let's consider this
140   //    scenario first:
141   //  4.A. Neither TRUNC, nor ANYEXT can be legalized in isolation, both of them
142   //       get moved to the retry worklist, but no additional artifacts were
143   //       created in the process, thus algorithm concludes no progress could be
144   //       made, and fails.
145   //  4.B. If, however, combining (unmerge (build_vector)) had re-inserted
146   //       ANYEXT into the worklist (as ANYEXT's source changes, not by value,
147   //       but by implementation), (anyext (trunc)) combine happens next, which
148   //       fully eliminates all the artifacts and legalization succeeds.
149   //
150   //  We're looking into making sure that (4.B) happens here, not (4.A). Note
151   //  that in that case the first scan through the artifacts worklist, while not
152   //  being done in any guaranteed order, only needs to find the innermost
153   //  pair(s) of artifacts that could be immediately combined out. After that
154   //  the process follows def-use chains, making them shorter at each step, thus
155   //  combining everything that can be combined in O(n) time.
156   Legalizer::MFResult Result =
157       Legalizer::legalizeMachineFunction(*MF, LI, {}, B);
158 
159   EXPECT_TRUE(isNullMIPtr(Result.FailedOn));
160   EXPECT_TRUE(Result.Changed);
161 
162   StringRef CheckString = R"(
163     CHECK:      %vptr:_(p0) = COPY $x4
164     CHECK-NEXT: [[LOAD_0:%[0-9]+]]:_(s16) = G_LOAD %vptr:_(p0) :: (load 1)
165     CHECK:      %v0_ext:_(s16) = COPY [[LOAD_0]]:_(s16)
166     CHECK-NEXT: $h4 = COPY %v0_ext:_(s16)
167   )";
168 
169   EXPECT_TRUE(CheckMachineFunction(*MF, CheckString)) << *MF;
170 }
171 
172 TEST_F(GISelMITest, UnorderedArtifactCombiningManyCopiesTest) {
173   StringRef MIRString = R"(
174     %vptr:_(p0) = COPY $x4
175     %v:_(<2 x s8>) = G_LOAD %vptr:_(p0) :: (load 2, align 1)
176     %vc0:_(<2 x s8>) = COPY %v:_(<2 x s8>)
177     %vc1:_(<2 x s8>) = COPY %v:_(<2 x s8>)
178     %vc00:_(s8), %vc01:_(s8) = G_UNMERGE_VALUES %vc0:_(<2 x s8>)
179     %vc10:_(s8), %vc11:_(s8) = G_UNMERGE_VALUES %vc1:_(<2 x s8>)
180     %v0t:_(s8) = COPY %vc00:_(s8)
181     %v0:_(s8) = COPY %v0t:_(s8)
182     %v1t:_(s8) = COPY %vc11:_(s8)
183     %v1:_(s8) = COPY %v1t:_(s8)
184     %v0_zext:_(s32) = G_ZEXT %v0:_(s8)
185     %v1_sext:_(s32) = G_SEXT %v1:_(s8)
186     $w4 = COPY %v0_zext:_(s32)
187     $w5 = COPY %v1_sext:_(s32)
188   )";
189   setUp(MIRString.rtrim(' '));
190   if (!TM)
191     return;
192 
193   ALegalizerInfo LI(MF->getSubtarget());
194 
195   Legalizer::MFResult Result =
196       Legalizer::legalizeMachineFunction(*MF, LI, {}, B);
197 
198   EXPECT_TRUE(isNullMIPtr(Result.FailedOn));
199   EXPECT_TRUE(Result.Changed);
200 
201   StringRef CheckString = R"(
202     CHECK:      %vptr:_(p0) = COPY $x4
203     CHECK-NEXT: [[LOAD_0:%[0-9]+]]:_(s16) = G_LOAD %vptr:_(p0) :: (load 1)
204     CHECK-NEXT: [[OFFSET_1:%[0-9]+]]:_(s64) = G_CONSTANT i64 1
205     CHECK-NEXT: [[VPTR_1:%[0-9]+]]:_(p0) = G_PTR_ADD %vptr:_, [[OFFSET_1]]:_(s64)
206     CHECK-NEXT: [[LOAD_1:%[0-9]+]]:_(s16) = G_LOAD [[VPTR_1]]:_(p0) :: (load 1)
207     CHECK-NEXT: [[FF_MASK:%[0-9]+]]:_(s32) = G_CONSTANT i32 255
208     CHECK-NEXT: [[V0_EXT:%[0-9]+]]:_(s32) = G_ANYEXT [[LOAD_0]]:_(s16)
209     CHECK-NEXT: %v0_zext:_(s32) = G_AND [[V0_EXT]]:_, [[FF_MASK]]:_
210     CHECK-NEXT: [[V1_EXT:%[0-9]+]]:_(s32) = G_ANYEXT [[LOAD_1]]:_(s16)
211     CHECK-NEXT: [[SHAMNT:%[0-9]+]]:_(s32) = G_CONSTANT i32 24
212     CHECK-NEXT: [[V1_SHL:%[0-9]+]]:_(s32) = G_SHL [[V1_EXT]]:_, [[SHAMNT]]:_(s32)
213     CHECK-NEXT: %v1_sext:_(s32) = G_ASHR [[V1_SHL]]:_, [[SHAMNT]]:_(s32)
214     CHECK-NEXT: $w4 = COPY %v0_zext:_(s32)
215     CHECK-NEXT: $w5 = COPY %v1_sext:_(s32)
216   )";
217 
218   EXPECT_TRUE(CheckMachineFunction(*MF, CheckString)) << *MF;
219 }
220 
221 } // namespace
222