181ad6265SDimitry Andric //=== ReplaceWithVeclib.cpp - Replace vector intrinsics with veclib calls -===// 2fe6060f1SDimitry Andric // 3fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6fe6060f1SDimitry Andric // 7fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 8fe6060f1SDimitry Andric // 9*0fca6ea1SDimitry Andric // Replaces calls to LLVM Intrinsics with matching calls to functions from a 10*0fca6ea1SDimitry Andric // vector library (e.g libmvec, SVML) using TargetLibraryInfo interface. 11fe6060f1SDimitry Andric // 12fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 13fe6060f1SDimitry Andric 14fe6060f1SDimitry Andric #include "llvm/CodeGen/ReplaceWithVeclib.h" 15fe6060f1SDimitry Andric #include "llvm/ADT/STLExtras.h" 16fe6060f1SDimitry Andric #include "llvm/ADT/Statistic.h" 17cb14a3feSDimitry Andric #include "llvm/ADT/StringRef.h" 18fe6060f1SDimitry Andric #include "llvm/Analysis/DemandedBits.h" 19fe6060f1SDimitry Andric #include "llvm/Analysis/GlobalsModRef.h" 20fe6060f1SDimitry Andric #include "llvm/Analysis/OptimizationRemarkEmitter.h" 21fe6060f1SDimitry Andric #include "llvm/Analysis/TargetLibraryInfo.h" 22fe6060f1SDimitry Andric #include "llvm/Analysis/VectorUtils.h" 23fe6060f1SDimitry Andric #include "llvm/CodeGen/Passes.h" 24cb14a3feSDimitry Andric #include "llvm/IR/DerivedTypes.h" 25fe6060f1SDimitry Andric #include "llvm/IR/IRBuilder.h" 26fe6060f1SDimitry Andric #include "llvm/IR/InstIterator.h" 27*0fca6ea1SDimitry Andric #include "llvm/IR/IntrinsicInst.h" 287a6dacacSDimitry Andric #include "llvm/IR/VFABIDemangler.h" 29cb14a3feSDimitry Andric #include "llvm/Support/TypeSize.h" 30fe6060f1SDimitry Andric #include "llvm/Transforms/Utils/ModuleUtils.h" 31fe6060f1SDimitry Andric 32fe6060f1SDimitry Andric using namespace llvm; 33fe6060f1SDimitry Andric 34fe6060f1SDimitry Andric #define DEBUG_TYPE "replace-with-veclib" 35fe6060f1SDimitry Andric 36fe6060f1SDimitry Andric STATISTIC(NumCallsReplaced, 37fe6060f1SDimitry Andric "Number of calls to intrinsics that have been replaced."); 38fe6060f1SDimitry Andric 39fe6060f1SDimitry Andric STATISTIC(NumTLIFuncDeclAdded, 40fe6060f1SDimitry Andric "Number of vector library function declarations added."); 41fe6060f1SDimitry Andric 42fe6060f1SDimitry Andric STATISTIC(NumFuncUsedAdded, 43fe6060f1SDimitry Andric "Number of functions added to `llvm.compiler.used`"); 44fe6060f1SDimitry Andric 45cb14a3feSDimitry Andric /// Returns a vector Function that it adds to the Module \p M. When an \p 46cb14a3feSDimitry Andric /// ScalarFunc is not null, it copies its attributes to the newly created 47cb14a3feSDimitry Andric /// Function. 48cb14a3feSDimitry Andric Function *getTLIFunction(Module *M, FunctionType *VectorFTy, 49cb14a3feSDimitry Andric const StringRef TLIName, 50cb14a3feSDimitry Andric Function *ScalarFunc = nullptr) { 51fe6060f1SDimitry Andric Function *TLIFunc = M->getFunction(TLIName); 52fe6060f1SDimitry Andric if (!TLIFunc) { 53cb14a3feSDimitry Andric TLIFunc = 54cb14a3feSDimitry Andric Function::Create(VectorFTy, Function::ExternalLinkage, TLIName, *M); 55cb14a3feSDimitry Andric if (ScalarFunc) 56cb14a3feSDimitry Andric TLIFunc->copyAttributesFrom(ScalarFunc); 57fe6060f1SDimitry Andric 58fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Added vector library function `" 59fe6060f1SDimitry Andric << TLIName << "` of type `" << *(TLIFunc->getType()) 60fe6060f1SDimitry Andric << "` to module.\n"); 61fe6060f1SDimitry Andric 62fe6060f1SDimitry Andric ++NumTLIFuncDeclAdded; 63cb14a3feSDimitry Andric // Add the freshly created function to llvm.compiler.used, similar to as it 64cb14a3feSDimitry Andric // is done in InjectTLIMappings. 65fe6060f1SDimitry Andric appendToCompilerUsed(*M, {TLIFunc}); 66fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Adding `" << TLIName 67fe6060f1SDimitry Andric << "` to `@llvm.compiler.used`.\n"); 68fe6060f1SDimitry Andric ++NumFuncUsedAdded; 69fe6060f1SDimitry Andric } 70cb14a3feSDimitry Andric return TLIFunc; 71cb14a3feSDimitry Andric } 72fe6060f1SDimitry Andric 73*0fca6ea1SDimitry Andric /// Replace the intrinsic call \p II to \p TLIVecFunc, which is the 74*0fca6ea1SDimitry Andric /// corresponding function from the vector library. 75*0fca6ea1SDimitry Andric static void replaceWithTLIFunction(IntrinsicInst *II, VFInfo &Info, 76cb14a3feSDimitry Andric Function *TLIVecFunc) { 77*0fca6ea1SDimitry Andric IRBuilder<> IRBuilder(II); 78*0fca6ea1SDimitry Andric SmallVector<Value *> Args(II->args()); 79cb14a3feSDimitry Andric if (auto OptMaskpos = Info.getParamIndexForOptionalMask()) { 801db9f3b2SDimitry Andric auto *MaskTy = 81*0fca6ea1SDimitry Andric VectorType::get(Type::getInt1Ty(II->getContext()), Info.Shape.VF); 82cb14a3feSDimitry Andric Args.insert(Args.begin() + OptMaskpos.value(), 83cb14a3feSDimitry Andric Constant::getAllOnesValue(MaskTy)); 84cb14a3feSDimitry Andric } 85cb14a3feSDimitry Andric 86*0fca6ea1SDimitry Andric // Preserve the operand bundles. 87fe6060f1SDimitry Andric SmallVector<OperandBundleDef, 1> OpBundles; 88*0fca6ea1SDimitry Andric II->getOperandBundlesAsDefs(OpBundles); 891db9f3b2SDimitry Andric 901db9f3b2SDimitry Andric auto *Replacement = IRBuilder.CreateCall(TLIVecFunc, Args, OpBundles); 91*0fca6ea1SDimitry Andric II->replaceAllUsesWith(Replacement); 92fe6060f1SDimitry Andric // Preserve fast math flags for FP math. 93cb14a3feSDimitry Andric if (isa<FPMathOperator>(Replacement)) 94*0fca6ea1SDimitry Andric Replacement->copyFastMathFlags(II); 95fe6060f1SDimitry Andric } 96fe6060f1SDimitry Andric 97*0fca6ea1SDimitry Andric /// Returns true when successfully replaced \p II, which is a call to a 98*0fca6ea1SDimitry Andric /// vectorized intrinsic, with a suitable function taking vector arguments, 99*0fca6ea1SDimitry Andric /// based on available mappings in the \p TLI. 100cb14a3feSDimitry Andric static bool replaceWithCallToVeclib(const TargetLibraryInfo &TLI, 101*0fca6ea1SDimitry Andric IntrinsicInst *II) { 102*0fca6ea1SDimitry Andric assert(II != nullptr && "Intrinsic cannot be null"); 1031db9f3b2SDimitry Andric // At the moment VFABI assumes the return type is always widened unless it is 1041db9f3b2SDimitry Andric // a void type. 105*0fca6ea1SDimitry Andric auto *VTy = dyn_cast<VectorType>(II->getType()); 1061db9f3b2SDimitry Andric ElementCount EC(VTy ? VTy->getElementCount() : ElementCount::getFixed(0)); 107*0fca6ea1SDimitry Andric // Compute the argument types of the corresponding scalar call and check that 108*0fca6ea1SDimitry Andric // all vector operands match the previously found EC. 1091db9f3b2SDimitry Andric SmallVector<Type *, 8> ScalarArgTypes; 110*0fca6ea1SDimitry Andric Intrinsic::ID IID = II->getIntrinsicID(); 111*0fca6ea1SDimitry Andric for (auto Arg : enumerate(II->args())) { 112cb14a3feSDimitry Andric auto *ArgTy = Arg.value()->getType(); 1131db9f3b2SDimitry Andric if (isVectorIntrinsicWithScalarOpAtArg(IID, Arg.index())) { 114cb14a3feSDimitry Andric ScalarArgTypes.push_back(ArgTy); 115cb14a3feSDimitry Andric } else if (auto *VectorArgTy = dyn_cast<VectorType>(ArgTy)) { 1161db9f3b2SDimitry Andric ScalarArgTypes.push_back(VectorArgTy->getElementType()); 1171db9f3b2SDimitry Andric // When return type is void, set EC to the first vector argument, and 1181db9f3b2SDimitry Andric // disallow vector arguments with different ECs. 1191db9f3b2SDimitry Andric if (EC.isZero()) 1201db9f3b2SDimitry Andric EC = VectorArgTy->getElementCount(); 1211db9f3b2SDimitry Andric else if (EC != VectorArgTy->getElementCount()) 122cb14a3feSDimitry Andric return false; 123cb14a3feSDimitry Andric } else 124cb14a3feSDimitry Andric // Exit when it is supposed to be a vector argument but it isn't. 125cb14a3feSDimitry Andric return false; 126cb14a3feSDimitry Andric } 127*0fca6ea1SDimitry Andric 1281db9f3b2SDimitry Andric // Try to reconstruct the name for the scalar version of the instruction, 1291db9f3b2SDimitry Andric // using scalar argument types. 130*0fca6ea1SDimitry Andric std::string ScalarName = 131*0fca6ea1SDimitry Andric Intrinsic::isOverloaded(IID) 132*0fca6ea1SDimitry Andric ? Intrinsic::getName(IID, ScalarArgTypes, II->getModule()) 1331db9f3b2SDimitry Andric : Intrinsic::getName(IID).str(); 134cb14a3feSDimitry Andric 135cb14a3feSDimitry Andric // Try to find the mapping for the scalar version of this intrinsic and the 136cb14a3feSDimitry Andric // exact vector width of the call operands in the TargetLibraryInfo. First, 137cb14a3feSDimitry Andric // check with a non-masked variant, and if that fails try with a masked one. 138cb14a3feSDimitry Andric const VecDesc *VD = 1391db9f3b2SDimitry Andric TLI.getVectorMappingInfo(ScalarName, EC, /*Masked*/ false); 1401db9f3b2SDimitry Andric if (!VD && !(VD = TLI.getVectorMappingInfo(ScalarName, EC, /*Masked*/ true))) 141cb14a3feSDimitry Andric return false; 142cb14a3feSDimitry Andric 143cb14a3feSDimitry Andric LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Found TLI mapping from: `" << ScalarName 1441db9f3b2SDimitry Andric << "` and vector width " << EC << " to: `" 145cb14a3feSDimitry Andric << VD->getVectorFnName() << "`.\n"); 146cb14a3feSDimitry Andric 147cb14a3feSDimitry Andric // Replace the call to the intrinsic with a call to the vector library 148cb14a3feSDimitry Andric // function. 149*0fca6ea1SDimitry Andric Type *ScalarRetTy = II->getType()->getScalarType(); 150cb14a3feSDimitry Andric FunctionType *ScalarFTy = 151cb14a3feSDimitry Andric FunctionType::get(ScalarRetTy, ScalarArgTypes, /*isVarArg*/ false); 152cb14a3feSDimitry Andric const std::string MangledName = VD->getVectorFunctionABIVariantString(); 153cb14a3feSDimitry Andric auto OptInfo = VFABI::tryDemangleForVFABI(MangledName, ScalarFTy); 154cb14a3feSDimitry Andric if (!OptInfo) 155cb14a3feSDimitry Andric return false; 156cb14a3feSDimitry Andric 1577a6dacacSDimitry Andric // There is no guarantee that the vectorized instructions followed the VFABI 1587a6dacacSDimitry Andric // specification when being created, this is why we need to add extra check to 1597a6dacacSDimitry Andric // make sure that the operands of the vector function obtained via VFABI match 1607a6dacacSDimitry Andric // the operands of the original vector instruction. 161*0fca6ea1SDimitry Andric for (auto &VFParam : OptInfo->Shape.Parameters) { 1627a6dacacSDimitry Andric if (VFParam.ParamKind == VFParamKind::GlobalPredicate) 1637a6dacacSDimitry Andric continue; 1647a6dacacSDimitry Andric 1657a6dacacSDimitry Andric // tryDemangleForVFABI must return valid ParamPos, otherwise it could be 1667a6dacacSDimitry Andric // a bug in the VFABI parser. 167*0fca6ea1SDimitry Andric assert(VFParam.ParamPos < II->arg_size() && "ParamPos has invalid range"); 168*0fca6ea1SDimitry Andric Type *OrigTy = II->getArgOperand(VFParam.ParamPos)->getType(); 1697a6dacacSDimitry Andric if (OrigTy->isVectorTy() != (VFParam.ParamKind == VFParamKind::Vector)) { 1707a6dacacSDimitry Andric LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Will not replace: " << ScalarName 171*0fca6ea1SDimitry Andric << ". Wrong type at index " << VFParam.ParamPos << ": " 172*0fca6ea1SDimitry Andric << *OrigTy << "\n"); 1737a6dacacSDimitry Andric return false; 1747a6dacacSDimitry Andric } 1757a6dacacSDimitry Andric } 1767a6dacacSDimitry Andric 177cb14a3feSDimitry Andric FunctionType *VectorFTy = VFABI::createFunctionType(*OptInfo, ScalarFTy); 178cb14a3feSDimitry Andric if (!VectorFTy) 179cb14a3feSDimitry Andric return false; 180cb14a3feSDimitry Andric 181*0fca6ea1SDimitry Andric Function *TLIFunc = 182*0fca6ea1SDimitry Andric getTLIFunction(II->getModule(), VectorFTy, VD->getVectorFnName(), 183*0fca6ea1SDimitry Andric II->getCalledFunction()); 184*0fca6ea1SDimitry Andric replaceWithTLIFunction(II, *OptInfo, TLIFunc); 1851db9f3b2SDimitry Andric LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Replaced call to `" << ScalarName 1861db9f3b2SDimitry Andric << "` with call to `" << TLIFunc->getName() << "`.\n"); 187fe6060f1SDimitry Andric ++NumCallsReplaced; 188fe6060f1SDimitry Andric return true; 189fe6060f1SDimitry Andric } 190fe6060f1SDimitry Andric 191fe6060f1SDimitry Andric static bool runImpl(const TargetLibraryInfo &TLI, Function &F) { 1921db9f3b2SDimitry Andric SmallVector<Instruction *> ReplacedCalls; 193fe6060f1SDimitry Andric for (auto &I : instructions(F)) { 194*0fca6ea1SDimitry Andric // Process only intrinsic calls that return void or a vector. 195*0fca6ea1SDimitry Andric if (auto *II = dyn_cast<IntrinsicInst>(&I)) { 196*0fca6ea1SDimitry Andric if (!II->getType()->isVectorTy() && !II->getType()->isVoidTy()) 1971db9f3b2SDimitry Andric continue; 198*0fca6ea1SDimitry Andric 199*0fca6ea1SDimitry Andric if (replaceWithCallToVeclib(TLI, II)) 2001db9f3b2SDimitry Andric ReplacedCalls.push_back(&I); 201fe6060f1SDimitry Andric } 202fe6060f1SDimitry Andric } 203*0fca6ea1SDimitry Andric // Erase any intrinsic calls that were replaced with vector library calls. 204*0fca6ea1SDimitry Andric for (auto *I : ReplacedCalls) 205*0fca6ea1SDimitry Andric I->eraseFromParent(); 206*0fca6ea1SDimitry Andric return !ReplacedCalls.empty(); 207fe6060f1SDimitry Andric } 208fe6060f1SDimitry Andric 209fe6060f1SDimitry Andric //////////////////////////////////////////////////////////////////////////////// 210fe6060f1SDimitry Andric // New pass manager implementation. 211fe6060f1SDimitry Andric //////////////////////////////////////////////////////////////////////////////// 212fe6060f1SDimitry Andric PreservedAnalyses ReplaceWithVeclib::run(Function &F, 213fe6060f1SDimitry Andric FunctionAnalysisManager &AM) { 214fe6060f1SDimitry Andric const TargetLibraryInfo &TLI = AM.getResult<TargetLibraryAnalysis>(F); 215fe6060f1SDimitry Andric auto Changed = runImpl(TLI, F); 216fe6060f1SDimitry Andric if (Changed) { 217*0fca6ea1SDimitry Andric LLVM_DEBUG(dbgs() << "Intrinsic calls replaced with vector libraries: " 2187a6dacacSDimitry Andric << NumCallsReplaced << "\n"); 2197a6dacacSDimitry Andric 220fe6060f1SDimitry Andric PreservedAnalyses PA; 221fe6060f1SDimitry Andric PA.preserveSet<CFGAnalyses>(); 222fe6060f1SDimitry Andric PA.preserve<TargetLibraryAnalysis>(); 223fe6060f1SDimitry Andric PA.preserve<ScalarEvolutionAnalysis>(); 224fe6060f1SDimitry Andric PA.preserve<LoopAccessAnalysis>(); 225fe6060f1SDimitry Andric PA.preserve<DemandedBitsAnalysis>(); 226fe6060f1SDimitry Andric PA.preserve<OptimizationRemarkEmitterAnalysis>(); 227fe6060f1SDimitry Andric return PA; 228cb14a3feSDimitry Andric } 229cb14a3feSDimitry Andric 230fe6060f1SDimitry Andric // The pass did not replace any calls, hence it preserves all analyses. 231fe6060f1SDimitry Andric return PreservedAnalyses::all(); 232fe6060f1SDimitry Andric } 233fe6060f1SDimitry Andric 234fe6060f1SDimitry Andric //////////////////////////////////////////////////////////////////////////////// 235fe6060f1SDimitry Andric // Legacy PM Implementation. 236fe6060f1SDimitry Andric //////////////////////////////////////////////////////////////////////////////// 237fe6060f1SDimitry Andric bool ReplaceWithVeclibLegacy::runOnFunction(Function &F) { 238fe6060f1SDimitry Andric const TargetLibraryInfo &TLI = 239fe6060f1SDimitry Andric getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F); 240fe6060f1SDimitry Andric return runImpl(TLI, F); 241fe6060f1SDimitry Andric } 242fe6060f1SDimitry Andric 243fe6060f1SDimitry Andric void ReplaceWithVeclibLegacy::getAnalysisUsage(AnalysisUsage &AU) const { 244fe6060f1SDimitry Andric AU.setPreservesCFG(); 245fe6060f1SDimitry Andric AU.addRequired<TargetLibraryInfoWrapperPass>(); 246fe6060f1SDimitry Andric AU.addPreserved<TargetLibraryInfoWrapperPass>(); 247fe6060f1SDimitry Andric AU.addPreserved<ScalarEvolutionWrapperPass>(); 248fe6060f1SDimitry Andric AU.addPreserved<AAResultsWrapperPass>(); 249fe6060f1SDimitry Andric AU.addPreserved<OptimizationRemarkEmitterWrapperPass>(); 250fe6060f1SDimitry Andric AU.addPreserved<GlobalsAAWrapperPass>(); 251fe6060f1SDimitry Andric } 252fe6060f1SDimitry Andric 253fe6060f1SDimitry Andric //////////////////////////////////////////////////////////////////////////////// 254fe6060f1SDimitry Andric // Legacy Pass manager initialization 255fe6060f1SDimitry Andric //////////////////////////////////////////////////////////////////////////////// 256fe6060f1SDimitry Andric char ReplaceWithVeclibLegacy::ID = 0; 257fe6060f1SDimitry Andric 258fe6060f1SDimitry Andric INITIALIZE_PASS_BEGIN(ReplaceWithVeclibLegacy, DEBUG_TYPE, 259fe6060f1SDimitry Andric "Replace intrinsics with calls to vector library", false, 260fe6060f1SDimitry Andric false) 261fe6060f1SDimitry Andric INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) 262fe6060f1SDimitry Andric INITIALIZE_PASS_END(ReplaceWithVeclibLegacy, DEBUG_TYPE, 263fe6060f1SDimitry Andric "Replace intrinsics with calls to vector library", false, 264fe6060f1SDimitry Andric false) 265fe6060f1SDimitry Andric 266fe6060f1SDimitry Andric FunctionPass *llvm::createReplaceWithVeclibLegacyPass() { 267fe6060f1SDimitry Andric return new ReplaceWithVeclibLegacy(); 268fe6060f1SDimitry Andric } 269