1*b8e1544bSIlia Diachkov //===-- SPIRVPrepareFunctions.cpp - modify function signatures --*- C++ -*-===//
2*b8e1544bSIlia Diachkov //
3*b8e1544bSIlia Diachkov // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*b8e1544bSIlia Diachkov // See https://llvm.org/LICENSE.txt for license information.
5*b8e1544bSIlia Diachkov // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*b8e1544bSIlia Diachkov //
7*b8e1544bSIlia Diachkov //===----------------------------------------------------------------------===//
8*b8e1544bSIlia Diachkov //
9*b8e1544bSIlia Diachkov // This pass modifies function signatures containing aggregate arguments
10*b8e1544bSIlia Diachkov // and/or return value. Also it substitutes some llvm intrinsic calls by
11*b8e1544bSIlia Diachkov // function calls, generating these functions as the translator does.
12*b8e1544bSIlia Diachkov //
13*b8e1544bSIlia Diachkov // NOTE: this pass is a module-level one due to the necessity to modify
14*b8e1544bSIlia Diachkov // GVs/functions.
15*b8e1544bSIlia Diachkov //
16*b8e1544bSIlia Diachkov //===----------------------------------------------------------------------===//
17*b8e1544bSIlia Diachkov 
18*b8e1544bSIlia Diachkov #include "SPIRV.h"
19*b8e1544bSIlia Diachkov #include "SPIRVTargetMachine.h"
20*b8e1544bSIlia Diachkov #include "SPIRVUtils.h"
21*b8e1544bSIlia Diachkov #include "llvm/IR/IRBuilder.h"
22*b8e1544bSIlia Diachkov #include "llvm/IR/IntrinsicInst.h"
23*b8e1544bSIlia Diachkov #include "llvm/Transforms/Utils/Cloning.h"
24*b8e1544bSIlia Diachkov #include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
25*b8e1544bSIlia Diachkov 
26*b8e1544bSIlia Diachkov using namespace llvm;
27*b8e1544bSIlia Diachkov 
28*b8e1544bSIlia Diachkov namespace llvm {
29*b8e1544bSIlia Diachkov void initializeSPIRVPrepareFunctionsPass(PassRegistry &);
30*b8e1544bSIlia Diachkov }
31*b8e1544bSIlia Diachkov 
32*b8e1544bSIlia Diachkov namespace {
33*b8e1544bSIlia Diachkov 
34*b8e1544bSIlia Diachkov class SPIRVPrepareFunctions : public ModulePass {
35*b8e1544bSIlia Diachkov   Function *processFunctionSignature(Function *F);
36*b8e1544bSIlia Diachkov 
37*b8e1544bSIlia Diachkov public:
38*b8e1544bSIlia Diachkov   static char ID;
SPIRVPrepareFunctions()39*b8e1544bSIlia Diachkov   SPIRVPrepareFunctions() : ModulePass(ID) {
40*b8e1544bSIlia Diachkov     initializeSPIRVPrepareFunctionsPass(*PassRegistry::getPassRegistry());
41*b8e1544bSIlia Diachkov   }
42*b8e1544bSIlia Diachkov 
43*b8e1544bSIlia Diachkov   bool runOnModule(Module &M) override;
44*b8e1544bSIlia Diachkov 
getPassName() const45*b8e1544bSIlia Diachkov   StringRef getPassName() const override { return "SPIRV prepare functions"; }
46*b8e1544bSIlia Diachkov 
getAnalysisUsage(AnalysisUsage & AU) const47*b8e1544bSIlia Diachkov   void getAnalysisUsage(AnalysisUsage &AU) const override {
48*b8e1544bSIlia Diachkov     ModulePass::getAnalysisUsage(AU);
49*b8e1544bSIlia Diachkov   }
50*b8e1544bSIlia Diachkov };
51*b8e1544bSIlia Diachkov 
52*b8e1544bSIlia Diachkov } // namespace
53*b8e1544bSIlia Diachkov 
54*b8e1544bSIlia Diachkov char SPIRVPrepareFunctions::ID = 0;
55*b8e1544bSIlia Diachkov 
56*b8e1544bSIlia Diachkov INITIALIZE_PASS(SPIRVPrepareFunctions, "prepare-functions",
57*b8e1544bSIlia Diachkov                 "SPIRV prepare functions", false, false)
58*b8e1544bSIlia Diachkov 
processFunctionSignature(Function * F)59*b8e1544bSIlia Diachkov Function *SPIRVPrepareFunctions::processFunctionSignature(Function *F) {
60*b8e1544bSIlia Diachkov   IRBuilder<> B(F->getContext());
61*b8e1544bSIlia Diachkov 
62*b8e1544bSIlia Diachkov   bool IsRetAggr = F->getReturnType()->isAggregateType();
63*b8e1544bSIlia Diachkov   bool HasAggrArg =
64*b8e1544bSIlia Diachkov       std::any_of(F->arg_begin(), F->arg_end(), [](Argument &Arg) {
65*b8e1544bSIlia Diachkov         return Arg.getType()->isAggregateType();
66*b8e1544bSIlia Diachkov       });
67*b8e1544bSIlia Diachkov   bool DoClone = IsRetAggr || HasAggrArg;
68*b8e1544bSIlia Diachkov   if (!DoClone)
69*b8e1544bSIlia Diachkov     return F;
70*b8e1544bSIlia Diachkov   SmallVector<std::pair<int, Type *>, 4> ChangedTypes;
71*b8e1544bSIlia Diachkov   Type *RetType = IsRetAggr ? B.getInt32Ty() : F->getReturnType();
72*b8e1544bSIlia Diachkov   if (IsRetAggr)
73*b8e1544bSIlia Diachkov     ChangedTypes.push_back(std::pair<int, Type *>(-1, F->getReturnType()));
74*b8e1544bSIlia Diachkov   SmallVector<Type *, 4> ArgTypes;
75*b8e1544bSIlia Diachkov   for (const auto &Arg : F->args()) {
76*b8e1544bSIlia Diachkov     if (Arg.getType()->isAggregateType()) {
77*b8e1544bSIlia Diachkov       ArgTypes.push_back(B.getInt32Ty());
78*b8e1544bSIlia Diachkov       ChangedTypes.push_back(
79*b8e1544bSIlia Diachkov           std::pair<int, Type *>(Arg.getArgNo(), Arg.getType()));
80*b8e1544bSIlia Diachkov     } else
81*b8e1544bSIlia Diachkov       ArgTypes.push_back(Arg.getType());
82*b8e1544bSIlia Diachkov   }
83*b8e1544bSIlia Diachkov   FunctionType *NewFTy =
84*b8e1544bSIlia Diachkov       FunctionType::get(RetType, ArgTypes, F->getFunctionType()->isVarArg());
85*b8e1544bSIlia Diachkov   Function *NewF =
86*b8e1544bSIlia Diachkov       Function::Create(NewFTy, F->getLinkage(), F->getName(), *F->getParent());
87*b8e1544bSIlia Diachkov 
88*b8e1544bSIlia Diachkov   ValueToValueMapTy VMap;
89*b8e1544bSIlia Diachkov   auto NewFArgIt = NewF->arg_begin();
90*b8e1544bSIlia Diachkov   for (auto &Arg : F->args()) {
91*b8e1544bSIlia Diachkov     StringRef ArgName = Arg.getName();
92*b8e1544bSIlia Diachkov     NewFArgIt->setName(ArgName);
93*b8e1544bSIlia Diachkov     VMap[&Arg] = &(*NewFArgIt++);
94*b8e1544bSIlia Diachkov   }
95*b8e1544bSIlia Diachkov   SmallVector<ReturnInst *, 8> Returns;
96*b8e1544bSIlia Diachkov 
97*b8e1544bSIlia Diachkov   CloneFunctionInto(NewF, F, VMap, CloneFunctionChangeType::LocalChangesOnly,
98*b8e1544bSIlia Diachkov                     Returns);
99*b8e1544bSIlia Diachkov   NewF->takeName(F);
100*b8e1544bSIlia Diachkov 
101*b8e1544bSIlia Diachkov   NamedMDNode *FuncMD =
102*b8e1544bSIlia Diachkov       F->getParent()->getOrInsertNamedMetadata("spv.cloned_funcs");
103*b8e1544bSIlia Diachkov   SmallVector<Metadata *, 2> MDArgs;
104*b8e1544bSIlia Diachkov   MDArgs.push_back(MDString::get(B.getContext(), NewF->getName()));
105*b8e1544bSIlia Diachkov   for (auto &ChangedTyP : ChangedTypes)
106*b8e1544bSIlia Diachkov     MDArgs.push_back(MDNode::get(
107*b8e1544bSIlia Diachkov         B.getContext(),
108*b8e1544bSIlia Diachkov         {ConstantAsMetadata::get(B.getInt32(ChangedTyP.first)),
109*b8e1544bSIlia Diachkov          ValueAsMetadata::get(Constant::getNullValue(ChangedTyP.second))}));
110*b8e1544bSIlia Diachkov   MDNode *ThisFuncMD = MDNode::get(B.getContext(), MDArgs);
111*b8e1544bSIlia Diachkov   FuncMD->addOperand(ThisFuncMD);
112*b8e1544bSIlia Diachkov 
113*b8e1544bSIlia Diachkov   for (auto *U : make_early_inc_range(F->users())) {
114*b8e1544bSIlia Diachkov     if (auto *CI = dyn_cast<CallInst>(U))
115*b8e1544bSIlia Diachkov       CI->mutateFunctionType(NewF->getFunctionType());
116*b8e1544bSIlia Diachkov     U->replaceUsesOfWith(F, NewF);
117*b8e1544bSIlia Diachkov   }
118*b8e1544bSIlia Diachkov   return NewF;
119*b8e1544bSIlia Diachkov }
120*b8e1544bSIlia Diachkov 
lowerLLVMIntrinsicName(IntrinsicInst * II)121*b8e1544bSIlia Diachkov std::string lowerLLVMIntrinsicName(IntrinsicInst *II) {
122*b8e1544bSIlia Diachkov   Function *IntrinsicFunc = II->getCalledFunction();
123*b8e1544bSIlia Diachkov   assert(IntrinsicFunc && "Missing function");
124*b8e1544bSIlia Diachkov   std::string FuncName = IntrinsicFunc->getName().str();
125*b8e1544bSIlia Diachkov   std::replace(FuncName.begin(), FuncName.end(), '.', '_');
126*b8e1544bSIlia Diachkov   FuncName = "spirv." + FuncName;
127*b8e1544bSIlia Diachkov   return FuncName;
128*b8e1544bSIlia Diachkov }
129*b8e1544bSIlia Diachkov 
getOrCreateFunction(Module * M,Type * RetTy,ArrayRef<Type * > ArgTypes,StringRef Name)130*b8e1544bSIlia Diachkov static Function *getOrCreateFunction(Module *M, Type *RetTy,
131*b8e1544bSIlia Diachkov                                      ArrayRef<Type *> ArgTypes,
132*b8e1544bSIlia Diachkov                                      StringRef Name) {
133*b8e1544bSIlia Diachkov   FunctionType *FT = FunctionType::get(RetTy, ArgTypes, false);
134*b8e1544bSIlia Diachkov   Function *F = M->getFunction(Name);
135*b8e1544bSIlia Diachkov   if (F && F->getFunctionType() == FT)
136*b8e1544bSIlia Diachkov     return F;
137*b8e1544bSIlia Diachkov   Function *NewF = Function::Create(FT, GlobalValue::ExternalLinkage, Name, M);
138*b8e1544bSIlia Diachkov   if (F)
139*b8e1544bSIlia Diachkov     NewF->setDSOLocal(F->isDSOLocal());
140*b8e1544bSIlia Diachkov   NewF->setCallingConv(CallingConv::SPIR_FUNC);
141*b8e1544bSIlia Diachkov   return NewF;
142*b8e1544bSIlia Diachkov }
143*b8e1544bSIlia Diachkov 
lowerFunnelShifts(Module * M,IntrinsicInst * FSHIntrinsic)144*b8e1544bSIlia Diachkov static void lowerFunnelShifts(Module *M, IntrinsicInst *FSHIntrinsic) {
145*b8e1544bSIlia Diachkov   // Get a separate function - otherwise, we'd have to rework the CFG of the
146*b8e1544bSIlia Diachkov   // current one. Then simply replace the intrinsic uses with a call to the new
147*b8e1544bSIlia Diachkov   // function.
148*b8e1544bSIlia Diachkov   // Generate LLVM IR for  i* @spirv.llvm_fsh?_i* (i* %a, i* %b, i* %c)
149*b8e1544bSIlia Diachkov   FunctionType *FSHFuncTy = FSHIntrinsic->getFunctionType();
150*b8e1544bSIlia Diachkov   Type *FSHRetTy = FSHFuncTy->getReturnType();
151*b8e1544bSIlia Diachkov   const std::string FuncName = lowerLLVMIntrinsicName(FSHIntrinsic);
152*b8e1544bSIlia Diachkov   Function *FSHFunc =
153*b8e1544bSIlia Diachkov       getOrCreateFunction(M, FSHRetTy, FSHFuncTy->params(), FuncName);
154*b8e1544bSIlia Diachkov 
155*b8e1544bSIlia Diachkov   if (!FSHFunc->empty()) {
156*b8e1544bSIlia Diachkov     FSHIntrinsic->setCalledFunction(FSHFunc);
157*b8e1544bSIlia Diachkov     return;
158*b8e1544bSIlia Diachkov   }
159*b8e1544bSIlia Diachkov   BasicBlock *RotateBB = BasicBlock::Create(M->getContext(), "rotate", FSHFunc);
160*b8e1544bSIlia Diachkov   IRBuilder<> IRB(RotateBB);
161*b8e1544bSIlia Diachkov   Type *Ty = FSHFunc->getReturnType();
162*b8e1544bSIlia Diachkov   // Build the actual funnel shift rotate logic.
163*b8e1544bSIlia Diachkov   // In the comments, "int" is used interchangeably with "vector of int
164*b8e1544bSIlia Diachkov   // elements".
165*b8e1544bSIlia Diachkov   FixedVectorType *VectorTy = dyn_cast<FixedVectorType>(Ty);
166*b8e1544bSIlia Diachkov   Type *IntTy = VectorTy ? VectorTy->getElementType() : Ty;
167*b8e1544bSIlia Diachkov   unsigned BitWidth = IntTy->getIntegerBitWidth();
168*b8e1544bSIlia Diachkov   ConstantInt *BitWidthConstant = IRB.getInt({BitWidth, BitWidth});
169*b8e1544bSIlia Diachkov   Value *BitWidthForInsts =
170*b8e1544bSIlia Diachkov       VectorTy
171*b8e1544bSIlia Diachkov           ? IRB.CreateVectorSplat(VectorTy->getNumElements(), BitWidthConstant)
172*b8e1544bSIlia Diachkov           : BitWidthConstant;
173*b8e1544bSIlia Diachkov   Value *RotateModVal =
174*b8e1544bSIlia Diachkov       IRB.CreateURem(/*Rotate*/ FSHFunc->getArg(2), BitWidthForInsts);
175*b8e1544bSIlia Diachkov   Value *FirstShift = nullptr, *SecShift = nullptr;
176*b8e1544bSIlia Diachkov   if (FSHIntrinsic->getIntrinsicID() == Intrinsic::fshr) {
177*b8e1544bSIlia Diachkov     // Shift the less significant number right, the "rotate" number of bits
178*b8e1544bSIlia Diachkov     // will be 0-filled on the left as a result of this regular shift.
179*b8e1544bSIlia Diachkov     FirstShift = IRB.CreateLShr(FSHFunc->getArg(1), RotateModVal);
180*b8e1544bSIlia Diachkov   } else {
181*b8e1544bSIlia Diachkov     // Shift the more significant number left, the "rotate" number of bits
182*b8e1544bSIlia Diachkov     // will be 0-filled on the right as a result of this regular shift.
183*b8e1544bSIlia Diachkov     FirstShift = IRB.CreateShl(FSHFunc->getArg(0), RotateModVal);
184*b8e1544bSIlia Diachkov   }
185*b8e1544bSIlia Diachkov   // We want the "rotate" number of the more significant int's LSBs (MSBs) to
186*b8e1544bSIlia Diachkov   // occupy the leftmost (rightmost) "0 space" left by the previous operation.
187*b8e1544bSIlia Diachkov   // Therefore, subtract the "rotate" number from the integer bitsize...
188*b8e1544bSIlia Diachkov   Value *SubRotateVal = IRB.CreateSub(BitWidthForInsts, RotateModVal);
189*b8e1544bSIlia Diachkov   if (FSHIntrinsic->getIntrinsicID() == Intrinsic::fshr) {
190*b8e1544bSIlia Diachkov     // ...and left-shift the more significant int by this number, zero-filling
191*b8e1544bSIlia Diachkov     // the LSBs.
192*b8e1544bSIlia Diachkov     SecShift = IRB.CreateShl(FSHFunc->getArg(0), SubRotateVal);
193*b8e1544bSIlia Diachkov   } else {
194*b8e1544bSIlia Diachkov     // ...and right-shift the less significant int by this number, zero-filling
195*b8e1544bSIlia Diachkov     // the MSBs.
196*b8e1544bSIlia Diachkov     SecShift = IRB.CreateLShr(FSHFunc->getArg(1), SubRotateVal);
197*b8e1544bSIlia Diachkov   }
198*b8e1544bSIlia Diachkov   // A simple binary addition of the shifted ints yields the final result.
199*b8e1544bSIlia Diachkov   IRB.CreateRet(IRB.CreateOr(FirstShift, SecShift));
200*b8e1544bSIlia Diachkov 
201*b8e1544bSIlia Diachkov   FSHIntrinsic->setCalledFunction(FSHFunc);
202*b8e1544bSIlia Diachkov }
203*b8e1544bSIlia Diachkov 
buildUMulWithOverflowFunc(Module * M,Function * UMulFunc)204*b8e1544bSIlia Diachkov static void buildUMulWithOverflowFunc(Module *M, Function *UMulFunc) {
205*b8e1544bSIlia Diachkov   // The function body is already created.
206*b8e1544bSIlia Diachkov   if (!UMulFunc->empty())
207*b8e1544bSIlia Diachkov     return;
208*b8e1544bSIlia Diachkov 
209*b8e1544bSIlia Diachkov   BasicBlock *EntryBB = BasicBlock::Create(M->getContext(), "entry", UMulFunc);
210*b8e1544bSIlia Diachkov   IRBuilder<> IRB(EntryBB);
211*b8e1544bSIlia Diachkov   // Build the actual unsigned multiplication logic with the overflow
212*b8e1544bSIlia Diachkov   // indication. Do unsigned multiplication Mul = A * B. Then check
213*b8e1544bSIlia Diachkov   // if unsigned division Div = Mul / A is not equal to B. If so,
214*b8e1544bSIlia Diachkov   // then overflow has happened.
215*b8e1544bSIlia Diachkov   Value *Mul = IRB.CreateNUWMul(UMulFunc->getArg(0), UMulFunc->getArg(1));
216*b8e1544bSIlia Diachkov   Value *Div = IRB.CreateUDiv(Mul, UMulFunc->getArg(0));
217*b8e1544bSIlia Diachkov   Value *Overflow = IRB.CreateICmpNE(UMulFunc->getArg(0), Div);
218*b8e1544bSIlia Diachkov 
219*b8e1544bSIlia Diachkov   // umul.with.overflow intrinsic return a structure, where the first element
220*b8e1544bSIlia Diachkov   // is the multiplication result, and the second is an overflow bit.
221*b8e1544bSIlia Diachkov   Type *StructTy = UMulFunc->getReturnType();
222*b8e1544bSIlia Diachkov   Value *Agg = IRB.CreateInsertValue(UndefValue::get(StructTy), Mul, {0});
223*b8e1544bSIlia Diachkov   Value *Res = IRB.CreateInsertValue(Agg, Overflow, {1});
224*b8e1544bSIlia Diachkov   IRB.CreateRet(Res);
225*b8e1544bSIlia Diachkov }
226*b8e1544bSIlia Diachkov 
lowerUMulWithOverflow(Module * M,IntrinsicInst * UMulIntrinsic)227*b8e1544bSIlia Diachkov static void lowerUMulWithOverflow(Module *M, IntrinsicInst *UMulIntrinsic) {
228*b8e1544bSIlia Diachkov   // Get a separate function - otherwise, we'd have to rework the CFG of the
229*b8e1544bSIlia Diachkov   // current one. Then simply replace the intrinsic uses with a call to the new
230*b8e1544bSIlia Diachkov   // function.
231*b8e1544bSIlia Diachkov   FunctionType *UMulFuncTy = UMulIntrinsic->getFunctionType();
232*b8e1544bSIlia Diachkov   Type *FSHLRetTy = UMulFuncTy->getReturnType();
233*b8e1544bSIlia Diachkov   const std::string FuncName = lowerLLVMIntrinsicName(UMulIntrinsic);
234*b8e1544bSIlia Diachkov   Function *UMulFunc =
235*b8e1544bSIlia Diachkov       getOrCreateFunction(M, FSHLRetTy, UMulFuncTy->params(), FuncName);
236*b8e1544bSIlia Diachkov   buildUMulWithOverflowFunc(M, UMulFunc);
237*b8e1544bSIlia Diachkov   UMulIntrinsic->setCalledFunction(UMulFunc);
238*b8e1544bSIlia Diachkov }
239*b8e1544bSIlia Diachkov 
substituteIntrinsicCalls(Module * M,Function * F)240*b8e1544bSIlia Diachkov static void substituteIntrinsicCalls(Module *M, Function *F) {
241*b8e1544bSIlia Diachkov   for (BasicBlock &BB : *F) {
242*b8e1544bSIlia Diachkov     for (Instruction &I : BB) {
243*b8e1544bSIlia Diachkov       auto Call = dyn_cast<CallInst>(&I);
244*b8e1544bSIlia Diachkov       if (!Call)
245*b8e1544bSIlia Diachkov         continue;
246*b8e1544bSIlia Diachkov       Call->setTailCall(false);
247*b8e1544bSIlia Diachkov       Function *CF = Call->getCalledFunction();
248*b8e1544bSIlia Diachkov       if (!CF || !CF->isIntrinsic())
249*b8e1544bSIlia Diachkov         continue;
250*b8e1544bSIlia Diachkov       auto *II = cast<IntrinsicInst>(Call);
251*b8e1544bSIlia Diachkov       if (II->getIntrinsicID() == Intrinsic::fshl ||
252*b8e1544bSIlia Diachkov           II->getIntrinsicID() == Intrinsic::fshr)
253*b8e1544bSIlia Diachkov         lowerFunnelShifts(M, II);
254*b8e1544bSIlia Diachkov       else if (II->getIntrinsicID() == Intrinsic::umul_with_overflow)
255*b8e1544bSIlia Diachkov         lowerUMulWithOverflow(M, II);
256*b8e1544bSIlia Diachkov     }
257*b8e1544bSIlia Diachkov   }
258*b8e1544bSIlia Diachkov }
259*b8e1544bSIlia Diachkov 
runOnModule(Module & M)260*b8e1544bSIlia Diachkov bool SPIRVPrepareFunctions::runOnModule(Module &M) {
261*b8e1544bSIlia Diachkov   for (Function &F : M)
262*b8e1544bSIlia Diachkov     substituteIntrinsicCalls(&M, &F);
263*b8e1544bSIlia Diachkov 
264*b8e1544bSIlia Diachkov   std::vector<Function *> FuncsWorklist;
265*b8e1544bSIlia Diachkov   bool Changed = false;
266*b8e1544bSIlia Diachkov   for (auto &F : M)
267*b8e1544bSIlia Diachkov     FuncsWorklist.push_back(&F);
268*b8e1544bSIlia Diachkov 
269*b8e1544bSIlia Diachkov   for (auto *Func : FuncsWorklist) {
270*b8e1544bSIlia Diachkov     Function *F = processFunctionSignature(Func);
271*b8e1544bSIlia Diachkov 
272*b8e1544bSIlia Diachkov     bool CreatedNewF = F != Func;
273*b8e1544bSIlia Diachkov 
274*b8e1544bSIlia Diachkov     if (Func->isDeclaration()) {
275*b8e1544bSIlia Diachkov       Changed |= CreatedNewF;
276*b8e1544bSIlia Diachkov       continue;
277*b8e1544bSIlia Diachkov     }
278*b8e1544bSIlia Diachkov 
279*b8e1544bSIlia Diachkov     if (CreatedNewF)
280*b8e1544bSIlia Diachkov       Func->eraseFromParent();
281*b8e1544bSIlia Diachkov   }
282*b8e1544bSIlia Diachkov 
283*b8e1544bSIlia Diachkov   return Changed;
284*b8e1544bSIlia Diachkov }
285*b8e1544bSIlia Diachkov 
createSPIRVPrepareFunctionsPass()286*b8e1544bSIlia Diachkov ModulePass *llvm::createSPIRVPrepareFunctionsPass() {
287*b8e1544bSIlia Diachkov   return new SPIRVPrepareFunctions();
288*b8e1544bSIlia Diachkov }
289