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