1*fe6060f1SDimitry Andric //=== ReplaceWithVeclib.cpp - Replace vector instrinsics with veclib calls ===// 2*fe6060f1SDimitry Andric // 3*fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*fe6060f1SDimitry Andric // 7*fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 8*fe6060f1SDimitry Andric // 9*fe6060f1SDimitry Andric // Replaces calls to LLVM vector intrinsics (i.e., calls to LLVM intrinsics 10*fe6060f1SDimitry Andric // with vector operands) with matching calls to functions from a vector 11*fe6060f1SDimitry Andric // library (e.g., libmvec, SVML) according to TargetLibraryInfo. 12*fe6060f1SDimitry Andric // 13*fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 14*fe6060f1SDimitry Andric 15*fe6060f1SDimitry Andric #include "llvm/CodeGen/ReplaceWithVeclib.h" 16*fe6060f1SDimitry Andric #include "llvm/ADT/STLExtras.h" 17*fe6060f1SDimitry Andric #include "llvm/ADT/Statistic.h" 18*fe6060f1SDimitry Andric #include "llvm/Analysis/DemandedBits.h" 19*fe6060f1SDimitry Andric #include "llvm/Analysis/GlobalsModRef.h" 20*fe6060f1SDimitry Andric #include "llvm/Analysis/OptimizationRemarkEmitter.h" 21*fe6060f1SDimitry Andric #include "llvm/Analysis/TargetLibraryInfo.h" 22*fe6060f1SDimitry Andric #include "llvm/Analysis/VectorUtils.h" 23*fe6060f1SDimitry Andric #include "llvm/CodeGen/Passes.h" 24*fe6060f1SDimitry Andric #include "llvm/IR/IRBuilder.h" 25*fe6060f1SDimitry Andric #include "llvm/IR/InstIterator.h" 26*fe6060f1SDimitry Andric #include "llvm/IR/IntrinsicInst.h" 27*fe6060f1SDimitry Andric #include "llvm/Transforms/Utils/ModuleUtils.h" 28*fe6060f1SDimitry Andric 29*fe6060f1SDimitry Andric using namespace llvm; 30*fe6060f1SDimitry Andric 31*fe6060f1SDimitry Andric #define DEBUG_TYPE "replace-with-veclib" 32*fe6060f1SDimitry Andric 33*fe6060f1SDimitry Andric STATISTIC(NumCallsReplaced, 34*fe6060f1SDimitry Andric "Number of calls to intrinsics that have been replaced."); 35*fe6060f1SDimitry Andric 36*fe6060f1SDimitry Andric STATISTIC(NumTLIFuncDeclAdded, 37*fe6060f1SDimitry Andric "Number of vector library function declarations added."); 38*fe6060f1SDimitry Andric 39*fe6060f1SDimitry Andric STATISTIC(NumFuncUsedAdded, 40*fe6060f1SDimitry Andric "Number of functions added to `llvm.compiler.used`"); 41*fe6060f1SDimitry Andric 42*fe6060f1SDimitry Andric static bool replaceWithTLIFunction(CallInst &CI, const StringRef TLIName) { 43*fe6060f1SDimitry Andric Module *M = CI.getModule(); 44*fe6060f1SDimitry Andric 45*fe6060f1SDimitry Andric Function *OldFunc = CI.getCalledFunction(); 46*fe6060f1SDimitry Andric 47*fe6060f1SDimitry Andric // Check if the vector library function is already declared in this module, 48*fe6060f1SDimitry Andric // otherwise insert it. 49*fe6060f1SDimitry Andric Function *TLIFunc = M->getFunction(TLIName); 50*fe6060f1SDimitry Andric if (!TLIFunc) { 51*fe6060f1SDimitry Andric TLIFunc = Function::Create(OldFunc->getFunctionType(), 52*fe6060f1SDimitry Andric Function::ExternalLinkage, TLIName, *M); 53*fe6060f1SDimitry Andric TLIFunc->copyAttributesFrom(OldFunc); 54*fe6060f1SDimitry Andric 55*fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Added vector library function `" 56*fe6060f1SDimitry Andric << TLIName << "` of type `" << *(TLIFunc->getType()) 57*fe6060f1SDimitry Andric << "` to module.\n"); 58*fe6060f1SDimitry Andric 59*fe6060f1SDimitry Andric ++NumTLIFuncDeclAdded; 60*fe6060f1SDimitry Andric 61*fe6060f1SDimitry Andric // Add the freshly created function to llvm.compiler.used, 62*fe6060f1SDimitry Andric // similar to as it is done in InjectTLIMappings 63*fe6060f1SDimitry Andric appendToCompilerUsed(*M, {TLIFunc}); 64*fe6060f1SDimitry Andric 65*fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Adding `" << TLIName 66*fe6060f1SDimitry Andric << "` to `@llvm.compiler.used`.\n"); 67*fe6060f1SDimitry Andric ++NumFuncUsedAdded; 68*fe6060f1SDimitry Andric } 69*fe6060f1SDimitry Andric 70*fe6060f1SDimitry Andric // Replace the call to the vector intrinsic with a call 71*fe6060f1SDimitry Andric // to the corresponding function from the vector library. 72*fe6060f1SDimitry Andric IRBuilder<> IRBuilder(&CI); 73*fe6060f1SDimitry Andric SmallVector<Value *> Args(CI.arg_operands()); 74*fe6060f1SDimitry Andric // Preserve the operand bundles. 75*fe6060f1SDimitry Andric SmallVector<OperandBundleDef, 1> OpBundles; 76*fe6060f1SDimitry Andric CI.getOperandBundlesAsDefs(OpBundles); 77*fe6060f1SDimitry Andric CallInst *Replacement = IRBuilder.CreateCall(TLIFunc, Args, OpBundles); 78*fe6060f1SDimitry Andric assert(OldFunc->getFunctionType() == TLIFunc->getFunctionType() && 79*fe6060f1SDimitry Andric "Expecting function types to be identical"); 80*fe6060f1SDimitry Andric CI.replaceAllUsesWith(Replacement); 81*fe6060f1SDimitry Andric if (isa<FPMathOperator>(Replacement)) { 82*fe6060f1SDimitry Andric // Preserve fast math flags for FP math. 83*fe6060f1SDimitry Andric Replacement->copyFastMathFlags(&CI); 84*fe6060f1SDimitry Andric } 85*fe6060f1SDimitry Andric 86*fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Replaced call to `" 87*fe6060f1SDimitry Andric << OldFunc->getName() << "` with call to `" << TLIName 88*fe6060f1SDimitry Andric << "`.\n"); 89*fe6060f1SDimitry Andric ++NumCallsReplaced; 90*fe6060f1SDimitry Andric return true; 91*fe6060f1SDimitry Andric } 92*fe6060f1SDimitry Andric 93*fe6060f1SDimitry Andric static bool replaceWithCallToVeclib(const TargetLibraryInfo &TLI, 94*fe6060f1SDimitry Andric CallInst &CI) { 95*fe6060f1SDimitry Andric if (!CI.getCalledFunction()) { 96*fe6060f1SDimitry Andric return false; 97*fe6060f1SDimitry Andric } 98*fe6060f1SDimitry Andric 99*fe6060f1SDimitry Andric auto IntrinsicID = CI.getCalledFunction()->getIntrinsicID(); 100*fe6060f1SDimitry Andric if (IntrinsicID == Intrinsic::not_intrinsic) { 101*fe6060f1SDimitry Andric // Replacement is only performed for intrinsic functions 102*fe6060f1SDimitry Andric return false; 103*fe6060f1SDimitry Andric } 104*fe6060f1SDimitry Andric 105*fe6060f1SDimitry Andric // Convert vector arguments to scalar type and check that 106*fe6060f1SDimitry Andric // all vector operands have identical vector width. 107*fe6060f1SDimitry Andric ElementCount VF = ElementCount::getFixed(0); 108*fe6060f1SDimitry Andric SmallVector<Type *> ScalarTypes; 109*fe6060f1SDimitry Andric for (auto Arg : enumerate(CI.arg_operands())) { 110*fe6060f1SDimitry Andric auto *ArgType = Arg.value()->getType(); 111*fe6060f1SDimitry Andric // Vector calls to intrinsics can still have 112*fe6060f1SDimitry Andric // scalar operands for specific arguments. 113*fe6060f1SDimitry Andric if (hasVectorInstrinsicScalarOpd(IntrinsicID, Arg.index())) { 114*fe6060f1SDimitry Andric ScalarTypes.push_back(ArgType); 115*fe6060f1SDimitry Andric } else { 116*fe6060f1SDimitry Andric // The argument in this place should be a vector if 117*fe6060f1SDimitry Andric // this is a call to a vector intrinsic. 118*fe6060f1SDimitry Andric auto *VectorArgTy = dyn_cast<VectorType>(ArgType); 119*fe6060f1SDimitry Andric if (!VectorArgTy) { 120*fe6060f1SDimitry Andric // The argument is not a vector, do not perform 121*fe6060f1SDimitry Andric // the replacement. 122*fe6060f1SDimitry Andric return false; 123*fe6060f1SDimitry Andric } 124*fe6060f1SDimitry Andric ElementCount NumElements = VectorArgTy->getElementCount(); 125*fe6060f1SDimitry Andric if (NumElements.isScalable()) { 126*fe6060f1SDimitry Andric // The current implementation does not support 127*fe6060f1SDimitry Andric // scalable vectors. 128*fe6060f1SDimitry Andric return false; 129*fe6060f1SDimitry Andric } 130*fe6060f1SDimitry Andric if (VF.isNonZero() && VF != NumElements) { 131*fe6060f1SDimitry Andric // The different arguments differ in vector size. 132*fe6060f1SDimitry Andric return false; 133*fe6060f1SDimitry Andric } else { 134*fe6060f1SDimitry Andric VF = NumElements; 135*fe6060f1SDimitry Andric } 136*fe6060f1SDimitry Andric ScalarTypes.push_back(VectorArgTy->getElementType()); 137*fe6060f1SDimitry Andric } 138*fe6060f1SDimitry Andric } 139*fe6060f1SDimitry Andric 140*fe6060f1SDimitry Andric // Try to reconstruct the name for the scalar version of this 141*fe6060f1SDimitry Andric // intrinsic using the intrinsic ID and the argument types 142*fe6060f1SDimitry Andric // converted to scalar above. 143*fe6060f1SDimitry Andric std::string ScalarName; 144*fe6060f1SDimitry Andric if (Intrinsic::isOverloaded(IntrinsicID)) { 145*fe6060f1SDimitry Andric ScalarName = Intrinsic::getName(IntrinsicID, ScalarTypes, CI.getModule()); 146*fe6060f1SDimitry Andric } else { 147*fe6060f1SDimitry Andric ScalarName = Intrinsic::getName(IntrinsicID).str(); 148*fe6060f1SDimitry Andric } 149*fe6060f1SDimitry Andric 150*fe6060f1SDimitry Andric if (!TLI.isFunctionVectorizable(ScalarName)) { 151*fe6060f1SDimitry Andric // The TargetLibraryInfo does not contain a vectorized version of 152*fe6060f1SDimitry Andric // the scalar function. 153*fe6060f1SDimitry Andric return false; 154*fe6060f1SDimitry Andric } 155*fe6060f1SDimitry Andric 156*fe6060f1SDimitry Andric // Try to find the mapping for the scalar version of this intrinsic 157*fe6060f1SDimitry Andric // and the exact vector width of the call operands in the 158*fe6060f1SDimitry Andric // TargetLibraryInfo. 159*fe6060f1SDimitry Andric const std::string TLIName = 160*fe6060f1SDimitry Andric std::string(TLI.getVectorizedFunction(ScalarName, VF)); 161*fe6060f1SDimitry Andric 162*fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Looking up TLI mapping for `" 163*fe6060f1SDimitry Andric << ScalarName << "` and vector width " << VF << ".\n"); 164*fe6060f1SDimitry Andric 165*fe6060f1SDimitry Andric if (!TLIName.empty()) { 166*fe6060f1SDimitry Andric // Found the correct mapping in the TargetLibraryInfo, 167*fe6060f1SDimitry Andric // replace the call to the intrinsic with a call to 168*fe6060f1SDimitry Andric // the vector library function. 169*fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Found TLI function `" << TLIName 170*fe6060f1SDimitry Andric << "`.\n"); 171*fe6060f1SDimitry Andric return replaceWithTLIFunction(CI, TLIName); 172*fe6060f1SDimitry Andric } 173*fe6060f1SDimitry Andric 174*fe6060f1SDimitry Andric return false; 175*fe6060f1SDimitry Andric } 176*fe6060f1SDimitry Andric 177*fe6060f1SDimitry Andric static bool runImpl(const TargetLibraryInfo &TLI, Function &F) { 178*fe6060f1SDimitry Andric bool Changed = false; 179*fe6060f1SDimitry Andric SmallVector<CallInst *> ReplacedCalls; 180*fe6060f1SDimitry Andric for (auto &I : instructions(F)) { 181*fe6060f1SDimitry Andric if (auto *CI = dyn_cast<CallInst>(&I)) { 182*fe6060f1SDimitry Andric if (replaceWithCallToVeclib(TLI, *CI)) { 183*fe6060f1SDimitry Andric ReplacedCalls.push_back(CI); 184*fe6060f1SDimitry Andric Changed = true; 185*fe6060f1SDimitry Andric } 186*fe6060f1SDimitry Andric } 187*fe6060f1SDimitry Andric } 188*fe6060f1SDimitry Andric // Erase the calls to the intrinsics that have been replaced 189*fe6060f1SDimitry Andric // with calls to the vector library. 190*fe6060f1SDimitry Andric for (auto *CI : ReplacedCalls) { 191*fe6060f1SDimitry Andric CI->eraseFromParent(); 192*fe6060f1SDimitry Andric } 193*fe6060f1SDimitry Andric return Changed; 194*fe6060f1SDimitry Andric } 195*fe6060f1SDimitry Andric 196*fe6060f1SDimitry Andric //////////////////////////////////////////////////////////////////////////////// 197*fe6060f1SDimitry Andric // New pass manager implementation. 198*fe6060f1SDimitry Andric //////////////////////////////////////////////////////////////////////////////// 199*fe6060f1SDimitry Andric PreservedAnalyses ReplaceWithVeclib::run(Function &F, 200*fe6060f1SDimitry Andric FunctionAnalysisManager &AM) { 201*fe6060f1SDimitry Andric const TargetLibraryInfo &TLI = AM.getResult<TargetLibraryAnalysis>(F); 202*fe6060f1SDimitry Andric auto Changed = runImpl(TLI, F); 203*fe6060f1SDimitry Andric if (Changed) { 204*fe6060f1SDimitry Andric PreservedAnalyses PA; 205*fe6060f1SDimitry Andric PA.preserveSet<CFGAnalyses>(); 206*fe6060f1SDimitry Andric PA.preserve<TargetLibraryAnalysis>(); 207*fe6060f1SDimitry Andric PA.preserve<ScalarEvolutionAnalysis>(); 208*fe6060f1SDimitry Andric PA.preserve<LoopAccessAnalysis>(); 209*fe6060f1SDimitry Andric PA.preserve<DemandedBitsAnalysis>(); 210*fe6060f1SDimitry Andric PA.preserve<OptimizationRemarkEmitterAnalysis>(); 211*fe6060f1SDimitry Andric return PA; 212*fe6060f1SDimitry Andric } else { 213*fe6060f1SDimitry Andric // The pass did not replace any calls, hence it preserves all analyses. 214*fe6060f1SDimitry Andric return PreservedAnalyses::all(); 215*fe6060f1SDimitry Andric } 216*fe6060f1SDimitry Andric } 217*fe6060f1SDimitry Andric 218*fe6060f1SDimitry Andric //////////////////////////////////////////////////////////////////////////////// 219*fe6060f1SDimitry Andric // Legacy PM Implementation. 220*fe6060f1SDimitry Andric //////////////////////////////////////////////////////////////////////////////// 221*fe6060f1SDimitry Andric bool ReplaceWithVeclibLegacy::runOnFunction(Function &F) { 222*fe6060f1SDimitry Andric const TargetLibraryInfo &TLI = 223*fe6060f1SDimitry Andric getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F); 224*fe6060f1SDimitry Andric return runImpl(TLI, F); 225*fe6060f1SDimitry Andric } 226*fe6060f1SDimitry Andric 227*fe6060f1SDimitry Andric void ReplaceWithVeclibLegacy::getAnalysisUsage(AnalysisUsage &AU) const { 228*fe6060f1SDimitry Andric AU.setPreservesCFG(); 229*fe6060f1SDimitry Andric AU.addRequired<TargetLibraryInfoWrapperPass>(); 230*fe6060f1SDimitry Andric AU.addPreserved<TargetLibraryInfoWrapperPass>(); 231*fe6060f1SDimitry Andric AU.addPreserved<ScalarEvolutionWrapperPass>(); 232*fe6060f1SDimitry Andric AU.addPreserved<AAResultsWrapperPass>(); 233*fe6060f1SDimitry Andric AU.addPreserved<LoopAccessLegacyAnalysis>(); 234*fe6060f1SDimitry Andric AU.addPreserved<DemandedBitsWrapperPass>(); 235*fe6060f1SDimitry Andric AU.addPreserved<OptimizationRemarkEmitterWrapperPass>(); 236*fe6060f1SDimitry Andric AU.addPreserved<GlobalsAAWrapperPass>(); 237*fe6060f1SDimitry Andric } 238*fe6060f1SDimitry Andric 239*fe6060f1SDimitry Andric //////////////////////////////////////////////////////////////////////////////// 240*fe6060f1SDimitry Andric // Legacy Pass manager initialization 241*fe6060f1SDimitry Andric //////////////////////////////////////////////////////////////////////////////// 242*fe6060f1SDimitry Andric char ReplaceWithVeclibLegacy::ID = 0; 243*fe6060f1SDimitry Andric 244*fe6060f1SDimitry Andric INITIALIZE_PASS_BEGIN(ReplaceWithVeclibLegacy, DEBUG_TYPE, 245*fe6060f1SDimitry Andric "Replace intrinsics with calls to vector library", false, 246*fe6060f1SDimitry Andric false) 247*fe6060f1SDimitry Andric INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) 248*fe6060f1SDimitry Andric INITIALIZE_PASS_END(ReplaceWithVeclibLegacy, DEBUG_TYPE, 249*fe6060f1SDimitry Andric "Replace intrinsics with calls to vector library", false, 250*fe6060f1SDimitry Andric false) 251*fe6060f1SDimitry Andric 252*fe6060f1SDimitry Andric FunctionPass *llvm::createReplaceWithVeclibLegacyPass() { 253*fe6060f1SDimitry Andric return new ReplaceWithVeclibLegacy(); 254*fe6060f1SDimitry Andric } 255