1 //===- llvm/unittest/CodeGen/GlobalISel/LegalizerInfoTest.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/LegalizerInfo.h" 10 #include "llvm/CodeGen/TargetOpcodes.h" 11 #include "GISelMITest.h" 12 #include "gtest/gtest.h" 13 14 using namespace llvm; 15 using namespace LegalizeActions; 16 17 // Define a couple of pretty printers to help debugging when things go wrong. 18 namespace llvm { 19 std::ostream & 20 operator<<(std::ostream &OS, const LegalizeAction Act) { 21 switch (Act) { 22 case Lower: OS << "Lower"; break; 23 case Legal: OS << "Legal"; break; 24 case NarrowScalar: OS << "NarrowScalar"; break; 25 case WidenScalar: OS << "WidenScalar"; break; 26 case FewerElements: OS << "FewerElements"; break; 27 case MoreElements: OS << "MoreElements"; break; 28 case Libcall: OS << "Libcall"; break; 29 case Custom: OS << "Custom"; break; 30 case Bitcast: OS << "Bitcast"; break; 31 case Unsupported: OS << "Unsupported"; break; 32 case NotFound: OS << "NotFound"; break; 33 case UseLegacyRules: OS << "UseLegacyRules"; break; 34 } 35 return OS; 36 } 37 38 std::ostream &operator<<(std::ostream &OS, const llvm::LegalizeActionStep Ty) { 39 OS << "LegalizeActionStep(" << Ty.Action << ", " << Ty.TypeIdx << ", " 40 << Ty.NewType << ')'; 41 return OS; 42 } 43 } 44 45 namespace { 46 47 48 TEST(LegalizerInfoTest, ScalarRISC) { 49 using namespace TargetOpcode; 50 LegalizerInfo L; 51 // Typical RISCy set of operations based on AArch64. 52 for (unsigned Op : {G_ADD, G_SUB}) { 53 for (unsigned Size : {32, 64}) 54 L.setAction({Op, 0, LLT::scalar(Size)}, Legal); 55 L.setLegalizeScalarToDifferentSizeStrategy( 56 Op, 0, LegalizerInfo::widenToLargerTypesAndNarrowToLargest); 57 } 58 59 L.computeTables(); 60 61 for (unsigned opcode : {G_ADD, G_SUB}) { 62 // Check we infer the correct types and actually do what we're told. 63 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(8)}}), 64 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32))); 65 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(16)}}), 66 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32))); 67 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(32)}}), 68 LegalizeActionStep(Legal, 0, LLT{})); 69 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(64)}}), 70 LegalizeActionStep(Legal, 0, LLT{})); 71 72 // Make sure the default for over-sized types applies. 73 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(128)}}), 74 LegalizeActionStep(NarrowScalar, 0, LLT::scalar(64))); 75 // Make sure we also handle unusual sizes 76 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(1)}}), 77 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32))); 78 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(31)}}), 79 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32))); 80 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(33)}}), 81 LegalizeActionStep(WidenScalar, 0, LLT::scalar(64))); 82 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(63)}}), 83 LegalizeActionStep(WidenScalar, 0, LLT::scalar(64))); 84 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(65)}}), 85 LegalizeActionStep(NarrowScalar, 0, LLT::scalar(64))); 86 } 87 } 88 89 TEST(LegalizerInfoTest, VectorRISC) { 90 using namespace TargetOpcode; 91 LegalizerInfo L; 92 // Typical RISCy set of operations based on ARM. 93 L.setAction({G_ADD, LLT::vector(8, 8)}, Legal); 94 L.setAction({G_ADD, LLT::vector(16, 8)}, Legal); 95 L.setAction({G_ADD, LLT::vector(4, 16)}, Legal); 96 L.setAction({G_ADD, LLT::vector(8, 16)}, Legal); 97 L.setAction({G_ADD, LLT::vector(2, 32)}, Legal); 98 L.setAction({G_ADD, LLT::vector(4, 32)}, Legal); 99 100 L.setLegalizeVectorElementToDifferentSizeStrategy( 101 G_ADD, 0, LegalizerInfo::widenToLargerTypesUnsupportedOtherwise); 102 103 L.setAction({G_ADD, 0, LLT::scalar(32)}, Legal); 104 105 L.computeTables(); 106 107 // Check we infer the correct types and actually do what we're told for some 108 // simple cases. 109 EXPECT_EQ(L.getAction({G_ADD, {LLT::vector(8, 8)}}), 110 LegalizeActionStep(Legal, 0, LLT{})); 111 EXPECT_EQ(L.getAction({G_ADD, {LLT::vector(8, 7)}}), 112 LegalizeActionStep(WidenScalar, 0, LLT::vector(8, 8))); 113 EXPECT_EQ(L.getAction({G_ADD, {LLT::vector(2, 8)}}), 114 LegalizeActionStep(MoreElements, 0, LLT::vector(8, 8))); 115 EXPECT_EQ(L.getAction({G_ADD, {LLT::vector(8, 32)}}), 116 LegalizeActionStep(FewerElements, 0, LLT::vector(4, 32))); 117 // Check a few non-power-of-2 sizes: 118 EXPECT_EQ(L.getAction({G_ADD, {LLT::vector(3, 3)}}), 119 LegalizeActionStep(WidenScalar, 0, LLT::vector(3, 8))); 120 EXPECT_EQ(L.getAction({G_ADD, {LLT::vector(3, 8)}}), 121 LegalizeActionStep(MoreElements, 0, LLT::vector(8, 8))); 122 } 123 124 TEST(LegalizerInfoTest, MultipleTypes) { 125 using namespace TargetOpcode; 126 LegalizerInfo L; 127 LLT p0 = LLT::pointer(0, 64); 128 LLT s64 = LLT::scalar(64); 129 130 // Typical RISCy set of operations based on AArch64. 131 L.setAction({G_PTRTOINT, 0, s64}, Legal); 132 L.setAction({G_PTRTOINT, 1, p0}, Legal); 133 134 L.setLegalizeScalarToDifferentSizeStrategy( 135 G_PTRTOINT, 0, LegalizerInfo::widenToLargerTypesAndNarrowToLargest); 136 137 L.computeTables(); 138 139 // Check we infer the correct types and actually do what we're told. 140 EXPECT_EQ(L.getAction({G_PTRTOINT, {s64, p0}}), 141 LegalizeActionStep(Legal, 0, LLT{})); 142 143 // Make sure we also handle unusual sizes 144 EXPECT_EQ( 145 L.getAction({G_PTRTOINT, {LLT::scalar(65), s64}}), 146 LegalizeActionStep(NarrowScalar, 0, s64)); 147 EXPECT_EQ( 148 L.getAction({G_PTRTOINT, {s64, LLT::pointer(0, 32)}}), 149 LegalizeActionStep(Unsupported, 1, LLT::pointer(0, 32))); 150 } 151 152 TEST(LegalizerInfoTest, MultipleSteps) { 153 using namespace TargetOpcode; 154 LegalizerInfo L; 155 LLT s32 = LLT::scalar(32); 156 LLT s64 = LLT::scalar(64); 157 158 L.setLegalizeScalarToDifferentSizeStrategy( 159 G_UREM, 0, LegalizerInfo::widenToLargerTypesUnsupportedOtherwise); 160 L.setAction({G_UREM, 0, s32}, Lower); 161 L.setAction({G_UREM, 0, s64}, Lower); 162 163 L.computeTables(); 164 165 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(16)}}), 166 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32))); 167 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(32)}}), 168 LegalizeActionStep(Lower, 0, LLT::scalar(32))); 169 } 170 171 TEST(LegalizerInfoTest, SizeChangeStrategy) { 172 using namespace TargetOpcode; 173 LegalizerInfo L; 174 for (unsigned Size : {1, 8, 16, 32}) 175 L.setAction({G_UREM, 0, LLT::scalar(Size)}, Legal); 176 177 L.setLegalizeScalarToDifferentSizeStrategy( 178 G_UREM, 0, LegalizerInfo::widenToLargerTypesUnsupportedOtherwise); 179 L.computeTables(); 180 181 // Check we infer the correct types and actually do what we're told. 182 for (unsigned Size : {1, 8, 16, 32}) { 183 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(Size)}}), 184 LegalizeActionStep(Legal, 0, LLT{})); 185 } 186 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(2)}}), 187 LegalizeActionStep(WidenScalar, 0, LLT::scalar(8))); 188 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(7)}}), 189 LegalizeActionStep(WidenScalar, 0, LLT::scalar(8))); 190 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(9)}}), 191 LegalizeActionStep(WidenScalar, 0, LLT::scalar(16))); 192 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(17)}}), 193 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32))); 194 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(31)}}), 195 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32))); 196 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(33)}}), 197 LegalizeActionStep(Unsupported, 0, LLT::scalar(33))); 198 } 199 } 200 201 #define EXPECT_ACTION(Action, Index, Type, Query) \ 202 do { \ 203 auto A = LI.getAction(Query); \ 204 EXPECT_EQ(LegalizeActionStep(Action, Index, Type), A) << A; \ 205 } while (0) 206 207 TEST(LegalizerInfoTest, RuleSets) { 208 using namespace TargetOpcode; 209 210 const LLT s5 = LLT::scalar(5); 211 const LLT s8 = LLT::scalar(8); 212 const LLT s16 = LLT::scalar(16); 213 const LLT s32 = LLT::scalar(32); 214 const LLT s33 = LLT::scalar(33); 215 const LLT s64 = LLT::scalar(64); 216 217 const LLT v2s5 = LLT::vector(2, 5); 218 const LLT v2s8 = LLT::vector(2, 8); 219 const LLT v2s16 = LLT::vector(2, 16); 220 const LLT v2s32 = LLT::vector(2, 32); 221 const LLT v3s32 = LLT::vector(3, 32); 222 const LLT v4s32 = LLT::vector(4, 32); 223 const LLT v2s33 = LLT::vector(2, 33); 224 const LLT v2s64 = LLT::vector(2, 64); 225 226 const LLT p0 = LLT::pointer(0, 32); 227 const LLT v3p0 = LLT::vector(3, p0); 228 const LLT v4p0 = LLT::vector(4, p0); 229 230 { 231 LegalizerInfo LI; 232 233 LI.getActionDefinitionsBuilder(G_IMPLICIT_DEF) 234 .legalFor({v4s32, v4p0}) 235 .moreElementsToNextPow2(0); 236 LI.computeTables(); 237 238 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_IMPLICIT_DEF, {s32})); 239 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_IMPLICIT_DEF, {v2s32})); 240 EXPECT_ACTION(MoreElements, 0, v4p0, LegalityQuery(G_IMPLICIT_DEF, {v3p0})); 241 EXPECT_ACTION(MoreElements, 0, v4s32, LegalityQuery(G_IMPLICIT_DEF, {v3s32})); 242 } 243 244 // Test minScalarOrElt 245 { 246 LegalizerInfo LI; 247 LI.getActionDefinitionsBuilder(G_OR) 248 .legalFor({s32}) 249 .minScalarOrElt(0, s32); 250 LI.computeTables(); 251 252 EXPECT_ACTION(WidenScalar, 0, s32, LegalityQuery(G_OR, {s16})); 253 EXPECT_ACTION(WidenScalar, 0, v2s32, LegalityQuery(G_OR, {v2s16})); 254 } 255 256 // Test maxScalarOrELt 257 { 258 LegalizerInfo LI; 259 LI.getActionDefinitionsBuilder(G_AND) 260 .legalFor({s16}) 261 .maxScalarOrElt(0, s16); 262 LI.computeTables(); 263 264 EXPECT_ACTION(NarrowScalar, 0, s16, LegalityQuery(G_AND, {s32})); 265 EXPECT_ACTION(NarrowScalar, 0, v2s16, LegalityQuery(G_AND, {v2s32})); 266 } 267 268 // Test clampScalarOrElt 269 { 270 LegalizerInfo LI; 271 LI.getActionDefinitionsBuilder(G_XOR) 272 .legalFor({s16}) 273 .clampScalarOrElt(0, s16, s32); 274 LI.computeTables(); 275 276 EXPECT_ACTION(NarrowScalar, 0, s32, LegalityQuery(G_XOR, {s64})); 277 EXPECT_ACTION(WidenScalar, 0, s16, LegalityQuery(G_XOR, {s8})); 278 279 // Make sure the number of elements is preserved. 280 EXPECT_ACTION(NarrowScalar, 0, v2s32, LegalityQuery(G_XOR, {v2s64})); 281 EXPECT_ACTION(WidenScalar, 0, v2s16, LegalityQuery(G_XOR, {v2s8})); 282 } 283 284 // Test minScalar 285 { 286 LegalizerInfo LI; 287 LI.getActionDefinitionsBuilder(G_OR) 288 .legalFor({s32}) 289 .minScalar(0, s32); 290 LI.computeTables(); 291 292 // Only handle scalars, ignore vectors. 293 EXPECT_ACTION(WidenScalar, 0, s32, LegalityQuery(G_OR, {s16})); 294 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_OR, {v2s16})); 295 } 296 297 // Test maxScalar 298 { 299 LegalizerInfo LI; 300 LI.getActionDefinitionsBuilder(G_AND) 301 .legalFor({s16}) 302 .maxScalar(0, s16); 303 LI.computeTables(); 304 305 // Only handle scalars, ignore vectors. 306 EXPECT_ACTION(NarrowScalar, 0, s16, LegalityQuery(G_AND, {s32})); 307 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_AND, {v2s32})); 308 } 309 310 // Test clampScalar 311 { 312 LegalizerInfo LI; 313 314 LI.getActionDefinitionsBuilder(G_XOR) 315 .legalFor({s16}) 316 .clampScalar(0, s16, s32); 317 LI.computeTables(); 318 319 EXPECT_ACTION(NarrowScalar, 0, s32, LegalityQuery(G_XOR, {s64})); 320 EXPECT_ACTION(WidenScalar, 0, s16, LegalityQuery(G_XOR, {s8})); 321 322 // Only handle scalars, ignore vectors. 323 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_XOR, {v2s64})); 324 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_XOR, {v2s8})); 325 } 326 327 // Test widenScalarOrEltToNextPow2 328 { 329 LegalizerInfo LI; 330 331 LI.getActionDefinitionsBuilder(G_AND) 332 .legalFor({s32}) 333 .widenScalarOrEltToNextPow2(0, 32); 334 LI.computeTables(); 335 336 // Handle scalars and vectors 337 EXPECT_ACTION(WidenScalar, 0, s32, LegalityQuery(G_AND, {s5})); 338 EXPECT_ACTION(WidenScalar, 0, v2s32, LegalityQuery(G_AND, {v2s5})); 339 EXPECT_ACTION(WidenScalar, 0, s64, LegalityQuery(G_AND, {s33})); 340 EXPECT_ACTION(WidenScalar, 0, v2s64, LegalityQuery(G_AND, {v2s33})); 341 } 342 343 // Test widenScalarToNextPow2 344 { 345 LegalizerInfo LI; 346 347 LI.getActionDefinitionsBuilder(G_AND) 348 .legalFor({s32}) 349 .widenScalarToNextPow2(0, 32); 350 LI.computeTables(); 351 352 EXPECT_ACTION(WidenScalar, 0, s32, LegalityQuery(G_AND, {s5})); 353 EXPECT_ACTION(WidenScalar, 0, s64, LegalityQuery(G_AND, {s33})); 354 355 // Do nothing for vectors. 356 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_AND, {v2s5})); 357 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_AND, {v2s33})); 358 } 359 } 360 361 TEST(LegalizerInfoTest, MMOAlignment) { 362 using namespace TargetOpcode; 363 364 const LLT s32 = LLT::scalar(32); 365 const LLT p0 = LLT::pointer(0, 64); 366 367 { 368 LegalizerInfo LI; 369 LI.getActionDefinitionsBuilder(G_LOAD) 370 .legalForTypesWithMemDesc({{s32, p0, 32, 32}}); 371 372 LI.computeTables(); 373 374 EXPECT_ACTION(Legal, 0, LLT(), 375 LegalityQuery(G_LOAD, {s32, p0}, 376 LegalityQuery::MemDesc{ 377 32, 32, AtomicOrdering::NotAtomic})); 378 EXPECT_ACTION(Unsupported, 0, LLT(), 379 LegalityQuery(G_LOAD, {s32, p0}, 380 LegalityQuery::MemDesc{ 381 32, 16, AtomicOrdering::NotAtomic })); 382 EXPECT_ACTION(Unsupported, 0, LLT(), 383 LegalityQuery(G_LOAD, {s32, p0}, 384 LegalityQuery::MemDesc{ 385 32, 8, AtomicOrdering::NotAtomic})); 386 } 387 388 // Test that the maximum supported alignment value isn't truncated 389 { 390 // Maximum IR defined alignment in bytes. 391 const uint64_t MaxAlignment = UINT64_C(1) << 29; 392 const uint64_t MaxAlignInBits = 8 * MaxAlignment; 393 LegalizerInfo LI; 394 LI.getActionDefinitionsBuilder(G_LOAD) 395 .legalForTypesWithMemDesc({{s32, p0, 32, MaxAlignInBits}}); 396 397 LI.computeTables(); 398 399 EXPECT_ACTION(Legal, 0, LLT(), 400 LegalityQuery(G_LOAD, {s32, p0}, 401 LegalityQuery::MemDesc{32, 402 MaxAlignInBits, AtomicOrdering::NotAtomic})); 403 EXPECT_ACTION(Unsupported, 0, LLT(), 404 LegalityQuery(G_LOAD, {s32, p0}, 405 LegalityQuery::MemDesc{ 406 32, 8, AtomicOrdering::NotAtomic })); 407 } 408 } 409