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