xref: /freebsd/contrib/llvm-project/llvm/lib/CodeGen/ReplaceWithVeclib.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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.
getTLIFunction(Module * M,FunctionType * VectorFTy,const StringRef TLIName,Function * ScalarFunc=nullptr)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.
replaceWithTLIFunction(IntrinsicInst * II,VFInfo & Info,Function * TLIVecFunc)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.
replaceWithCallToVeclib(const TargetLibraryInfo & TLI,IntrinsicInst * II)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 
runImpl(const TargetLibraryInfo & TLI,Function & F)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 ////////////////////////////////////////////////////////////////////////////////
run(Function & F,FunctionAnalysisManager & AM)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 ////////////////////////////////////////////////////////////////////////////////
runOnFunction(Function & F)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 
getAnalysisUsage(AnalysisUsage & AU) const243fe6060f1SDimitry 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)
INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)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