1 //=- AArch64PromoteConstant.cpp --- Promote constant to global for AArch64 -==// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file implements the AArch64PromoteConstant pass which promotes constants 11 // to global variables when this is likely to be more efficient. Currently only 12 // types related to constant vector (i.e., constant vector, array of constant 13 // vectors, constant structure with a constant vector field, etc.) are promoted 14 // to global variables. Constant vectors are likely to be lowered in target 15 // constant pool during instruction selection already; therefore, the access 16 // will remain the same (memory load), but the structure types are not split 17 // into different constant pool accesses for each field. A bonus side effect is 18 // that created globals may be merged by the global merge pass. 19 // 20 // FIXME: This pass may be useful for other targets too. 21 //===----------------------------------------------------------------------===// 22 23 #include "AArch64.h" 24 #include "llvm/ADT/DenseMap.h" 25 #include "llvm/ADT/SmallPtrSet.h" 26 #include "llvm/ADT/SmallVector.h" 27 #include "llvm/ADT/Statistic.h" 28 #include "llvm/IR/Constants.h" 29 #include "llvm/IR/Dominators.h" 30 #include "llvm/IR/Function.h" 31 #include "llvm/IR/GlobalVariable.h" 32 #include "llvm/IR/IRBuilder.h" 33 #include "llvm/IR/InlineAsm.h" 34 #include "llvm/IR/InstIterator.h" 35 #include "llvm/IR/Instructions.h" 36 #include "llvm/IR/IntrinsicInst.h" 37 #include "llvm/IR/Module.h" 38 #include "llvm/Pass.h" 39 #include "llvm/Support/CommandLine.h" 40 #include "llvm/Support/Debug.h" 41 #include "llvm/Support/raw_ostream.h" 42 43 using namespace llvm; 44 45 #define DEBUG_TYPE "aarch64-promote-const" 46 47 // Stress testing mode - disable heuristics. 48 static cl::opt<bool> Stress("aarch64-stress-promote-const", cl::Hidden, 49 cl::desc("Promote all vector constants")); 50 51 STATISTIC(NumPromoted, "Number of promoted constants"); 52 STATISTIC(NumPromotedUses, "Number of promoted constants uses"); 53 54 //===----------------------------------------------------------------------===// 55 // AArch64PromoteConstant 56 //===----------------------------------------------------------------------===// 57 58 namespace { 59 /// Promotes interesting constant into global variables. 60 /// The motivating example is: 61 /// static const uint16_t TableA[32] = { 62 /// 41944, 40330, 38837, 37450, 36158, 34953, 33826, 32768, 63 /// 31776, 30841, 29960, 29128, 28340, 27595, 26887, 26215, 64 /// 25576, 24967, 24386, 23832, 23302, 22796, 22311, 21846, 65 /// 21400, 20972, 20561, 20165, 19785, 19419, 19066, 18725, 66 /// }; 67 /// 68 /// uint8x16x4_t LoadStatic(void) { 69 /// uint8x16x4_t ret; 70 /// ret.val[0] = vld1q_u16(TableA + 0); 71 /// ret.val[1] = vld1q_u16(TableA + 8); 72 /// ret.val[2] = vld1q_u16(TableA + 16); 73 /// ret.val[3] = vld1q_u16(TableA + 24); 74 /// return ret; 75 /// } 76 /// 77 /// The constants in this example are folded into the uses. Thus, 4 different 78 /// constants are created. 79 /// 80 /// As their type is vector the cheapest way to create them is to load them 81 /// for the memory. 82 /// 83 /// Therefore the final assembly final has 4 different loads. With this pass 84 /// enabled, only one load is issued for the constants. 85 class AArch64PromoteConstant : public ModulePass { 86 87 public: 88 struct PromotedConstant { 89 bool ShouldConvert = false; 90 GlobalVariable *GV = nullptr; 91 }; 92 typedef SmallDenseMap<Constant *, PromotedConstant, 16> PromotionCacheTy; 93 94 struct UpdateRecord { 95 Constant *C; 96 Instruction *User; 97 unsigned Op; 98 99 UpdateRecord(Constant *C, Instruction *User, unsigned Op) 100 : C(C), User(User), Op(Op) {} 101 }; 102 103 static char ID; 104 AArch64PromoteConstant() : ModulePass(ID) {} 105 106 const char *getPassName() const override { return "AArch64 Promote Constant"; } 107 108 /// Iterate over the functions and promote the interesting constants into 109 /// global variables with module scope. 110 bool runOnModule(Module &M) override { 111 DEBUG(dbgs() << getPassName() << '\n'); 112 bool Changed = false; 113 PromotionCacheTy PromotionCache; 114 for (auto &MF : M) { 115 Changed |= runOnFunction(MF, PromotionCache); 116 } 117 return Changed; 118 } 119 120 private: 121 /// Look for interesting constants used within the given function. 122 /// Promote them into global variables, load these global variables within 123 /// the related function, so that the number of inserted load is minimal. 124 bool runOnFunction(Function &F, PromotionCacheTy &PromotionCache); 125 126 // This transformation requires dominator info 127 void getAnalysisUsage(AnalysisUsage &AU) const override { 128 AU.setPreservesCFG(); 129 AU.addRequired<DominatorTreeWrapperPass>(); 130 AU.addPreserved<DominatorTreeWrapperPass>(); 131 } 132 133 /// Type to store a list of Uses. 134 typedef SmallVector<std::pair<Instruction *, unsigned>, 4> Uses; 135 /// Map an insertion point to all the uses it dominates. 136 typedef DenseMap<Instruction *, Uses> InsertionPoints; 137 138 /// Find the closest point that dominates the given Use. 139 Instruction *findInsertionPoint(Instruction &User, unsigned OpNo); 140 141 /// Check if the given insertion point is dominated by an existing 142 /// insertion point. 143 /// If true, the given use is added to the list of dominated uses for 144 /// the related existing point. 145 /// \param NewPt the insertion point to be checked 146 /// \param User the user of the constant 147 /// \param OpNo the operand number of the use 148 /// \param InsertPts existing insertion points 149 /// \pre NewPt and all instruction in InsertPts belong to the same function 150 /// \return true if one of the insertion point in InsertPts dominates NewPt, 151 /// false otherwise 152 bool isDominated(Instruction *NewPt, Instruction *User, unsigned OpNo, 153 InsertionPoints &InsertPts); 154 155 /// Check if the given insertion point can be merged with an existing 156 /// insertion point in a common dominator. 157 /// If true, the given use is added to the list of the created insertion 158 /// point. 159 /// \param NewPt the insertion point to be checked 160 /// \param User the user of the constant 161 /// \param OpNo the operand number of the use 162 /// \param InsertPts existing insertion points 163 /// \pre NewPt and all instruction in InsertPts belong to the same function 164 /// \pre isDominated returns false for the exact same parameters. 165 /// \return true if it exists an insertion point in InsertPts that could 166 /// have been merged with NewPt in a common dominator, 167 /// false otherwise 168 bool tryAndMerge(Instruction *NewPt, Instruction *User, unsigned OpNo, 169 InsertionPoints &InsertPts); 170 171 /// Compute the minimal insertion points to dominates all the interesting 172 /// uses of value. 173 /// Insertion points are group per function and each insertion point 174 /// contains a list of all the uses it dominates within the related function 175 /// \param User the user of the constant 176 /// \param OpNo the operand number of the constant 177 /// \param[out] InsertPts output storage of the analysis 178 void computeInsertionPoint(Instruction *User, unsigned OpNo, 179 InsertionPoints &InsertPts); 180 181 /// Insert a definition of a new global variable at each point contained in 182 /// InsPtsPerFunc and update the related uses (also contained in 183 /// InsPtsPerFunc). 184 void insertDefinitions(Function &F, GlobalVariable &GV, 185 InsertionPoints &InsertPts); 186 187 /// Sort the updates in a deterministic way. 188 void sortUpdates(SmallVectorImpl<UpdateRecord> &Updates); 189 190 /// Do the constant promotion indicated by the Updates records, keeping track 191 /// of globals in PromotionCache. 192 void promoteConstants(Function &F, SmallVectorImpl<UpdateRecord> &Updates, 193 PromotionCacheTy &PromotionCache); 194 195 /// Transfer the list of dominated uses of IPI to NewPt in InsertPts. 196 /// Append Use to this list and delete the entry of IPI in InsertPts. 197 static void appendAndTransferDominatedUses(Instruction *NewPt, 198 Instruction *User, unsigned OpNo, 199 InsertionPoints::iterator &IPI, 200 InsertionPoints &InsertPts) { 201 // Record the dominated use. 202 IPI->second.emplace_back(User, OpNo); 203 // Transfer the dominated uses of IPI to NewPt 204 // Inserting into the DenseMap may invalidate existing iterator. 205 // Keep a copy of the key to find the iterator to erase. Keep a copy of the 206 // value so that we don't have to dereference IPI->second. 207 Instruction *OldInstr = IPI->first; 208 Uses OldUses = std::move(IPI->second); 209 InsertPts[NewPt] = std::move(OldUses); 210 // Erase IPI. 211 InsertPts.erase(OldInstr); 212 } 213 }; 214 } // end anonymous namespace 215 216 char AArch64PromoteConstant::ID = 0; 217 218 namespace llvm { 219 void initializeAArch64PromoteConstantPass(PassRegistry &); 220 } 221 222 INITIALIZE_PASS_BEGIN(AArch64PromoteConstant, "aarch64-promote-const", 223 "AArch64 Promote Constant Pass", false, false) 224 INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) 225 INITIALIZE_PASS_END(AArch64PromoteConstant, "aarch64-promote-const", 226 "AArch64 Promote Constant Pass", false, false) 227 228 ModulePass *llvm::createAArch64PromoteConstantPass() { 229 return new AArch64PromoteConstant(); 230 } 231 232 /// Check if the given type uses a vector type. 233 static bool isConstantUsingVectorTy(const Type *CstTy) { 234 if (CstTy->isVectorTy()) 235 return true; 236 if (CstTy->isStructTy()) { 237 for (unsigned EltIdx = 0, EndEltIdx = CstTy->getStructNumElements(); 238 EltIdx < EndEltIdx; ++EltIdx) 239 if (isConstantUsingVectorTy(CstTy->getStructElementType(EltIdx))) 240 return true; 241 } else if (CstTy->isArrayTy()) 242 return isConstantUsingVectorTy(CstTy->getArrayElementType()); 243 return false; 244 } 245 246 /// Check if the given use (Instruction + OpIdx) of Cst should be converted into 247 /// a load of a global variable initialized with Cst. 248 /// A use should be converted if it is legal to do so. 249 /// For instance, it is not legal to turn the mask operand of a shuffle vector 250 /// into a load of a global variable. 251 static bool shouldConvertUse(const Constant *Cst, const Instruction *Instr, 252 unsigned OpIdx) { 253 // shufflevector instruction expects a const for the mask argument, i.e., the 254 // third argument. Do not promote this use in that case. 255 if (isa<const ShuffleVectorInst>(Instr) && OpIdx == 2) 256 return false; 257 258 // extractvalue instruction expects a const idx. 259 if (isa<const ExtractValueInst>(Instr) && OpIdx > 0) 260 return false; 261 262 // extractvalue instruction expects a const idx. 263 if (isa<const InsertValueInst>(Instr) && OpIdx > 1) 264 return false; 265 266 if (isa<const AllocaInst>(Instr) && OpIdx > 0) 267 return false; 268 269 // Alignment argument must be constant. 270 if (isa<const LoadInst>(Instr) && OpIdx > 0) 271 return false; 272 273 // Alignment argument must be constant. 274 if (isa<const StoreInst>(Instr) && OpIdx > 1) 275 return false; 276 277 // Index must be constant. 278 if (isa<const GetElementPtrInst>(Instr) && OpIdx > 0) 279 return false; 280 281 // Personality function and filters must be constant. 282 // Give up on that instruction. 283 if (isa<const LandingPadInst>(Instr)) 284 return false; 285 286 // Switch instruction expects constants to compare to. 287 if (isa<const SwitchInst>(Instr)) 288 return false; 289 290 // Expected address must be a constant. 291 if (isa<const IndirectBrInst>(Instr)) 292 return false; 293 294 // Do not mess with intrinsics. 295 if (isa<const IntrinsicInst>(Instr)) 296 return false; 297 298 // Do not mess with inline asm. 299 const CallInst *CI = dyn_cast<const CallInst>(Instr); 300 return !(CI && isa<const InlineAsm>(CI->getCalledValue())); 301 } 302 303 /// Check if the given Cst should be converted into 304 /// a load of a global variable initialized with Cst. 305 /// A constant should be converted if it is likely that the materialization of 306 /// the constant will be tricky. Thus, we give up on zero or undef values. 307 /// 308 /// \todo Currently, accept only vector related types. 309 /// Also we give up on all simple vector type to keep the existing 310 /// behavior. Otherwise, we should push here all the check of the lowering of 311 /// BUILD_VECTOR. By giving up, we lose the potential benefit of merging 312 /// constant via global merge and the fact that the same constant is stored 313 /// only once with this method (versus, as many function that uses the constant 314 /// for the regular approach, even for float). 315 /// Again, the simplest solution would be to promote every 316 /// constant and rematerialize them when they are actually cheap to create. 317 static bool shouldConvertImpl(const Constant *Cst) { 318 if (isa<const UndefValue>(Cst)) 319 return false; 320 321 // FIXME: In some cases, it may be interesting to promote in memory 322 // a zero initialized constant. 323 // E.g., when the type of Cst require more instructions than the 324 // adrp/add/load sequence or when this sequence can be shared by several 325 // instances of Cst. 326 // Ideally, we could promote this into a global and rematerialize the constant 327 // when it was a bad idea. 328 if (Cst->isZeroValue()) 329 return false; 330 331 if (Stress) 332 return true; 333 334 // FIXME: see function \todo 335 if (Cst->getType()->isVectorTy()) 336 return false; 337 return isConstantUsingVectorTy(Cst->getType()); 338 } 339 340 static bool 341 shouldConvert(Constant &C, 342 AArch64PromoteConstant::PromotionCacheTy &PromotionCache) { 343 auto Converted = PromotionCache.insert( 344 std::make_pair(&C, AArch64PromoteConstant::PromotedConstant())); 345 if (Converted.second) 346 Converted.first->second.ShouldConvert = shouldConvertImpl(&C); 347 return Converted.first->second.ShouldConvert; 348 } 349 350 Instruction *AArch64PromoteConstant::findInsertionPoint(Instruction &User, 351 unsigned OpNo) { 352 // If this user is a phi, the insertion point is in the related 353 // incoming basic block. 354 if (PHINode *PhiInst = dyn_cast<PHINode>(&User)) 355 return PhiInst->getIncomingBlock(OpNo)->getTerminator(); 356 357 return &User; 358 } 359 360 bool AArch64PromoteConstant::isDominated(Instruction *NewPt, Instruction *User, 361 unsigned OpNo, 362 InsertionPoints &InsertPts) { 363 364 DominatorTree &DT = getAnalysis<DominatorTreeWrapperPass>( 365 *NewPt->getParent()->getParent()).getDomTree(); 366 367 // Traverse all the existing insertion points and check if one is dominating 368 // NewPt. If it is, remember that. 369 for (auto &IPI : InsertPts) { 370 if (NewPt == IPI.first || DT.dominates(IPI.first, NewPt) || 371 // When IPI.first is a terminator instruction, DT may think that 372 // the result is defined on the edge. 373 // Here we are testing the insertion point, not the definition. 374 (IPI.first->getParent() != NewPt->getParent() && 375 DT.dominates(IPI.first->getParent(), NewPt->getParent()))) { 376 // No need to insert this point. Just record the dominated use. 377 DEBUG(dbgs() << "Insertion point dominated by:\n"); 378 DEBUG(IPI.first->print(dbgs())); 379 DEBUG(dbgs() << '\n'); 380 IPI.second.emplace_back(User, OpNo); 381 return true; 382 } 383 } 384 return false; 385 } 386 387 bool AArch64PromoteConstant::tryAndMerge(Instruction *NewPt, Instruction *User, 388 unsigned OpNo, 389 InsertionPoints &InsertPts) { 390 DominatorTree &DT = getAnalysis<DominatorTreeWrapperPass>( 391 *NewPt->getParent()->getParent()).getDomTree(); 392 BasicBlock *NewBB = NewPt->getParent(); 393 394 // Traverse all the existing insertion point and check if one is dominated by 395 // NewPt and thus useless or can be combined with NewPt into a common 396 // dominator. 397 for (InsertionPoints::iterator IPI = InsertPts.begin(), 398 EndIPI = InsertPts.end(); 399 IPI != EndIPI; ++IPI) { 400 BasicBlock *CurBB = IPI->first->getParent(); 401 if (NewBB == CurBB) { 402 // Instructions are in the same block. 403 // By construction, NewPt is dominating the other. 404 // Indeed, isDominated returned false with the exact same arguments. 405 DEBUG(dbgs() << "Merge insertion point with:\n"); 406 DEBUG(IPI->first->print(dbgs())); 407 DEBUG(dbgs() << "\nat considered insertion point.\n"); 408 appendAndTransferDominatedUses(NewPt, User, OpNo, IPI, InsertPts); 409 return true; 410 } 411 412 // Look for a common dominator 413 BasicBlock *CommonDominator = DT.findNearestCommonDominator(NewBB, CurBB); 414 // If none exists, we cannot merge these two points. 415 if (!CommonDominator) 416 continue; 417 418 if (CommonDominator != NewBB) { 419 // By construction, the CommonDominator cannot be CurBB. 420 assert(CommonDominator != CurBB && 421 "Instruction has not been rejected during isDominated check!"); 422 // Take the last instruction of the CommonDominator as insertion point 423 NewPt = CommonDominator->getTerminator(); 424 } 425 // else, CommonDominator is the block of NewBB, hence NewBB is the last 426 // possible insertion point in that block. 427 DEBUG(dbgs() << "Merge insertion point with:\n"); 428 DEBUG(IPI->first->print(dbgs())); 429 DEBUG(dbgs() << '\n'); 430 DEBUG(NewPt->print(dbgs())); 431 DEBUG(dbgs() << '\n'); 432 appendAndTransferDominatedUses(NewPt, User, OpNo, IPI, InsertPts); 433 return true; 434 } 435 return false; 436 } 437 438 void AArch64PromoteConstant::computeInsertionPoint( 439 Instruction *User, unsigned OpNo, InsertionPoints &InsertPts) { 440 DEBUG(dbgs() << "Considered use, opidx " << OpNo << ":\n"); 441 DEBUG(User->print(dbgs())); 442 DEBUG(dbgs() << '\n'); 443 444 Instruction *InsertionPoint = findInsertionPoint(*User, OpNo); 445 446 DEBUG(dbgs() << "Considered insertion point:\n"); 447 DEBUG(InsertionPoint->print(dbgs())); 448 DEBUG(dbgs() << '\n'); 449 450 if (isDominated(InsertionPoint, User, OpNo, InsertPts)) 451 return; 452 // This insertion point is useful, check if we can merge some insertion 453 // point in a common dominator or if NewPt dominates an existing one. 454 if (tryAndMerge(InsertionPoint, User, OpNo, InsertPts)) 455 return; 456 457 DEBUG(dbgs() << "Keep considered insertion point\n"); 458 459 // It is definitely useful by its own 460 InsertPts[InsertionPoint].emplace_back(User, OpNo); 461 } 462 463 static void ensurePromotedGV(Function &F, Constant &C, 464 AArch64PromoteConstant::PromotedConstant &PC) { 465 assert(PC.ShouldConvert && 466 "Expected that we should convert this to a global"); 467 if (PC.GV) 468 return; 469 PC.GV = new GlobalVariable( 470 *F.getParent(), C.getType(), true, GlobalValue::InternalLinkage, nullptr, 471 "_PromotedConst", nullptr, GlobalVariable::NotThreadLocal); 472 PC.GV->setInitializer(&C); 473 DEBUG(dbgs() << "Global replacement: "); 474 DEBUG(PC.GV->print(dbgs())); 475 DEBUG(dbgs() << '\n'); 476 ++NumPromoted; 477 } 478 479 void AArch64PromoteConstant::insertDefinitions(Function &F, 480 GlobalVariable &PromotedGV, 481 InsertionPoints &InsertPts) { 482 #ifndef NDEBUG 483 // Do more checking for debug purposes. 484 DominatorTree &DT = getAnalysis<DominatorTreeWrapperPass>(F).getDomTree(); 485 #endif 486 assert(!InsertPts.empty() && "Empty uses does not need a definition"); 487 488 for (const auto &IPI : InsertPts) { 489 // Create the load of the global variable. 490 IRBuilder<> Builder(IPI.first); 491 LoadInst *LoadedCst = Builder.CreateLoad(&PromotedGV); 492 DEBUG(dbgs() << "**********\n"); 493 DEBUG(dbgs() << "New def: "); 494 DEBUG(LoadedCst->print(dbgs())); 495 DEBUG(dbgs() << '\n'); 496 497 // Update the dominated uses. 498 for (auto Use : IPI.second) { 499 #ifndef NDEBUG 500 assert(DT.dominates(LoadedCst, 501 findInsertionPoint(*Use.first, Use.second)) && 502 "Inserted definition does not dominate all its uses!"); 503 #endif 504 DEBUG({ 505 dbgs() << "Use to update " << Use.second << ":"; 506 Use.first->print(dbgs()); 507 dbgs() << '\n'; 508 }); 509 Use.first->setOperand(Use.second, LoadedCst); 510 ++NumPromotedUses; 511 } 512 } 513 } 514 515 void AArch64PromoteConstant::sortUpdates( 516 SmallVectorImpl<UpdateRecord> &Updates) { 517 // The order the constants were inserted is deterministic (unlike their 518 // address). 519 SmallDenseMap<const Constant *, unsigned, 128> InsertionOrder; 520 for (const auto &Record : Updates) 521 InsertionOrder.insert(std::make_pair(Record.C, InsertionOrder.size())); 522 523 // This is already sorted by Instruction ordering in the function and operand 524 // number, which is a good first step. Now reorder by constant. 525 std::stable_sort( 526 Updates.begin(), Updates.end(), 527 [&InsertionOrder](const UpdateRecord &L, const UpdateRecord &R) { 528 return InsertionOrder.lookup(L.C) < InsertionOrder.lookup(R.C); 529 }); 530 } 531 532 void AArch64PromoteConstant::promoteConstants( 533 Function &F, SmallVectorImpl<UpdateRecord> &Updates, 534 PromotionCacheTy &PromotionCache) { 535 // Promote the constants. 536 for (auto U = Updates.begin(), E = Updates.end(); U != E;) { 537 DEBUG(dbgs() << "** Compute insertion points **\n"); 538 auto First = U; 539 Constant *C = First->C; 540 InsertionPoints InsertPts; 541 do { 542 computeInsertionPoint(U->User, U->Op, InsertPts); 543 } while (++U != E && U->C == C); 544 545 auto &Promotion = PromotionCache[C]; 546 ensurePromotedGV(F, *C, Promotion); 547 insertDefinitions(F, *Promotion.GV, InsertPts); 548 } 549 } 550 551 bool AArch64PromoteConstant::runOnFunction(Function &F, 552 PromotionCacheTy &PromotionCache) { 553 // Look for instructions using constant vector. Promote that constant to a 554 // global variable. Create as few loads of this variable as possible and 555 // update the uses accordingly. 556 SmallVector<UpdateRecord, 64> Updates; 557 for (Instruction &I : instructions(&F)) { 558 // Traverse the operand, looking for constant vectors. Replace them by a 559 // load of a global variable of constant vector type. 560 for (Use &U : I.operands()) { 561 Constant *Cst = dyn_cast<Constant>(U); 562 // There is no point in promoting global values as they are already 563 // global. Do not promote constant expressions either, as they may 564 // require some code expansion. 565 if (!Cst || isa<GlobalValue>(Cst) || isa<ConstantExpr>(Cst)) 566 continue; 567 568 // Check if this constant is worth promoting. 569 if (!shouldConvert(*Cst, PromotionCache)) 570 continue; 571 572 // Check if this use should be promoted. 573 unsigned OpNo = &U - I.op_begin(); 574 if (!shouldConvertUse(Cst, &I, OpNo)) 575 continue; 576 577 Updates.emplace_back(Cst, &I, OpNo); 578 } 579 } 580 581 if (Updates.empty()) 582 return false; 583 584 promoteConstants(F, Updates, PromotionCache); 585 return true; 586 } 587