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 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::fixed_vector(2, 8); 35 auto v2s16 = LLT::fixed_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 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 from unknown-address + 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. 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 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 from unknown-address + 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