xref: /freebsd-src/contrib/llvm-project/llvm/lib/CodeGen/ReplaceWithVeclib.cpp (revision fe6060f10f634930ff71b7c50291ddc610da2475)
1*fe6060f1SDimitry Andric //=== ReplaceWithVeclib.cpp - Replace vector instrinsics with veclib calls ===//
2*fe6060f1SDimitry Andric //
3*fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*fe6060f1SDimitry Andric //
7*fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
8*fe6060f1SDimitry Andric //
9*fe6060f1SDimitry Andric // Replaces calls to LLVM vector intrinsics (i.e., calls to LLVM intrinsics
10*fe6060f1SDimitry Andric // with vector operands) with matching calls to functions from a vector
11*fe6060f1SDimitry Andric // library (e.g., libmvec, SVML) according to TargetLibraryInfo.
12*fe6060f1SDimitry Andric //
13*fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
14*fe6060f1SDimitry Andric 
15*fe6060f1SDimitry Andric #include "llvm/CodeGen/ReplaceWithVeclib.h"
16*fe6060f1SDimitry Andric #include "llvm/ADT/STLExtras.h"
17*fe6060f1SDimitry Andric #include "llvm/ADT/Statistic.h"
18*fe6060f1SDimitry Andric #include "llvm/Analysis/DemandedBits.h"
19*fe6060f1SDimitry Andric #include "llvm/Analysis/GlobalsModRef.h"
20*fe6060f1SDimitry Andric #include "llvm/Analysis/OptimizationRemarkEmitter.h"
21*fe6060f1SDimitry Andric #include "llvm/Analysis/TargetLibraryInfo.h"
22*fe6060f1SDimitry Andric #include "llvm/Analysis/VectorUtils.h"
23*fe6060f1SDimitry Andric #include "llvm/CodeGen/Passes.h"
24*fe6060f1SDimitry Andric #include "llvm/IR/IRBuilder.h"
25*fe6060f1SDimitry Andric #include "llvm/IR/InstIterator.h"
26*fe6060f1SDimitry Andric #include "llvm/IR/IntrinsicInst.h"
27*fe6060f1SDimitry Andric #include "llvm/Transforms/Utils/ModuleUtils.h"
28*fe6060f1SDimitry Andric 
29*fe6060f1SDimitry Andric using namespace llvm;
30*fe6060f1SDimitry Andric 
31*fe6060f1SDimitry Andric #define DEBUG_TYPE "replace-with-veclib"
32*fe6060f1SDimitry Andric 
33*fe6060f1SDimitry Andric STATISTIC(NumCallsReplaced,
34*fe6060f1SDimitry Andric           "Number of calls to intrinsics that have been replaced.");
35*fe6060f1SDimitry Andric 
36*fe6060f1SDimitry Andric STATISTIC(NumTLIFuncDeclAdded,
37*fe6060f1SDimitry Andric           "Number of vector library function declarations added.");
38*fe6060f1SDimitry Andric 
39*fe6060f1SDimitry Andric STATISTIC(NumFuncUsedAdded,
40*fe6060f1SDimitry Andric           "Number of functions added to `llvm.compiler.used`");
41*fe6060f1SDimitry Andric 
42*fe6060f1SDimitry Andric static bool replaceWithTLIFunction(CallInst &CI, const StringRef TLIName) {
43*fe6060f1SDimitry Andric   Module *M = CI.getModule();
44*fe6060f1SDimitry Andric 
45*fe6060f1SDimitry Andric   Function *OldFunc = CI.getCalledFunction();
46*fe6060f1SDimitry Andric 
47*fe6060f1SDimitry Andric   // Check if the vector library function is already declared in this module,
48*fe6060f1SDimitry Andric   // otherwise insert it.
49*fe6060f1SDimitry Andric   Function *TLIFunc = M->getFunction(TLIName);
50*fe6060f1SDimitry Andric   if (!TLIFunc) {
51*fe6060f1SDimitry Andric     TLIFunc = Function::Create(OldFunc->getFunctionType(),
52*fe6060f1SDimitry Andric                                Function::ExternalLinkage, TLIName, *M);
53*fe6060f1SDimitry Andric     TLIFunc->copyAttributesFrom(OldFunc);
54*fe6060f1SDimitry Andric 
55*fe6060f1SDimitry Andric     LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Added vector library function `"
56*fe6060f1SDimitry Andric                       << TLIName << "` of type `" << *(TLIFunc->getType())
57*fe6060f1SDimitry Andric                       << "` to module.\n");
58*fe6060f1SDimitry Andric 
59*fe6060f1SDimitry Andric     ++NumTLIFuncDeclAdded;
60*fe6060f1SDimitry Andric 
61*fe6060f1SDimitry Andric     // Add the freshly created function to llvm.compiler.used,
62*fe6060f1SDimitry Andric     // similar to as it is done in InjectTLIMappings
63*fe6060f1SDimitry Andric     appendToCompilerUsed(*M, {TLIFunc});
64*fe6060f1SDimitry Andric 
65*fe6060f1SDimitry Andric     LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Adding `" << TLIName
66*fe6060f1SDimitry Andric                       << "` to `@llvm.compiler.used`.\n");
67*fe6060f1SDimitry Andric     ++NumFuncUsedAdded;
68*fe6060f1SDimitry Andric   }
69*fe6060f1SDimitry Andric 
70*fe6060f1SDimitry Andric   // Replace the call to the vector intrinsic with a call
71*fe6060f1SDimitry Andric   // to the corresponding function from the vector library.
72*fe6060f1SDimitry Andric   IRBuilder<> IRBuilder(&CI);
73*fe6060f1SDimitry Andric   SmallVector<Value *> Args(CI.arg_operands());
74*fe6060f1SDimitry Andric   // Preserve the operand bundles.
75*fe6060f1SDimitry Andric   SmallVector<OperandBundleDef, 1> OpBundles;
76*fe6060f1SDimitry Andric   CI.getOperandBundlesAsDefs(OpBundles);
77*fe6060f1SDimitry Andric   CallInst *Replacement = IRBuilder.CreateCall(TLIFunc, Args, OpBundles);
78*fe6060f1SDimitry Andric   assert(OldFunc->getFunctionType() == TLIFunc->getFunctionType() &&
79*fe6060f1SDimitry Andric          "Expecting function types to be identical");
80*fe6060f1SDimitry Andric   CI.replaceAllUsesWith(Replacement);
81*fe6060f1SDimitry Andric   if (isa<FPMathOperator>(Replacement)) {
82*fe6060f1SDimitry Andric     // Preserve fast math flags for FP math.
83*fe6060f1SDimitry Andric     Replacement->copyFastMathFlags(&CI);
84*fe6060f1SDimitry Andric   }
85*fe6060f1SDimitry Andric 
86*fe6060f1SDimitry Andric   LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Replaced call to `"
87*fe6060f1SDimitry Andric                     << OldFunc->getName() << "` with call to `" << TLIName
88*fe6060f1SDimitry Andric                     << "`.\n");
89*fe6060f1SDimitry Andric   ++NumCallsReplaced;
90*fe6060f1SDimitry Andric   return true;
91*fe6060f1SDimitry Andric }
92*fe6060f1SDimitry Andric 
93*fe6060f1SDimitry Andric static bool replaceWithCallToVeclib(const TargetLibraryInfo &TLI,
94*fe6060f1SDimitry Andric                                     CallInst &CI) {
95*fe6060f1SDimitry Andric   if (!CI.getCalledFunction()) {
96*fe6060f1SDimitry Andric     return false;
97*fe6060f1SDimitry Andric   }
98*fe6060f1SDimitry Andric 
99*fe6060f1SDimitry Andric   auto IntrinsicID = CI.getCalledFunction()->getIntrinsicID();
100*fe6060f1SDimitry Andric   if (IntrinsicID == Intrinsic::not_intrinsic) {
101*fe6060f1SDimitry Andric     // Replacement is only performed for intrinsic functions
102*fe6060f1SDimitry Andric     return false;
103*fe6060f1SDimitry Andric   }
104*fe6060f1SDimitry Andric 
105*fe6060f1SDimitry Andric   // Convert vector arguments to scalar type and check that
106*fe6060f1SDimitry Andric   // all vector operands have identical vector width.
107*fe6060f1SDimitry Andric   ElementCount VF = ElementCount::getFixed(0);
108*fe6060f1SDimitry Andric   SmallVector<Type *> ScalarTypes;
109*fe6060f1SDimitry Andric   for (auto Arg : enumerate(CI.arg_operands())) {
110*fe6060f1SDimitry Andric     auto *ArgType = Arg.value()->getType();
111*fe6060f1SDimitry Andric     // Vector calls to intrinsics can still have
112*fe6060f1SDimitry Andric     // scalar operands for specific arguments.
113*fe6060f1SDimitry Andric     if (hasVectorInstrinsicScalarOpd(IntrinsicID, Arg.index())) {
114*fe6060f1SDimitry Andric       ScalarTypes.push_back(ArgType);
115*fe6060f1SDimitry Andric     } else {
116*fe6060f1SDimitry Andric       // The argument in this place should be a vector if
117*fe6060f1SDimitry Andric       // this is a call to a vector intrinsic.
118*fe6060f1SDimitry Andric       auto *VectorArgTy = dyn_cast<VectorType>(ArgType);
119*fe6060f1SDimitry Andric       if (!VectorArgTy) {
120*fe6060f1SDimitry Andric         // The argument is not a vector, do not perform
121*fe6060f1SDimitry Andric         // the replacement.
122*fe6060f1SDimitry Andric         return false;
123*fe6060f1SDimitry Andric       }
124*fe6060f1SDimitry Andric       ElementCount NumElements = VectorArgTy->getElementCount();
125*fe6060f1SDimitry Andric       if (NumElements.isScalable()) {
126*fe6060f1SDimitry Andric         // The current implementation does not support
127*fe6060f1SDimitry Andric         // scalable vectors.
128*fe6060f1SDimitry Andric         return false;
129*fe6060f1SDimitry Andric       }
130*fe6060f1SDimitry Andric       if (VF.isNonZero() && VF != NumElements) {
131*fe6060f1SDimitry Andric         // The different arguments differ in vector size.
132*fe6060f1SDimitry Andric         return false;
133*fe6060f1SDimitry Andric       } else {
134*fe6060f1SDimitry Andric         VF = NumElements;
135*fe6060f1SDimitry Andric       }
136*fe6060f1SDimitry Andric       ScalarTypes.push_back(VectorArgTy->getElementType());
137*fe6060f1SDimitry Andric     }
138*fe6060f1SDimitry Andric   }
139*fe6060f1SDimitry Andric 
140*fe6060f1SDimitry Andric   // Try to reconstruct the name for the scalar version of this
141*fe6060f1SDimitry Andric   // intrinsic using the intrinsic ID and the argument types
142*fe6060f1SDimitry Andric   // converted to scalar above.
143*fe6060f1SDimitry Andric   std::string ScalarName;
144*fe6060f1SDimitry Andric   if (Intrinsic::isOverloaded(IntrinsicID)) {
145*fe6060f1SDimitry Andric     ScalarName = Intrinsic::getName(IntrinsicID, ScalarTypes, CI.getModule());
146*fe6060f1SDimitry Andric   } else {
147*fe6060f1SDimitry Andric     ScalarName = Intrinsic::getName(IntrinsicID).str();
148*fe6060f1SDimitry Andric   }
149*fe6060f1SDimitry Andric 
150*fe6060f1SDimitry Andric   if (!TLI.isFunctionVectorizable(ScalarName)) {
151*fe6060f1SDimitry Andric     // The TargetLibraryInfo does not contain a vectorized version of
152*fe6060f1SDimitry Andric     // the scalar function.
153*fe6060f1SDimitry Andric     return false;
154*fe6060f1SDimitry Andric   }
155*fe6060f1SDimitry Andric 
156*fe6060f1SDimitry Andric   // Try to find the mapping for the scalar version of this intrinsic
157*fe6060f1SDimitry Andric   // and the exact vector width of the call operands in the
158*fe6060f1SDimitry Andric   // TargetLibraryInfo.
159*fe6060f1SDimitry Andric   const std::string TLIName =
160*fe6060f1SDimitry Andric       std::string(TLI.getVectorizedFunction(ScalarName, VF));
161*fe6060f1SDimitry Andric 
162*fe6060f1SDimitry Andric   LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Looking up TLI mapping for `"
163*fe6060f1SDimitry Andric                     << ScalarName << "` and vector width " << VF << ".\n");
164*fe6060f1SDimitry Andric 
165*fe6060f1SDimitry Andric   if (!TLIName.empty()) {
166*fe6060f1SDimitry Andric     // Found the correct mapping in the TargetLibraryInfo,
167*fe6060f1SDimitry Andric     // replace the call to the intrinsic with a call to
168*fe6060f1SDimitry Andric     // the vector library function.
169*fe6060f1SDimitry Andric     LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Found TLI function `" << TLIName
170*fe6060f1SDimitry Andric                       << "`.\n");
171*fe6060f1SDimitry Andric     return replaceWithTLIFunction(CI, TLIName);
172*fe6060f1SDimitry Andric   }
173*fe6060f1SDimitry Andric 
174*fe6060f1SDimitry Andric   return false;
175*fe6060f1SDimitry Andric }
176*fe6060f1SDimitry Andric 
177*fe6060f1SDimitry Andric static bool runImpl(const TargetLibraryInfo &TLI, Function &F) {
178*fe6060f1SDimitry Andric   bool Changed = false;
179*fe6060f1SDimitry Andric   SmallVector<CallInst *> ReplacedCalls;
180*fe6060f1SDimitry Andric   for (auto &I : instructions(F)) {
181*fe6060f1SDimitry Andric     if (auto *CI = dyn_cast<CallInst>(&I)) {
182*fe6060f1SDimitry Andric       if (replaceWithCallToVeclib(TLI, *CI)) {
183*fe6060f1SDimitry Andric         ReplacedCalls.push_back(CI);
184*fe6060f1SDimitry Andric         Changed = true;
185*fe6060f1SDimitry Andric       }
186*fe6060f1SDimitry Andric     }
187*fe6060f1SDimitry Andric   }
188*fe6060f1SDimitry Andric   // Erase the calls to the intrinsics that have been replaced
189*fe6060f1SDimitry Andric   // with calls to the vector library.
190*fe6060f1SDimitry Andric   for (auto *CI : ReplacedCalls) {
191*fe6060f1SDimitry Andric     CI->eraseFromParent();
192*fe6060f1SDimitry Andric   }
193*fe6060f1SDimitry Andric   return Changed;
194*fe6060f1SDimitry Andric }
195*fe6060f1SDimitry Andric 
196*fe6060f1SDimitry Andric ////////////////////////////////////////////////////////////////////////////////
197*fe6060f1SDimitry Andric // New pass manager implementation.
198*fe6060f1SDimitry Andric ////////////////////////////////////////////////////////////////////////////////
199*fe6060f1SDimitry Andric PreservedAnalyses ReplaceWithVeclib::run(Function &F,
200*fe6060f1SDimitry Andric                                          FunctionAnalysisManager &AM) {
201*fe6060f1SDimitry Andric   const TargetLibraryInfo &TLI = AM.getResult<TargetLibraryAnalysis>(F);
202*fe6060f1SDimitry Andric   auto Changed = runImpl(TLI, F);
203*fe6060f1SDimitry Andric   if (Changed) {
204*fe6060f1SDimitry Andric     PreservedAnalyses PA;
205*fe6060f1SDimitry Andric     PA.preserveSet<CFGAnalyses>();
206*fe6060f1SDimitry Andric     PA.preserve<TargetLibraryAnalysis>();
207*fe6060f1SDimitry Andric     PA.preserve<ScalarEvolutionAnalysis>();
208*fe6060f1SDimitry Andric     PA.preserve<LoopAccessAnalysis>();
209*fe6060f1SDimitry Andric     PA.preserve<DemandedBitsAnalysis>();
210*fe6060f1SDimitry Andric     PA.preserve<OptimizationRemarkEmitterAnalysis>();
211*fe6060f1SDimitry Andric     return PA;
212*fe6060f1SDimitry Andric   } else {
213*fe6060f1SDimitry Andric     // The pass did not replace any calls, hence it preserves all analyses.
214*fe6060f1SDimitry Andric     return PreservedAnalyses::all();
215*fe6060f1SDimitry Andric   }
216*fe6060f1SDimitry Andric }
217*fe6060f1SDimitry Andric 
218*fe6060f1SDimitry Andric ////////////////////////////////////////////////////////////////////////////////
219*fe6060f1SDimitry Andric // Legacy PM Implementation.
220*fe6060f1SDimitry Andric ////////////////////////////////////////////////////////////////////////////////
221*fe6060f1SDimitry Andric bool ReplaceWithVeclibLegacy::runOnFunction(Function &F) {
222*fe6060f1SDimitry Andric   const TargetLibraryInfo &TLI =
223*fe6060f1SDimitry Andric       getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F);
224*fe6060f1SDimitry Andric   return runImpl(TLI, F);
225*fe6060f1SDimitry Andric }
226*fe6060f1SDimitry Andric 
227*fe6060f1SDimitry Andric void ReplaceWithVeclibLegacy::getAnalysisUsage(AnalysisUsage &AU) const {
228*fe6060f1SDimitry Andric   AU.setPreservesCFG();
229*fe6060f1SDimitry Andric   AU.addRequired<TargetLibraryInfoWrapperPass>();
230*fe6060f1SDimitry Andric   AU.addPreserved<TargetLibraryInfoWrapperPass>();
231*fe6060f1SDimitry Andric   AU.addPreserved<ScalarEvolutionWrapperPass>();
232*fe6060f1SDimitry Andric   AU.addPreserved<AAResultsWrapperPass>();
233*fe6060f1SDimitry Andric   AU.addPreserved<LoopAccessLegacyAnalysis>();
234*fe6060f1SDimitry Andric   AU.addPreserved<DemandedBitsWrapperPass>();
235*fe6060f1SDimitry Andric   AU.addPreserved<OptimizationRemarkEmitterWrapperPass>();
236*fe6060f1SDimitry Andric   AU.addPreserved<GlobalsAAWrapperPass>();
237*fe6060f1SDimitry Andric }
238*fe6060f1SDimitry Andric 
239*fe6060f1SDimitry Andric ////////////////////////////////////////////////////////////////////////////////
240*fe6060f1SDimitry Andric // Legacy Pass manager initialization
241*fe6060f1SDimitry Andric ////////////////////////////////////////////////////////////////////////////////
242*fe6060f1SDimitry Andric char ReplaceWithVeclibLegacy::ID = 0;
243*fe6060f1SDimitry Andric 
244*fe6060f1SDimitry Andric INITIALIZE_PASS_BEGIN(ReplaceWithVeclibLegacy, DEBUG_TYPE,
245*fe6060f1SDimitry Andric                       "Replace intrinsics with calls to vector library", false,
246*fe6060f1SDimitry Andric                       false)
247*fe6060f1SDimitry Andric INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
248*fe6060f1SDimitry Andric INITIALIZE_PASS_END(ReplaceWithVeclibLegacy, DEBUG_TYPE,
249*fe6060f1SDimitry Andric                     "Replace intrinsics with calls to vector library", false,
250*fe6060f1SDimitry Andric                     false)
251*fe6060f1SDimitry Andric 
252*fe6060f1SDimitry Andric FunctionPass *llvm::createReplaceWithVeclibLegacyPass() {
253*fe6060f1SDimitry Andric   return new ReplaceWithVeclibLegacy();
254*fe6060f1SDimitry Andric }
255