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 auto &LegacyInfo = L.getLegacyLegalizerInfo(); 52 // Typical RISCy set of operations based on AArch64. 53 for (unsigned Op : {G_ADD, G_SUB}) { 54 for (unsigned Size : {32, 64}) 55 LegacyInfo.setAction({Op, 0, LLT::scalar(Size)}, 56 LegacyLegalizeActions::Legal); 57 LegacyInfo.setLegalizeScalarToDifferentSizeStrategy( 58 Op, 0, LegacyLegalizerInfo::widenToLargerTypesAndNarrowToLargest); 59 } 60 61 LegacyInfo.computeTables(); 62 63 for (unsigned opcode : {G_ADD, G_SUB}) { 64 // Check we infer the correct types and actually do what we're told. 65 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(8)}}), 66 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32))); 67 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(16)}}), 68 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32))); 69 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(32)}}), 70 LegalizeActionStep(Legal, 0, LLT{})); 71 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(64)}}), 72 LegalizeActionStep(Legal, 0, LLT{})); 73 74 // Make sure the default for over-sized types applies. 75 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(128)}}), 76 LegalizeActionStep(NarrowScalar, 0, LLT::scalar(64))); 77 // Make sure we also handle unusual sizes 78 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(1)}}), 79 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32))); 80 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(31)}}), 81 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32))); 82 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(33)}}), 83 LegalizeActionStep(WidenScalar, 0, LLT::scalar(64))); 84 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(63)}}), 85 LegalizeActionStep(WidenScalar, 0, LLT::scalar(64))); 86 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(65)}}), 87 LegalizeActionStep(NarrowScalar, 0, LLT::scalar(64))); 88 } 89 } 90 91 TEST(LegalizerInfoTest, VectorRISC) { 92 using namespace TargetOpcode; 93 LegalizerInfo L; 94 auto &LegacyInfo = L.getLegacyLegalizerInfo(); 95 // Typical RISCy set of operations based on ARM. 96 LegacyInfo.setAction({G_ADD, LLT::fixed_vector(8, 8)}, 97 LegacyLegalizeActions::Legal); 98 LegacyInfo.setAction({G_ADD, LLT::fixed_vector(16, 8)}, 99 LegacyLegalizeActions::Legal); 100 LegacyInfo.setAction({G_ADD, LLT::fixed_vector(4, 16)}, 101 LegacyLegalizeActions::Legal); 102 LegacyInfo.setAction({G_ADD, LLT::fixed_vector(8, 16)}, 103 LegacyLegalizeActions::Legal); 104 LegacyInfo.setAction({G_ADD, LLT::fixed_vector(2, 32)}, 105 LegacyLegalizeActions::Legal); 106 LegacyInfo.setAction({G_ADD, LLT::fixed_vector(4, 32)}, 107 LegacyLegalizeActions::Legal); 108 109 LegacyInfo.setLegalizeVectorElementToDifferentSizeStrategy( 110 G_ADD, 0, LegacyLegalizerInfo::widenToLargerTypesUnsupportedOtherwise); 111 112 LegacyInfo.setAction({G_ADD, 0, LLT::scalar(32)}, 113 LegacyLegalizeActions::Legal); 114 115 LegacyInfo.computeTables(); 116 117 // Check we infer the correct types and actually do what we're told for some 118 // simple cases. 119 EXPECT_EQ(L.getAction({G_ADD, {LLT::fixed_vector(8, 8)}}), 120 LegalizeActionStep(Legal, 0, LLT{})); 121 EXPECT_EQ(L.getAction({G_ADD, {LLT::fixed_vector(8, 7)}}), 122 LegalizeActionStep(WidenScalar, 0, LLT::fixed_vector(8, 8))); 123 EXPECT_EQ(L.getAction({G_ADD, {LLT::fixed_vector(2, 8)}}), 124 LegalizeActionStep(MoreElements, 0, LLT::fixed_vector(8, 8))); 125 EXPECT_EQ(L.getAction({G_ADD, {LLT::fixed_vector(8, 32)}}), 126 LegalizeActionStep(FewerElements, 0, LLT::fixed_vector(4, 32))); 127 // Check a few non-power-of-2 sizes: 128 EXPECT_EQ(L.getAction({G_ADD, {LLT::fixed_vector(3, 3)}}), 129 LegalizeActionStep(WidenScalar, 0, LLT::fixed_vector(3, 8))); 130 EXPECT_EQ(L.getAction({G_ADD, {LLT::fixed_vector(3, 8)}}), 131 LegalizeActionStep(MoreElements, 0, LLT::fixed_vector(8, 8))); 132 } 133 134 TEST(LegalizerInfoTest, MultipleTypes) { 135 using namespace TargetOpcode; 136 LegalizerInfo L; 137 auto &LegacyInfo = L.getLegacyLegalizerInfo(); 138 LLT p0 = LLT::pointer(0, 64); 139 LLT s64 = LLT::scalar(64); 140 141 // Typical RISCy set of operations based on AArch64. 142 LegacyInfo.setAction({G_PTRTOINT, 0, s64}, LegacyLegalizeActions::Legal); 143 LegacyInfo.setAction({G_PTRTOINT, 1, p0}, LegacyLegalizeActions::Legal); 144 145 LegacyInfo.setLegalizeScalarToDifferentSizeStrategy( 146 G_PTRTOINT, 0, LegacyLegalizerInfo::widenToLargerTypesAndNarrowToLargest); 147 148 LegacyInfo.computeTables(); 149 150 // Check we infer the correct types and actually do what we're told. 151 EXPECT_EQ(L.getAction({G_PTRTOINT, {s64, p0}}), 152 LegalizeActionStep(Legal, 0, LLT{})); 153 154 // Make sure we also handle unusual sizes 155 EXPECT_EQ( 156 L.getAction({G_PTRTOINT, {LLT::scalar(65), s64}}), 157 LegalizeActionStep(NarrowScalar, 0, s64)); 158 EXPECT_EQ( 159 L.getAction({G_PTRTOINT, {s64, LLT::pointer(0, 32)}}), 160 LegalizeActionStep(Unsupported, 1, LLT::pointer(0, 32))); 161 } 162 163 TEST(LegalizerInfoTest, MultipleSteps) { 164 using namespace TargetOpcode; 165 LegalizerInfo L; 166 auto &LegacyInfo = L.getLegacyLegalizerInfo(); 167 LLT s32 = LLT::scalar(32); 168 LLT s64 = LLT::scalar(64); 169 170 LegacyInfo.setLegalizeScalarToDifferentSizeStrategy( 171 G_UREM, 0, LegacyLegalizerInfo::widenToLargerTypesUnsupportedOtherwise); 172 LegacyInfo.setAction({G_UREM, 0, s32}, LegacyLegalizeActions::Lower); 173 LegacyInfo.setAction({G_UREM, 0, s64}, LegacyLegalizeActions::Lower); 174 175 LegacyInfo.computeTables(); 176 177 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(16)}}), 178 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32))); 179 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(32)}}), 180 LegalizeActionStep(Lower, 0, LLT::scalar(32))); 181 } 182 183 TEST(LegalizerInfoTest, SizeChangeStrategy) { 184 using namespace TargetOpcode; 185 LegalizerInfo L; 186 auto &LegacyInfo = L.getLegacyLegalizerInfo(); 187 for (unsigned Size : {1, 8, 16, 32}) 188 LegacyInfo.setAction({G_UREM, 0, LLT::scalar(Size)}, 189 LegacyLegalizeActions::Legal); 190 191 LegacyInfo.setLegalizeScalarToDifferentSizeStrategy( 192 G_UREM, 0, LegacyLegalizerInfo::widenToLargerTypesUnsupportedOtherwise); 193 LegacyInfo.computeTables(); 194 195 // Check we infer the correct types and actually do what we're told. 196 for (unsigned Size : {1, 8, 16, 32}) { 197 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(Size)}}), 198 LegalizeActionStep(Legal, 0, LLT{})); 199 } 200 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(2)}}), 201 LegalizeActionStep(WidenScalar, 0, LLT::scalar(8))); 202 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(7)}}), 203 LegalizeActionStep(WidenScalar, 0, LLT::scalar(8))); 204 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(9)}}), 205 LegalizeActionStep(WidenScalar, 0, LLT::scalar(16))); 206 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(17)}}), 207 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32))); 208 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(31)}}), 209 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32))); 210 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(33)}}), 211 LegalizeActionStep(Unsupported, 0, LLT::scalar(33))); 212 } 213 } 214 215 #define EXPECT_ACTION(Action, Index, Type, Query) \ 216 do { \ 217 auto A = LI.getAction(Query); \ 218 EXPECT_EQ(LegalizeActionStep(Action, Index, Type), A) << A; \ 219 } while (0) 220 221 TEST(LegalizerInfoTest, RuleSets) { 222 using namespace TargetOpcode; 223 224 const LLT s5 = LLT::scalar(5); 225 const LLT s8 = LLT::scalar(8); 226 const LLT s16 = LLT::scalar(16); 227 const LLT s32 = LLT::scalar(32); 228 const LLT s33 = LLT::scalar(33); 229 const LLT s64 = LLT::scalar(64); 230 231 const LLT v2s5 = LLT::fixed_vector(2, 5); 232 const LLT v2s8 = LLT::fixed_vector(2, 8); 233 const LLT v2s16 = LLT::fixed_vector(2, 16); 234 const LLT v2s32 = LLT::fixed_vector(2, 32); 235 const LLT v3s32 = LLT::fixed_vector(3, 32); 236 const LLT v4s32 = LLT::fixed_vector(4, 32); 237 const LLT v2s33 = LLT::fixed_vector(2, 33); 238 const LLT v2s64 = LLT::fixed_vector(2, 64); 239 240 const LLT p0 = LLT::pointer(0, 32); 241 const LLT v3p0 = LLT::fixed_vector(3, p0); 242 const LLT v4p0 = LLT::fixed_vector(4, p0); 243 244 { 245 LegalizerInfo LI; 246 auto &LegacyInfo = LI.getLegacyLegalizerInfo(); 247 248 LI.getActionDefinitionsBuilder(G_IMPLICIT_DEF) 249 .legalFor({v4s32, v4p0}) 250 .moreElementsToNextPow2(0); 251 LegacyInfo.computeTables(); 252 253 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_IMPLICIT_DEF, {s32})); 254 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_IMPLICIT_DEF, {v2s32})); 255 EXPECT_ACTION(MoreElements, 0, v4p0, LegalityQuery(G_IMPLICIT_DEF, {v3p0})); 256 EXPECT_ACTION(MoreElements, 0, v4s32, LegalityQuery(G_IMPLICIT_DEF, {v3s32})); 257 } 258 259 // Test minScalarOrElt 260 { 261 LegalizerInfo LI; 262 auto &LegacyInfo = LI.getLegacyLegalizerInfo(); 263 LI.getActionDefinitionsBuilder(G_OR) 264 .legalFor({s32}) 265 .minScalarOrElt(0, s32); 266 LegacyInfo.computeTables(); 267 268 EXPECT_ACTION(WidenScalar, 0, s32, LegalityQuery(G_OR, {s16})); 269 EXPECT_ACTION(WidenScalar, 0, v2s32, LegalityQuery(G_OR, {v2s16})); 270 } 271 272 // Test maxScalarOrELt 273 { 274 LegalizerInfo LI; 275 auto &LegacyInfo = LI.getLegacyLegalizerInfo(); 276 LI.getActionDefinitionsBuilder(G_AND) 277 .legalFor({s16}) 278 .maxScalarOrElt(0, s16); 279 LegacyInfo.computeTables(); 280 281 EXPECT_ACTION(NarrowScalar, 0, s16, LegalityQuery(G_AND, {s32})); 282 EXPECT_ACTION(NarrowScalar, 0, v2s16, LegalityQuery(G_AND, {v2s32})); 283 } 284 285 // Test clampScalarOrElt 286 { 287 LegalizerInfo LI; 288 auto &LegacyInfo = LI.getLegacyLegalizerInfo(); 289 LI.getActionDefinitionsBuilder(G_XOR) 290 .legalFor({s16}) 291 .clampScalarOrElt(0, s16, s32); 292 LegacyInfo.computeTables(); 293 294 EXPECT_ACTION(NarrowScalar, 0, s32, LegalityQuery(G_XOR, {s64})); 295 EXPECT_ACTION(WidenScalar, 0, s16, LegalityQuery(G_XOR, {s8})); 296 297 // Make sure the number of elements is preserved. 298 EXPECT_ACTION(NarrowScalar, 0, v2s32, LegalityQuery(G_XOR, {v2s64})); 299 EXPECT_ACTION(WidenScalar, 0, v2s16, LegalityQuery(G_XOR, {v2s8})); 300 } 301 302 // Test minScalar 303 { 304 LegalizerInfo LI; 305 auto &LegacyInfo = LI.getLegacyLegalizerInfo(); 306 LI.getActionDefinitionsBuilder(G_OR) 307 .legalFor({s32}) 308 .minScalar(0, s32); 309 LegacyInfo.computeTables(); 310 311 // Only handle scalars, ignore vectors. 312 EXPECT_ACTION(WidenScalar, 0, s32, LegalityQuery(G_OR, {s16})); 313 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_OR, {v2s16})); 314 } 315 316 // Test maxScalar 317 { 318 LegalizerInfo LI; 319 auto &LegacyInfo = LI.getLegacyLegalizerInfo(); 320 LI.getActionDefinitionsBuilder(G_AND) 321 .legalFor({s16}) 322 .maxScalar(0, s16); 323 LegacyInfo.computeTables(); 324 325 // Only handle scalars, ignore vectors. 326 EXPECT_ACTION(NarrowScalar, 0, s16, LegalityQuery(G_AND, {s32})); 327 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_AND, {v2s32})); 328 } 329 330 // Test clampScalar 331 { 332 LegalizerInfo LI; 333 auto &LegacyInfo = LI.getLegacyLegalizerInfo(); 334 335 LI.getActionDefinitionsBuilder(G_XOR) 336 .legalFor({s16}) 337 .clampScalar(0, s16, s32); 338 LegacyInfo.computeTables(); 339 340 EXPECT_ACTION(NarrowScalar, 0, s32, LegalityQuery(G_XOR, {s64})); 341 EXPECT_ACTION(WidenScalar, 0, s16, LegalityQuery(G_XOR, {s8})); 342 343 // Only handle scalars, ignore vectors. 344 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_XOR, {v2s64})); 345 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_XOR, {v2s8})); 346 } 347 348 // Test widenScalarOrEltToNextPow2 349 { 350 LegalizerInfo LI; 351 auto &LegacyInfo = LI.getLegacyLegalizerInfo(); 352 353 LI.getActionDefinitionsBuilder(G_AND) 354 .legalFor({s32}) 355 .widenScalarOrEltToNextPow2(0, 32); 356 LegacyInfo.computeTables(); 357 358 // Handle scalars and vectors 359 EXPECT_ACTION(WidenScalar, 0, s32, LegalityQuery(G_AND, {s5})); 360 EXPECT_ACTION(WidenScalar, 0, v2s32, LegalityQuery(G_AND, {v2s5})); 361 EXPECT_ACTION(WidenScalar, 0, s64, LegalityQuery(G_AND, {s33})); 362 EXPECT_ACTION(WidenScalar, 0, v2s64, LegalityQuery(G_AND, {v2s33})); 363 } 364 365 // Test widenScalarToNextPow2 366 { 367 LegalizerInfo LI; 368 auto &LegacyInfo = LI.getLegacyLegalizerInfo(); 369 370 LI.getActionDefinitionsBuilder(G_AND) 371 .legalFor({s32}) 372 .widenScalarToNextPow2(0, 32); 373 LegacyInfo.computeTables(); 374 375 EXPECT_ACTION(WidenScalar, 0, s32, LegalityQuery(G_AND, {s5})); 376 EXPECT_ACTION(WidenScalar, 0, s64, LegalityQuery(G_AND, {s33})); 377 378 // Do nothing for vectors. 379 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_AND, {v2s5})); 380 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_AND, {v2s33})); 381 } 382 } 383 384 TEST(LegalizerInfoTest, MMOAlignment) { 385 using namespace TargetOpcode; 386 387 const LLT s32 = LLT::scalar(32); 388 const LLT p0 = LLT::pointer(0, 64); 389 390 { 391 LegalizerInfo LI; 392 auto &LegacyInfo = LI.getLegacyLegalizerInfo(); 393 LI.getActionDefinitionsBuilder(G_LOAD) 394 .legalForTypesWithMemDesc({{s32, p0, 32, 32}}); 395 396 LegacyInfo.computeTables(); 397 398 EXPECT_ACTION(Legal, 0, LLT(), 399 LegalityQuery(G_LOAD, {s32, p0}, 400 LegalityQuery::MemDesc{ 401 32, 32, AtomicOrdering::NotAtomic})); 402 EXPECT_ACTION(Unsupported, 0, LLT(), 403 LegalityQuery(G_LOAD, {s32, p0}, 404 LegalityQuery::MemDesc{ 405 32, 16, AtomicOrdering::NotAtomic })); 406 EXPECT_ACTION(Unsupported, 0, LLT(), 407 LegalityQuery(G_LOAD, {s32, p0}, 408 LegalityQuery::MemDesc{ 409 32, 8, AtomicOrdering::NotAtomic})); 410 } 411 412 // Test that the maximum supported alignment value isn't truncated 413 { 414 // Maximum IR defined alignment in bytes. 415 const uint64_t MaxAlignment = UINT64_C(1) << 29; 416 const uint64_t MaxAlignInBits = 8 * MaxAlignment; 417 LegalizerInfo LI; 418 auto &LegacyInfo = LI.getLegacyLegalizerInfo(); 419 LI.getActionDefinitionsBuilder(G_LOAD) 420 .legalForTypesWithMemDesc({{s32, p0, 32, MaxAlignInBits}}); 421 422 LegacyInfo.computeTables(); 423 424 EXPECT_ACTION(Legal, 0, LLT(), 425 LegalityQuery(G_LOAD, {s32, p0}, 426 LegalityQuery::MemDesc{32, 427 MaxAlignInBits, AtomicOrdering::NotAtomic})); 428 EXPECT_ACTION(Unsupported, 0, LLT(), 429 LegalityQuery(G_LOAD, {s32, p0}, 430 LegalityQuery::MemDesc{ 431 32, 8, AtomicOrdering::NotAtomic })); 432 } 433 } 434 435 // This code sequence doesn't do anything, but it covers a previously uncovered 436 // codepath that used to crash in MSVC x86_32 debug mode. 437 TEST(LegalizerInfoTest, MSVCDebugMiscompile) { 438 const LLT S1 = LLT::scalar(1); 439 const LLT P0 = LLT::pointer(0, 32); 440 LegalizerInfo LI; 441 auto Builder = LI.getActionDefinitionsBuilder(TargetOpcode::G_PTRTOINT); 442 (void)Builder.legalForCartesianProduct({S1}, {P0}); 443 } 444