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*1db9f3b2SDimitry Andric // Replaces LLVM IR instructions with vector operands (i.e., the frem 10*1db9f3b2SDimitry Andric // instruction or calls to LLVM intrinsics) with matching calls to functions 11*1db9f3b2SDimitry Andric // from a vector library (e.g libmvec, SVML) using TargetLibraryInfo interface. 12fe6060f1SDimitry Andric // 13fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 14fe6060f1SDimitry Andric 15fe6060f1SDimitry Andric #include "llvm/CodeGen/ReplaceWithVeclib.h" 16fe6060f1SDimitry Andric #include "llvm/ADT/STLExtras.h" 17fe6060f1SDimitry Andric #include "llvm/ADT/Statistic.h" 18cb14a3feSDimitry Andric #include "llvm/ADT/StringRef.h" 19fe6060f1SDimitry Andric #include "llvm/Analysis/DemandedBits.h" 20fe6060f1SDimitry Andric #include "llvm/Analysis/GlobalsModRef.h" 21fe6060f1SDimitry Andric #include "llvm/Analysis/OptimizationRemarkEmitter.h" 22fe6060f1SDimitry Andric #include "llvm/Analysis/TargetLibraryInfo.h" 23fe6060f1SDimitry Andric #include "llvm/Analysis/VectorUtils.h" 24fe6060f1SDimitry Andric #include "llvm/CodeGen/Passes.h" 25cb14a3feSDimitry Andric #include "llvm/IR/DerivedTypes.h" 26fe6060f1SDimitry Andric #include "llvm/IR/IRBuilder.h" 27fe6060f1SDimitry Andric #include "llvm/IR/InstIterator.h" 28cb14a3feSDimitry Andric #include "llvm/Support/TypeSize.h" 29fe6060f1SDimitry Andric #include "llvm/Transforms/Utils/ModuleUtils.h" 30fe6060f1SDimitry Andric 31fe6060f1SDimitry Andric using namespace llvm; 32fe6060f1SDimitry Andric 33fe6060f1SDimitry Andric #define DEBUG_TYPE "replace-with-veclib" 34fe6060f1SDimitry Andric 35fe6060f1SDimitry Andric STATISTIC(NumCallsReplaced, 36fe6060f1SDimitry Andric "Number of calls to intrinsics that have been replaced."); 37fe6060f1SDimitry Andric 38fe6060f1SDimitry Andric STATISTIC(NumTLIFuncDeclAdded, 39fe6060f1SDimitry Andric "Number of vector library function declarations added."); 40fe6060f1SDimitry Andric 41fe6060f1SDimitry Andric STATISTIC(NumFuncUsedAdded, 42fe6060f1SDimitry Andric "Number of functions added to `llvm.compiler.used`"); 43fe6060f1SDimitry Andric 44cb14a3feSDimitry Andric /// Returns a vector Function that it adds to the Module \p M. When an \p 45cb14a3feSDimitry Andric /// ScalarFunc is not null, it copies its attributes to the newly created 46cb14a3feSDimitry Andric /// Function. 47cb14a3feSDimitry Andric Function *getTLIFunction(Module *M, FunctionType *VectorFTy, 48cb14a3feSDimitry Andric const StringRef TLIName, 49cb14a3feSDimitry Andric Function *ScalarFunc = nullptr) { 50fe6060f1SDimitry Andric Function *TLIFunc = M->getFunction(TLIName); 51fe6060f1SDimitry Andric if (!TLIFunc) { 52cb14a3feSDimitry Andric TLIFunc = 53cb14a3feSDimitry Andric Function::Create(VectorFTy, Function::ExternalLinkage, TLIName, *M); 54cb14a3feSDimitry Andric if (ScalarFunc) 55cb14a3feSDimitry Andric TLIFunc->copyAttributesFrom(ScalarFunc); 56fe6060f1SDimitry Andric 57fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Added vector library function `" 58fe6060f1SDimitry Andric << TLIName << "` of type `" << *(TLIFunc->getType()) 59fe6060f1SDimitry Andric << "` to module.\n"); 60fe6060f1SDimitry Andric 61fe6060f1SDimitry Andric ++NumTLIFuncDeclAdded; 62cb14a3feSDimitry Andric // Add the freshly created function to llvm.compiler.used, similar to as it 63cb14a3feSDimitry Andric // is done in InjectTLIMappings. 64fe6060f1SDimitry Andric appendToCompilerUsed(*M, {TLIFunc}); 65fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Adding `" << TLIName 66fe6060f1SDimitry Andric << "` to `@llvm.compiler.used`.\n"); 67fe6060f1SDimitry Andric ++NumFuncUsedAdded; 68fe6060f1SDimitry Andric } 69cb14a3feSDimitry Andric return TLIFunc; 70cb14a3feSDimitry Andric } 71fe6060f1SDimitry Andric 72*1db9f3b2SDimitry Andric /// Replace the instruction \p I with a call to the corresponding function from 73*1db9f3b2SDimitry Andric /// the vector library (\p TLIVecFunc). 74*1db9f3b2SDimitry Andric static void replaceWithTLIFunction(Instruction &I, VFInfo &Info, 75cb14a3feSDimitry Andric Function *TLIVecFunc) { 76*1db9f3b2SDimitry Andric IRBuilder<> IRBuilder(&I); 77*1db9f3b2SDimitry Andric auto *CI = dyn_cast<CallInst>(&I); 78*1db9f3b2SDimitry Andric SmallVector<Value *> Args(CI ? CI->args() : I.operands()); 79cb14a3feSDimitry Andric if (auto OptMaskpos = Info.getParamIndexForOptionalMask()) { 80*1db9f3b2SDimitry Andric auto *MaskTy = 81*1db9f3b2SDimitry Andric VectorType::get(Type::getInt1Ty(I.getContext()), Info.Shape.VF); 82cb14a3feSDimitry Andric Args.insert(Args.begin() + OptMaskpos.value(), 83cb14a3feSDimitry Andric Constant::getAllOnesValue(MaskTy)); 84cb14a3feSDimitry Andric } 85cb14a3feSDimitry Andric 86*1db9f3b2SDimitry Andric // If it is a call instruction, preserve the operand bundles. 87fe6060f1SDimitry Andric SmallVector<OperandBundleDef, 1> OpBundles; 88*1db9f3b2SDimitry Andric if (CI) 89*1db9f3b2SDimitry Andric CI->getOperandBundlesAsDefs(OpBundles); 90*1db9f3b2SDimitry Andric 91*1db9f3b2SDimitry Andric auto *Replacement = IRBuilder.CreateCall(TLIVecFunc, Args, OpBundles); 92*1db9f3b2SDimitry Andric I.replaceAllUsesWith(Replacement); 93fe6060f1SDimitry Andric // Preserve fast math flags for FP math. 94cb14a3feSDimitry Andric if (isa<FPMathOperator>(Replacement)) 95*1db9f3b2SDimitry Andric Replacement->copyFastMathFlags(&I); 96fe6060f1SDimitry Andric } 97fe6060f1SDimitry Andric 98*1db9f3b2SDimitry Andric /// Returns true when successfully replaced \p I with a suitable function taking 99*1db9f3b2SDimitry Andric /// vector arguments, based on available mappings in the \p TLI. Currently only 100*1db9f3b2SDimitry Andric /// works when \p I is a call to vectorized intrinsic or the frem instruction. 101cb14a3feSDimitry Andric static bool replaceWithCallToVeclib(const TargetLibraryInfo &TLI, 102*1db9f3b2SDimitry Andric Instruction &I) { 103*1db9f3b2SDimitry Andric // At the moment VFABI assumes the return type is always widened unless it is 104*1db9f3b2SDimitry Andric // a void type. 105*1db9f3b2SDimitry Andric auto *VTy = dyn_cast<VectorType>(I.getType()); 106*1db9f3b2SDimitry Andric ElementCount EC(VTy ? VTy->getElementCount() : ElementCount::getFixed(0)); 107cb14a3feSDimitry Andric 108*1db9f3b2SDimitry Andric // Compute the argument types of the corresponding scalar call and the scalar 109*1db9f3b2SDimitry Andric // function name. For calls, it additionally finds the function to replace 110*1db9f3b2SDimitry Andric // and checks that all vector operands match the previously found EC. 111*1db9f3b2SDimitry Andric SmallVector<Type *, 8> ScalarArgTypes; 112*1db9f3b2SDimitry Andric std::string ScalarName; 113*1db9f3b2SDimitry Andric Function *FuncToReplace = nullptr; 114*1db9f3b2SDimitry Andric if (auto *CI = dyn_cast<CallInst>(&I)) { 115*1db9f3b2SDimitry Andric FuncToReplace = CI->getCalledFunction(); 116*1db9f3b2SDimitry Andric Intrinsic::ID IID = FuncToReplace->getIntrinsicID(); 117*1db9f3b2SDimitry Andric assert(IID != Intrinsic::not_intrinsic && "Not an intrinsic"); 118*1db9f3b2SDimitry Andric for (auto Arg : enumerate(CI->args())) { 119cb14a3feSDimitry Andric auto *ArgTy = Arg.value()->getType(); 120*1db9f3b2SDimitry Andric if (isVectorIntrinsicWithScalarOpAtArg(IID, Arg.index())) { 121cb14a3feSDimitry Andric ScalarArgTypes.push_back(ArgTy); 122cb14a3feSDimitry Andric } else if (auto *VectorArgTy = dyn_cast<VectorType>(ArgTy)) { 123*1db9f3b2SDimitry Andric ScalarArgTypes.push_back(VectorArgTy->getElementType()); 124*1db9f3b2SDimitry Andric // When return type is void, set EC to the first vector argument, and 125*1db9f3b2SDimitry Andric // disallow vector arguments with different ECs. 126*1db9f3b2SDimitry Andric if (EC.isZero()) 127*1db9f3b2SDimitry Andric EC = VectorArgTy->getElementCount(); 128*1db9f3b2SDimitry Andric else if (EC != VectorArgTy->getElementCount()) 129cb14a3feSDimitry Andric return false; 130cb14a3feSDimitry Andric } else 131cb14a3feSDimitry Andric // Exit when it is supposed to be a vector argument but it isn't. 132cb14a3feSDimitry Andric return false; 133cb14a3feSDimitry Andric } 134*1db9f3b2SDimitry Andric // Try to reconstruct the name for the scalar version of the instruction, 135*1db9f3b2SDimitry Andric // using scalar argument types. 136*1db9f3b2SDimitry Andric ScalarName = Intrinsic::isOverloaded(IID) 137*1db9f3b2SDimitry Andric ? Intrinsic::getName(IID, ScalarArgTypes, I.getModule()) 138*1db9f3b2SDimitry Andric : Intrinsic::getName(IID).str(); 139*1db9f3b2SDimitry Andric } else { 140*1db9f3b2SDimitry Andric assert(VTy && "Return type must be a vector"); 141*1db9f3b2SDimitry Andric auto *ScalarTy = VTy->getScalarType(); 142*1db9f3b2SDimitry Andric LibFunc Func; 143*1db9f3b2SDimitry Andric if (!TLI.getLibFunc(I.getOpcode(), ScalarTy, Func)) 144*1db9f3b2SDimitry Andric return false; 145*1db9f3b2SDimitry Andric ScalarName = TLI.getName(Func); 146*1db9f3b2SDimitry Andric ScalarArgTypes = {ScalarTy, ScalarTy}; 147*1db9f3b2SDimitry Andric } 148cb14a3feSDimitry Andric 149cb14a3feSDimitry Andric // Try to find the mapping for the scalar version of this intrinsic and the 150cb14a3feSDimitry Andric // exact vector width of the call operands in the TargetLibraryInfo. First, 151cb14a3feSDimitry Andric // check with a non-masked variant, and if that fails try with a masked one. 152cb14a3feSDimitry Andric const VecDesc *VD = 153*1db9f3b2SDimitry Andric TLI.getVectorMappingInfo(ScalarName, EC, /*Masked*/ false); 154*1db9f3b2SDimitry Andric if (!VD && !(VD = TLI.getVectorMappingInfo(ScalarName, EC, /*Masked*/ true))) 155cb14a3feSDimitry Andric return false; 156cb14a3feSDimitry Andric 157cb14a3feSDimitry Andric LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Found TLI mapping from: `" << ScalarName 158*1db9f3b2SDimitry Andric << "` and vector width " << EC << " to: `" 159cb14a3feSDimitry Andric << VD->getVectorFnName() << "`.\n"); 160cb14a3feSDimitry Andric 161cb14a3feSDimitry Andric // Replace the call to the intrinsic with a call to the vector library 162cb14a3feSDimitry Andric // function. 163*1db9f3b2SDimitry Andric Type *ScalarRetTy = I.getType()->getScalarType(); 164cb14a3feSDimitry Andric FunctionType *ScalarFTy = 165cb14a3feSDimitry Andric FunctionType::get(ScalarRetTy, ScalarArgTypes, /*isVarArg*/ false); 166cb14a3feSDimitry Andric const std::string MangledName = VD->getVectorFunctionABIVariantString(); 167cb14a3feSDimitry Andric auto OptInfo = VFABI::tryDemangleForVFABI(MangledName, ScalarFTy); 168cb14a3feSDimitry Andric if (!OptInfo) 169cb14a3feSDimitry Andric return false; 170cb14a3feSDimitry Andric 171cb14a3feSDimitry Andric FunctionType *VectorFTy = VFABI::createFunctionType(*OptInfo, ScalarFTy); 172cb14a3feSDimitry Andric if (!VectorFTy) 173cb14a3feSDimitry Andric return false; 174cb14a3feSDimitry Andric 175*1db9f3b2SDimitry Andric Function *TLIFunc = getTLIFunction(I.getModule(), VectorFTy, 176cb14a3feSDimitry Andric VD->getVectorFnName(), FuncToReplace); 177*1db9f3b2SDimitry Andric replaceWithTLIFunction(I, *OptInfo, TLIFunc); 178*1db9f3b2SDimitry Andric LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Replaced call to `" << ScalarName 179*1db9f3b2SDimitry Andric << "` with call to `" << TLIFunc->getName() << "`.\n"); 180fe6060f1SDimitry Andric ++NumCallsReplaced; 181fe6060f1SDimitry Andric return true; 182fe6060f1SDimitry Andric } 183fe6060f1SDimitry Andric 184*1db9f3b2SDimitry Andric /// Supported instruction \p I must be a vectorized frem or a call to an 185*1db9f3b2SDimitry Andric /// intrinsic that returns either void or a vector. 186*1db9f3b2SDimitry Andric static bool isSupportedInstruction(Instruction *I) { 187*1db9f3b2SDimitry Andric Type *Ty = I->getType(); 188*1db9f3b2SDimitry Andric if (auto *CI = dyn_cast<CallInst>(I)) 189*1db9f3b2SDimitry Andric return (Ty->isVectorTy() || Ty->isVoidTy()) && CI->getCalledFunction() && 190*1db9f3b2SDimitry Andric CI->getCalledFunction()->getIntrinsicID() != 191*1db9f3b2SDimitry Andric Intrinsic::not_intrinsic; 192*1db9f3b2SDimitry Andric if (I->getOpcode() == Instruction::FRem && Ty->isVectorTy()) 193*1db9f3b2SDimitry Andric return true; 194*1db9f3b2SDimitry Andric return false; 195*1db9f3b2SDimitry Andric } 196*1db9f3b2SDimitry Andric 197fe6060f1SDimitry Andric static bool runImpl(const TargetLibraryInfo &TLI, Function &F) { 198fe6060f1SDimitry Andric bool Changed = false; 199*1db9f3b2SDimitry Andric SmallVector<Instruction *> ReplacedCalls; 200fe6060f1SDimitry Andric for (auto &I : instructions(F)) { 201*1db9f3b2SDimitry Andric if (!isSupportedInstruction(&I)) 202*1db9f3b2SDimitry Andric continue; 203*1db9f3b2SDimitry Andric if (replaceWithCallToVeclib(TLI, I)) { 204*1db9f3b2SDimitry Andric ReplacedCalls.push_back(&I); 205fe6060f1SDimitry Andric Changed = true; 206fe6060f1SDimitry Andric } 207fe6060f1SDimitry Andric } 208fe6060f1SDimitry Andric // Erase the calls to the intrinsics that have been replaced 209fe6060f1SDimitry Andric // with calls to the vector library. 210cb14a3feSDimitry Andric for (auto *CI : ReplacedCalls) 211fe6060f1SDimitry Andric CI->eraseFromParent(); 212fe6060f1SDimitry Andric return Changed; 213fe6060f1SDimitry Andric } 214fe6060f1SDimitry Andric 215fe6060f1SDimitry Andric //////////////////////////////////////////////////////////////////////////////// 216fe6060f1SDimitry Andric // New pass manager implementation. 217fe6060f1SDimitry Andric //////////////////////////////////////////////////////////////////////////////// 218fe6060f1SDimitry Andric PreservedAnalyses ReplaceWithVeclib::run(Function &F, 219fe6060f1SDimitry Andric FunctionAnalysisManager &AM) { 220fe6060f1SDimitry Andric const TargetLibraryInfo &TLI = AM.getResult<TargetLibraryAnalysis>(F); 221fe6060f1SDimitry Andric auto Changed = runImpl(TLI, F); 222fe6060f1SDimitry Andric if (Changed) { 223fe6060f1SDimitry Andric PreservedAnalyses PA; 224fe6060f1SDimitry Andric PA.preserveSet<CFGAnalyses>(); 225fe6060f1SDimitry Andric PA.preserve<TargetLibraryAnalysis>(); 226fe6060f1SDimitry Andric PA.preserve<ScalarEvolutionAnalysis>(); 227fe6060f1SDimitry Andric PA.preserve<LoopAccessAnalysis>(); 228fe6060f1SDimitry Andric PA.preserve<DemandedBitsAnalysis>(); 229fe6060f1SDimitry Andric PA.preserve<OptimizationRemarkEmitterAnalysis>(); 230fe6060f1SDimitry Andric return PA; 231cb14a3feSDimitry Andric } 232cb14a3feSDimitry Andric 233fe6060f1SDimitry Andric // The pass did not replace any calls, hence it preserves all analyses. 234fe6060f1SDimitry Andric return PreservedAnalyses::all(); 235fe6060f1SDimitry Andric } 236fe6060f1SDimitry Andric 237fe6060f1SDimitry Andric //////////////////////////////////////////////////////////////////////////////// 238fe6060f1SDimitry Andric // Legacy PM Implementation. 239fe6060f1SDimitry Andric //////////////////////////////////////////////////////////////////////////////// 240fe6060f1SDimitry Andric bool ReplaceWithVeclibLegacy::runOnFunction(Function &F) { 241fe6060f1SDimitry Andric const TargetLibraryInfo &TLI = 242fe6060f1SDimitry Andric getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F); 243fe6060f1SDimitry Andric return runImpl(TLI, F); 244fe6060f1SDimitry Andric } 245fe6060f1SDimitry Andric 246fe6060f1SDimitry Andric void ReplaceWithVeclibLegacy::getAnalysisUsage(AnalysisUsage &AU) const { 247fe6060f1SDimitry Andric AU.setPreservesCFG(); 248fe6060f1SDimitry Andric AU.addRequired<TargetLibraryInfoWrapperPass>(); 249fe6060f1SDimitry Andric AU.addPreserved<TargetLibraryInfoWrapperPass>(); 250fe6060f1SDimitry Andric AU.addPreserved<ScalarEvolutionWrapperPass>(); 251fe6060f1SDimitry Andric AU.addPreserved<AAResultsWrapperPass>(); 252fe6060f1SDimitry Andric AU.addPreserved<OptimizationRemarkEmitterWrapperPass>(); 253fe6060f1SDimitry Andric AU.addPreserved<GlobalsAAWrapperPass>(); 254fe6060f1SDimitry Andric } 255fe6060f1SDimitry Andric 256fe6060f1SDimitry Andric //////////////////////////////////////////////////////////////////////////////// 257fe6060f1SDimitry Andric // Legacy Pass manager initialization 258fe6060f1SDimitry Andric //////////////////////////////////////////////////////////////////////////////// 259fe6060f1SDimitry Andric char ReplaceWithVeclibLegacy::ID = 0; 260fe6060f1SDimitry Andric 261fe6060f1SDimitry Andric INITIALIZE_PASS_BEGIN(ReplaceWithVeclibLegacy, DEBUG_TYPE, 262fe6060f1SDimitry Andric "Replace intrinsics with calls to vector library", false, 263fe6060f1SDimitry Andric false) 264fe6060f1SDimitry Andric INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) 265fe6060f1SDimitry Andric INITIALIZE_PASS_END(ReplaceWithVeclibLegacy, DEBUG_TYPE, 266fe6060f1SDimitry Andric "Replace intrinsics with calls to vector library", false, 267fe6060f1SDimitry Andric false) 268fe6060f1SDimitry Andric 269fe6060f1SDimitry Andric FunctionPass *llvm::createReplaceWithVeclibLegacyPass() { 270fe6060f1SDimitry Andric return new ReplaceWithVeclibLegacy(); 271fe6060f1SDimitry Andric } 272