1 //=== lib/CodeGen/GlobalISel/AArch64PostLegalizerCombiner.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 // This performs post-legalization combines on generic MachineInstrs. 10 // 11 // Any combine that this pass performs must preserve instruction legality. 12 // Combines unconcerned with legality should be handled by the 13 // PreLegalizerCombiner instead. 14 // 15 //===----------------------------------------------------------------------===// 16 17 #include "AArch64TargetMachine.h" 18 #include "llvm/CodeGen/GlobalISel/Combiner.h" 19 #include "llvm/CodeGen/GlobalISel/CombinerHelper.h" 20 #include "llvm/CodeGen/GlobalISel/CombinerInfo.h" 21 #include "llvm/CodeGen/GlobalISel/GISelKnownBits.h" 22 #include "llvm/CodeGen/GlobalISel/MIPatternMatch.h" 23 #include "llvm/CodeGen/GlobalISel/Utils.h" 24 #include "llvm/CodeGen/MachineDominators.h" 25 #include "llvm/CodeGen/MachineFunctionPass.h" 26 #include "llvm/CodeGen/TargetPassConfig.h" 27 #include "llvm/Support/Debug.h" 28 29 #define DEBUG_TYPE "aarch64-postlegalizer-combiner" 30 31 using namespace llvm; 32 using namespace MIPatternMatch; 33 34 /// Represents a pseudo instruction which replaces a G_SHUFFLE_VECTOR. 35 /// 36 /// Used for matching target-supported shuffles before codegen. 37 struct ShuffleVectorPseudo { 38 unsigned Opc; ///< Opcode for the instruction. (E.g. G_ZIP1) 39 Register Dst; ///< Destination register. 40 SmallVector<SrcOp, 2> SrcOps; ///< Source registers. 41 ShuffleVectorPseudo(unsigned Opc, Register Dst, 42 std::initializer_list<SrcOp> SrcOps) 43 : Opc(Opc), Dst(Dst), SrcOps(SrcOps){}; 44 ShuffleVectorPseudo() {} 45 }; 46 47 /// Check if a vector shuffle corresponds to a REV instruction with the 48 /// specified blocksize. 49 static bool isREVMask(ArrayRef<int> M, unsigned EltSize, unsigned NumElts, 50 unsigned BlockSize) { 51 assert((BlockSize == 16 || BlockSize == 32 || BlockSize == 64) && 52 "Only possible block sizes for REV are: 16, 32, 64"); 53 assert(EltSize != 64 && "EltSize cannot be 64 for REV mask."); 54 55 unsigned BlockElts = M[0] + 1; 56 57 // If the first shuffle index is UNDEF, be optimistic. 58 if (M[0] < 0) 59 BlockElts = BlockSize / EltSize; 60 61 if (BlockSize <= EltSize || BlockSize != BlockElts * EltSize) 62 return false; 63 64 for (unsigned i = 0; i < NumElts; ++i) { 65 // Ignore undef indices. 66 if (M[i] < 0) 67 continue; 68 if (static_cast<unsigned>(M[i]) != 69 (i - i % BlockElts) + (BlockElts - 1 - i % BlockElts)) 70 return false; 71 } 72 73 return true; 74 } 75 76 /// Determines if \p M is a shuffle vector mask for a TRN of \p NumElts. 77 /// Whether or not G_TRN1 or G_TRN2 should be used is stored in \p WhichResult. 78 static bool isTRNMask(ArrayRef<int> M, unsigned NumElts, 79 unsigned &WhichResult) { 80 if (NumElts % 2 != 0) 81 return false; 82 WhichResult = (M[0] == 0 ? 0 : 1); 83 for (unsigned i = 0; i < NumElts; i += 2) { 84 if ((M[i] >= 0 && static_cast<unsigned>(M[i]) != i + WhichResult) || 85 (M[i + 1] >= 0 && 86 static_cast<unsigned>(M[i + 1]) != i + NumElts + WhichResult)) 87 return false; 88 } 89 return true; 90 } 91 92 /// Check if a G_EXT instruction can handle a shuffle mask \p M when the vector 93 /// sources of the shuffle are different. 94 static Optional<std::pair<bool, uint64_t>> getExtMask(ArrayRef<int> M, 95 unsigned NumElts) { 96 // Look for the first non-undef element. 97 auto FirstRealElt = find_if(M, [](int Elt) { return Elt >= 0; }); 98 if (FirstRealElt == M.end()) 99 return None; 100 101 // Use APInt to handle overflow when calculating expected element. 102 unsigned MaskBits = APInt(32, NumElts * 2).logBase2(); 103 APInt ExpectedElt = APInt(MaskBits, *FirstRealElt + 1); 104 105 // The following shuffle indices must be the successive elements after the 106 // first real element. 107 if (any_of( 108 make_range(std::next(FirstRealElt), M.end()), 109 [&ExpectedElt](int Elt) { return Elt != ExpectedElt++ && Elt >= 0; })) 110 return None; 111 112 // The index of an EXT is the first element if it is not UNDEF. 113 // Watch out for the beginning UNDEFs. The EXT index should be the expected 114 // value of the first element. E.g. 115 // <-1, -1, 3, ...> is treated as <1, 2, 3, ...>. 116 // <-1, -1, 0, 1, ...> is treated as <2*NumElts-2, 2*NumElts-1, 0, 1, ...>. 117 // ExpectedElt is the last mask index plus 1. 118 uint64_t Imm = ExpectedElt.getZExtValue(); 119 bool ReverseExt = false; 120 121 // There are two difference cases requiring to reverse input vectors. 122 // For example, for vector <4 x i32> we have the following cases, 123 // Case 1: shufflevector(<4 x i32>,<4 x i32>,<-1, -1, -1, 0>) 124 // Case 2: shufflevector(<4 x i32>,<4 x i32>,<-1, -1, 7, 0>) 125 // For both cases, we finally use mask <5, 6, 7, 0>, which requires 126 // to reverse two input vectors. 127 if (Imm < NumElts) 128 ReverseExt = true; 129 else 130 Imm -= NumElts; 131 return std::make_pair(ReverseExt, Imm); 132 } 133 134 /// Determines if \p M is a shuffle vector mask for a UZP of \p NumElts. 135 /// Whether or not G_UZP1 or G_UZP2 should be used is stored in \p WhichResult. 136 static bool isUZPMask(ArrayRef<int> M, unsigned NumElts, 137 unsigned &WhichResult) { 138 WhichResult = (M[0] == 0 ? 0 : 1); 139 for (unsigned i = 0; i != NumElts; ++i) { 140 // Skip undef indices. 141 if (M[i] < 0) 142 continue; 143 if (static_cast<unsigned>(M[i]) != 2 * i + WhichResult) 144 return false; 145 } 146 return true; 147 } 148 149 /// \return true if \p M is a zip mask for a shuffle vector of \p NumElts. 150 /// Whether or not G_ZIP1 or G_ZIP2 should be used is stored in \p WhichResult. 151 static bool isZipMask(ArrayRef<int> M, unsigned NumElts, 152 unsigned &WhichResult) { 153 if (NumElts % 2 != 0) 154 return false; 155 156 // 0 means use ZIP1, 1 means use ZIP2. 157 WhichResult = (M[0] == 0 ? 0 : 1); 158 unsigned Idx = WhichResult * NumElts / 2; 159 for (unsigned i = 0; i != NumElts; i += 2) { 160 if ((M[i] >= 0 && static_cast<unsigned>(M[i]) != Idx) || 161 (M[i + 1] >= 0 && static_cast<unsigned>(M[i + 1]) != Idx + NumElts)) 162 return false; 163 Idx += 1; 164 } 165 return true; 166 } 167 168 /// \return true if a G_SHUFFLE_VECTOR instruction \p MI can be replaced with a 169 /// G_REV instruction. Returns the appropriate G_REV opcode in \p Opc. 170 static bool matchREV(MachineInstr &MI, MachineRegisterInfo &MRI, 171 ShuffleVectorPseudo &MatchInfo) { 172 assert(MI.getOpcode() == TargetOpcode::G_SHUFFLE_VECTOR); 173 ArrayRef<int> ShuffleMask = MI.getOperand(3).getShuffleMask(); 174 Register Dst = MI.getOperand(0).getReg(); 175 Register Src = MI.getOperand(1).getReg(); 176 LLT Ty = MRI.getType(Dst); 177 unsigned EltSize = Ty.getScalarSizeInBits(); 178 179 // Element size for a rev cannot be 64. 180 if (EltSize == 64) 181 return false; 182 183 unsigned NumElts = Ty.getNumElements(); 184 185 // Try to produce G_REV64 186 if (isREVMask(ShuffleMask, EltSize, NumElts, 64)) { 187 MatchInfo = ShuffleVectorPseudo(AArch64::G_REV64, Dst, {Src}); 188 return true; 189 } 190 191 // TODO: Produce G_REV32 and G_REV16 once we have proper legalization support. 192 // This should be identical to above, but with a constant 32 and constant 193 // 16. 194 return false; 195 } 196 197 /// \return true if a G_SHUFFLE_VECTOR instruction \p MI can be replaced with 198 /// a G_TRN1 or G_TRN2 instruction. 199 static bool matchTRN(MachineInstr &MI, MachineRegisterInfo &MRI, 200 ShuffleVectorPseudo &MatchInfo) { 201 assert(MI.getOpcode() == TargetOpcode::G_SHUFFLE_VECTOR); 202 unsigned WhichResult; 203 ArrayRef<int> ShuffleMask = MI.getOperand(3).getShuffleMask(); 204 Register Dst = MI.getOperand(0).getReg(); 205 unsigned NumElts = MRI.getType(Dst).getNumElements(); 206 if (!isTRNMask(ShuffleMask, NumElts, WhichResult)) 207 return false; 208 unsigned Opc = (WhichResult == 0) ? AArch64::G_TRN1 : AArch64::G_TRN2; 209 Register V1 = MI.getOperand(1).getReg(); 210 Register V2 = MI.getOperand(2).getReg(); 211 MatchInfo = ShuffleVectorPseudo(Opc, Dst, {V1, V2}); 212 return true; 213 } 214 215 /// \return true if a G_SHUFFLE_VECTOR instruction \p MI can be replaced with 216 /// a G_UZP1 or G_UZP2 instruction. 217 /// 218 /// \param [in] MI - The shuffle vector instruction. 219 /// \param [out] MatchInfo - Either G_UZP1 or G_UZP2 on success. 220 static bool matchUZP(MachineInstr &MI, MachineRegisterInfo &MRI, 221 ShuffleVectorPseudo &MatchInfo) { 222 assert(MI.getOpcode() == TargetOpcode::G_SHUFFLE_VECTOR); 223 unsigned WhichResult; 224 ArrayRef<int> ShuffleMask = MI.getOperand(3).getShuffleMask(); 225 Register Dst = MI.getOperand(0).getReg(); 226 unsigned NumElts = MRI.getType(Dst).getNumElements(); 227 if (!isUZPMask(ShuffleMask, NumElts, WhichResult)) 228 return false; 229 unsigned Opc = (WhichResult == 0) ? AArch64::G_UZP1 : AArch64::G_UZP2; 230 Register V1 = MI.getOperand(1).getReg(); 231 Register V2 = MI.getOperand(2).getReg(); 232 MatchInfo = ShuffleVectorPseudo(Opc, Dst, {V1, V2}); 233 return true; 234 } 235 236 static bool matchZip(MachineInstr &MI, MachineRegisterInfo &MRI, 237 ShuffleVectorPseudo &MatchInfo) { 238 assert(MI.getOpcode() == TargetOpcode::G_SHUFFLE_VECTOR); 239 unsigned WhichResult; 240 ArrayRef<int> ShuffleMask = MI.getOperand(3).getShuffleMask(); 241 Register Dst = MI.getOperand(0).getReg(); 242 unsigned NumElts = MRI.getType(Dst).getNumElements(); 243 if (!isZipMask(ShuffleMask, NumElts, WhichResult)) 244 return false; 245 unsigned Opc = (WhichResult == 0) ? AArch64::G_ZIP1 : AArch64::G_ZIP2; 246 Register V1 = MI.getOperand(1).getReg(); 247 Register V2 = MI.getOperand(2).getReg(); 248 MatchInfo = ShuffleVectorPseudo(Opc, Dst, {V1, V2}); 249 return true; 250 } 251 252 /// Helper function for matchDup. 253 static bool matchDupFromInsertVectorElt(int Lane, MachineInstr &MI, 254 MachineRegisterInfo &MRI, 255 ShuffleVectorPseudo &MatchInfo) { 256 if (Lane != 0) 257 return false; 258 259 // Try to match a vector splat operation into a dup instruction. 260 // We're looking for this pattern: 261 // 262 // %scalar:gpr(s64) = COPY $x0 263 // %undef:fpr(<2 x s64>) = G_IMPLICIT_DEF 264 // %cst0:gpr(s32) = G_CONSTANT i32 0 265 // %zerovec:fpr(<2 x s32>) = G_BUILD_VECTOR %cst0(s32), %cst0(s32) 266 // %ins:fpr(<2 x s64>) = G_INSERT_VECTOR_ELT %undef, %scalar(s64), %cst0(s32) 267 // %splat:fpr(<2 x s64>) = G_SHUFFLE_VECTOR %ins(<2 x s64>), %undef, %zerovec(<2 x s32>) 268 // 269 // ...into: 270 // %splat = G_DUP %scalar 271 272 // Begin matching the insert. 273 auto *InsMI = getOpcodeDef(TargetOpcode::G_INSERT_VECTOR_ELT, 274 MI.getOperand(1).getReg(), MRI); 275 if (!InsMI) 276 return false; 277 // Match the undef vector operand. 278 if (!getOpcodeDef(TargetOpcode::G_IMPLICIT_DEF, InsMI->getOperand(1).getReg(), 279 MRI)) 280 return false; 281 282 // Match the index constant 0. 283 int64_t Index = 0; 284 if (!mi_match(InsMI->getOperand(3).getReg(), MRI, m_ICst(Index)) || Index) 285 return false; 286 287 MatchInfo = ShuffleVectorPseudo(AArch64::G_DUP, MI.getOperand(0).getReg(), 288 {InsMI->getOperand(2).getReg()}); 289 return true; 290 } 291 292 /// Helper function for matchDup. 293 static bool matchDupFromBuildVector(int Lane, MachineInstr &MI, 294 MachineRegisterInfo &MRI, 295 ShuffleVectorPseudo &MatchInfo) { 296 assert(Lane >= 0 && "Expected positive lane?"); 297 // Test if the LHS is a BUILD_VECTOR. If it is, then we can just reference the 298 // lane's definition directly. 299 auto *BuildVecMI = getOpcodeDef(TargetOpcode::G_BUILD_VECTOR, 300 MI.getOperand(1).getReg(), MRI); 301 if (!BuildVecMI) 302 return false; 303 Register Reg = BuildVecMI->getOperand(Lane + 1).getReg(); 304 MatchInfo = 305 ShuffleVectorPseudo(AArch64::G_DUP, MI.getOperand(0).getReg(), {Reg}); 306 return true; 307 } 308 309 static bool matchDup(MachineInstr &MI, MachineRegisterInfo &MRI, 310 ShuffleVectorPseudo &MatchInfo) { 311 assert(MI.getOpcode() == TargetOpcode::G_SHUFFLE_VECTOR); 312 auto MaybeLane = getSplatIndex(MI); 313 if (!MaybeLane) 314 return false; 315 int Lane = *MaybeLane; 316 // If this is undef splat, generate it via "just" vdup, if possible. 317 if (Lane < 0) 318 Lane = 0; 319 if (matchDupFromInsertVectorElt(Lane, MI, MRI, MatchInfo)) 320 return true; 321 if (matchDupFromBuildVector(Lane, MI, MRI, MatchInfo)) 322 return true; 323 return false; 324 } 325 326 static bool matchEXT(MachineInstr &MI, MachineRegisterInfo &MRI, 327 ShuffleVectorPseudo &MatchInfo) { 328 assert(MI.getOpcode() == TargetOpcode::G_SHUFFLE_VECTOR); 329 Register Dst = MI.getOperand(0).getReg(); 330 auto ExtInfo = getExtMask(MI.getOperand(3).getShuffleMask(), 331 MRI.getType(Dst).getNumElements()); 332 if (!ExtInfo) 333 return false; 334 bool ReverseExt; 335 uint64_t Imm; 336 std::tie(ReverseExt, Imm) = *ExtInfo; 337 Register V1 = MI.getOperand(1).getReg(); 338 Register V2 = MI.getOperand(2).getReg(); 339 if (ReverseExt) 340 std::swap(V1, V2); 341 uint64_t ExtFactor = MRI.getType(V1).getScalarSizeInBits() / 8; 342 Imm *= ExtFactor; 343 MatchInfo = ShuffleVectorPseudo(AArch64::G_EXT, Dst, {V1, V2, Imm}); 344 return true; 345 } 346 347 /// Replace a G_SHUFFLE_VECTOR instruction with a pseudo. 348 /// \p Opc is the opcode to use. \p MI is the G_SHUFFLE_VECTOR. 349 static bool applyShuffleVectorPseudo(MachineInstr &MI, 350 ShuffleVectorPseudo &MatchInfo) { 351 MachineIRBuilder MIRBuilder(MI); 352 MIRBuilder.buildInstr(MatchInfo.Opc, {MatchInfo.Dst}, MatchInfo.SrcOps); 353 MI.eraseFromParent(); 354 return true; 355 } 356 357 /// Replace a G_SHUFFLE_VECTOR instruction with G_EXT. 358 /// Special-cased because the constant operand must be emitted as a G_CONSTANT 359 /// for the imported tablegen patterns to work. 360 static bool applyEXT(MachineInstr &MI, ShuffleVectorPseudo &MatchInfo) { 361 MachineIRBuilder MIRBuilder(MI); 362 // Tablegen patterns expect an i32 G_CONSTANT as the final op. 363 auto Cst = 364 MIRBuilder.buildConstant(LLT::scalar(32), MatchInfo.SrcOps[2].getImm()); 365 MIRBuilder.buildInstr(MatchInfo.Opc, {MatchInfo.Dst}, 366 {MatchInfo.SrcOps[0], MatchInfo.SrcOps[1], Cst}); 367 MI.eraseFromParent(); 368 return true; 369 } 370 371 #define AARCH64POSTLEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_DEPS 372 #include "AArch64GenPostLegalizeGICombiner.inc" 373 #undef AARCH64POSTLEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_DEPS 374 375 namespace { 376 #define AARCH64POSTLEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_H 377 #include "AArch64GenPostLegalizeGICombiner.inc" 378 #undef AARCH64POSTLEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_H 379 380 class AArch64PostLegalizerCombinerInfo : public CombinerInfo { 381 GISelKnownBits *KB; 382 MachineDominatorTree *MDT; 383 384 public: 385 AArch64GenPostLegalizerCombinerHelperRuleConfig GeneratedRuleCfg; 386 387 AArch64PostLegalizerCombinerInfo(bool EnableOpt, bool OptSize, bool MinSize, 388 GISelKnownBits *KB, 389 MachineDominatorTree *MDT) 390 : CombinerInfo(/*AllowIllegalOps*/ true, /*ShouldLegalizeIllegal*/ false, 391 /*LegalizerInfo*/ nullptr, EnableOpt, OptSize, MinSize), 392 KB(KB), MDT(MDT) { 393 if (!GeneratedRuleCfg.parseCommandLineOption()) 394 report_fatal_error("Invalid rule identifier"); 395 } 396 397 virtual bool combine(GISelChangeObserver &Observer, MachineInstr &MI, 398 MachineIRBuilder &B) const override; 399 }; 400 401 bool AArch64PostLegalizerCombinerInfo::combine(GISelChangeObserver &Observer, 402 MachineInstr &MI, 403 MachineIRBuilder &B) const { 404 const auto *LI = 405 MI.getParent()->getParent()->getSubtarget().getLegalizerInfo(); 406 CombinerHelper Helper(Observer, B, KB, MDT, LI); 407 AArch64GenPostLegalizerCombinerHelper Generated(GeneratedRuleCfg); 408 return Generated.tryCombineAll(Observer, MI, B, Helper); 409 } 410 411 #define AARCH64POSTLEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_CPP 412 #include "AArch64GenPostLegalizeGICombiner.inc" 413 #undef AARCH64POSTLEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_CPP 414 415 class AArch64PostLegalizerCombiner : public MachineFunctionPass { 416 public: 417 static char ID; 418 419 AArch64PostLegalizerCombiner(bool IsOptNone = false); 420 421 StringRef getPassName() const override { 422 return "AArch64PostLegalizerCombiner"; 423 } 424 425 bool runOnMachineFunction(MachineFunction &MF) override; 426 void getAnalysisUsage(AnalysisUsage &AU) const override; 427 428 private: 429 bool IsOptNone; 430 }; 431 } // end anonymous namespace 432 433 void AArch64PostLegalizerCombiner::getAnalysisUsage(AnalysisUsage &AU) const { 434 AU.addRequired<TargetPassConfig>(); 435 AU.setPreservesCFG(); 436 getSelectionDAGFallbackAnalysisUsage(AU); 437 AU.addRequired<GISelKnownBitsAnalysis>(); 438 AU.addPreserved<GISelKnownBitsAnalysis>(); 439 if (!IsOptNone) { 440 AU.addRequired<MachineDominatorTree>(); 441 AU.addPreserved<MachineDominatorTree>(); 442 } 443 MachineFunctionPass::getAnalysisUsage(AU); 444 } 445 446 AArch64PostLegalizerCombiner::AArch64PostLegalizerCombiner(bool IsOptNone) 447 : MachineFunctionPass(ID), IsOptNone(IsOptNone) { 448 initializeAArch64PostLegalizerCombinerPass(*PassRegistry::getPassRegistry()); 449 } 450 451 bool AArch64PostLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) { 452 if (MF.getProperties().hasProperty( 453 MachineFunctionProperties::Property::FailedISel)) 454 return false; 455 assert(MF.getProperties().hasProperty( 456 MachineFunctionProperties::Property::Legalized) && 457 "Expected a legalized function?"); 458 auto *TPC = &getAnalysis<TargetPassConfig>(); 459 const Function &F = MF.getFunction(); 460 bool EnableOpt = 461 MF.getTarget().getOptLevel() != CodeGenOpt::None && !skipFunction(F); 462 GISelKnownBits *KB = &getAnalysis<GISelKnownBitsAnalysis>().get(MF); 463 MachineDominatorTree *MDT = 464 IsOptNone ? nullptr : &getAnalysis<MachineDominatorTree>(); 465 AArch64PostLegalizerCombinerInfo PCInfo(EnableOpt, F.hasOptSize(), 466 F.hasMinSize(), KB, MDT); 467 Combiner C(PCInfo, TPC); 468 return C.combineMachineInstrs(MF, /*CSEInfo*/ nullptr); 469 } 470 471 char AArch64PostLegalizerCombiner::ID = 0; 472 INITIALIZE_PASS_BEGIN(AArch64PostLegalizerCombiner, DEBUG_TYPE, 473 "Combine AArch64 MachineInstrs after legalization", false, 474 false) 475 INITIALIZE_PASS_DEPENDENCY(TargetPassConfig) 476 INITIALIZE_PASS_DEPENDENCY(GISelKnownBitsAnalysis) 477 INITIALIZE_PASS_END(AArch64PostLegalizerCombiner, DEBUG_TYPE, 478 "Combine AArch64 MachineInstrs after legalization", false, 479 false) 480 481 namespace llvm { 482 FunctionPass *createAArch64PostLegalizeCombiner(bool IsOptNone) { 483 return new AArch64PostLegalizerCombiner(IsOptNone); 484 } 485 } // end namespace llvm 486