xref: /freebsd-src/contrib/llvm-project/llvm/lib/CodeGen/ReplaceWithVeclib.cpp (revision 1db9f3b21e39176dd5b67cf8ac378633b172463e)
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