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