RSForEachExpand.cpp revision 02f3cd69a4103f91a51a2f988f2179c885d734b5
1/*
2 * Copyright 2012, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "bcc/Assert.h"
18#include "bcc/Renderscript/RSTransforms.h"
19
20#include <cstdlib>
21
22#include <llvm/IR/DerivedTypes.h>
23#include <llvm/IR/Function.h>
24#include <llvm/IR/Instructions.h>
25#include <llvm/IR/IRBuilder.h>
26#include <llvm/IR/Module.h>
27#include <llvm/Pass.h>
28#include <llvm/Support/raw_ostream.h>
29#include <llvm/IR/DataLayout.h>
30#include <llvm/IR/Type.h>
31#include <llvm/Transforms/Utils/BasicBlockUtils.h>
32
33#include "bcc/Config/Config.h"
34#include "bcc/Renderscript/RSInfo.h"
35#include "bcc/Support/Log.h"
36
37using namespace bcc;
38
39namespace {
40
41/* RSForEachExpandPass - This pass operates on functions that are able to be
42 * called via rsForEach() or "foreach_<NAME>". We create an inner loop for the
43 * ForEach-able function to be invoked over the appropriate data cells of the
44 * input/output allocations (adjusting other relevant parameters as we go). We
45 * support doing this for any ForEach-able compute kernels. The new function
46 * name is the original function name followed by ".expand". Note that we
47 * still generate code for the original function.
48 */
49class RSForEachExpandPass : public llvm::ModulePass {
50private:
51  static char ID;
52
53  llvm::Module *M;
54  llvm::LLVMContext *C;
55
56  const RSInfo::ExportForeachFuncListTy &mFuncs;
57
58  // Turns on optimization of allocation stride values.
59  bool mEnableStepOpt;
60
61  uint32_t getRootSignature(llvm::Function *F) {
62    const llvm::NamedMDNode *ExportForEachMetadata =
63        M->getNamedMetadata("#rs_export_foreach");
64
65    if (!ExportForEachMetadata) {
66      llvm::SmallVector<llvm::Type*, 8> RootArgTys;
67      for (llvm::Function::arg_iterator B = F->arg_begin(),
68                                        E = F->arg_end();
69           B != E;
70           ++B) {
71        RootArgTys.push_back(B->getType());
72      }
73
74      // For pre-ICS bitcode, we may not have signature information. In that
75      // case, we use the size of the RootArgTys to select the number of
76      // arguments.
77      return (1 << RootArgTys.size()) - 1;
78    }
79
80    if (ExportForEachMetadata->getNumOperands() == 0) {
81      return 0;
82    }
83
84    bccAssert(ExportForEachMetadata->getNumOperands() > 0);
85
86    // We only handle the case for legacy root() functions here, so this is
87    // hard-coded to look at only the first such function.
88    llvm::MDNode *SigNode = ExportForEachMetadata->getOperand(0);
89    if (SigNode != NULL && SigNode->getNumOperands() == 1) {
90      llvm::Value *SigVal = SigNode->getOperand(0);
91      if (SigVal->getValueID() == llvm::Value::MDStringVal) {
92        llvm::StringRef SigString =
93            static_cast<llvm::MDString*>(SigVal)->getString();
94        uint32_t Signature = 0;
95        if (SigString.getAsInteger(10, Signature)) {
96          ALOGE("Non-integer signature value '%s'", SigString.str().c_str());
97          return 0;
98        }
99        return Signature;
100      }
101    }
102
103    return 0;
104  }
105
106  // Get the actual value we should use to step through an allocation.
107  //
108  // Normally the value we use to step through an allocation is given to us by
109  // the driver. However, for certain primitive data types, we can derive an
110  // integer constant for the step value. We use this integer constant whenever
111  // possible to allow further compiler optimizations to take place.
112  //
113  // DL - Target Data size/layout information.
114  // T - Type of allocation (should be a pointer).
115  // OrigStep - Original step increment (root.expand() input from driver).
116  llvm::Value *getStepValue(llvm::DataLayout *DL, llvm::Type *T,
117                            llvm::Value *OrigStep) {
118    bccAssert(DL);
119    bccAssert(T);
120    bccAssert(OrigStep);
121    llvm::PointerType *PT = llvm::dyn_cast<llvm::PointerType>(T);
122    llvm::Type *VoidPtrTy = llvm::Type::getInt8PtrTy(*C);
123    if (mEnableStepOpt && T != VoidPtrTy && PT) {
124      llvm::Type *ET = PT->getElementType();
125      uint64_t ETSize = DL->getTypeAllocSize(ET);
126      llvm::Type *Int32Ty = llvm::Type::getInt32Ty(*C);
127      return llvm::ConstantInt::get(Int32Ty, ETSize);
128    } else {
129      return OrigStep;
130    }
131  }
132
133  static bool hasIn(uint32_t Signature) {
134    return Signature & 0x01;
135  }
136
137  static bool hasOut(uint32_t Signature) {
138    return Signature & 0x02;
139  }
140
141  static bool hasUsrData(uint32_t Signature) {
142    return Signature & 0x04;
143  }
144
145  static bool hasX(uint32_t Signature) {
146    return Signature & 0x08;
147  }
148
149  static bool hasY(uint32_t Signature) {
150    return Signature & 0x10;
151  }
152
153  static bool isKernel(uint32_t Signature) {
154    return Signature & 0x20;
155  }
156
157  /// @brief Returns the type of the ForEach stub parameter structure.
158  ///
159  /// Renderscript uses a single structure in which all parameters are passed
160  /// to keep the signature of the expanded function independent of the
161  /// parameters passed to it.
162  llvm::Type *getForeachStubTy() {
163    llvm::Type *VoidPtrTy = llvm::Type::getInt8PtrTy(*C);
164    llvm::Type *Int32Ty = llvm::Type::getInt32Ty(*C);
165    llvm::Type *SizeTy = Int32Ty;
166    /* Defined in frameworks/base/libs/rs/rs_hal.h:
167     *
168     * struct RsForEachStubParamStruct {
169     *   const void *in;
170     *   void *out;
171     *   const void *usr;
172     *   size_t usr_len;
173     *   uint32_t x;
174     *   uint32_t y;
175     *   uint32_t z;
176     *   uint32_t lod;
177     *   enum RsAllocationCubemapFace face;
178     *   uint32_t ar[16];
179     * };
180     */
181    llvm::SmallVector<llvm::Type*, 9> StructTys;
182    StructTys.push_back(VoidPtrTy);  // const void *in
183    StructTys.push_back(VoidPtrTy);  // void *out
184    StructTys.push_back(VoidPtrTy);  // const void *usr
185    StructTys.push_back(SizeTy);     // size_t usr_len
186    StructTys.push_back(Int32Ty);    // uint32_t x
187    StructTys.push_back(Int32Ty);    // uint32_t y
188    StructTys.push_back(Int32Ty);    // uint32_t z
189    StructTys.push_back(Int32Ty);    // uint32_t lod
190    StructTys.push_back(Int32Ty);    // enum RsAllocationCubemapFace
191    StructTys.push_back(llvm::ArrayType::get(Int32Ty, 16));  // uint32_t ar[16]
192
193    return llvm::StructType::create(StructTys, "RsForEachStubParamStruct");
194  }
195
196  /// @brief Create skeleton of the expanded function.
197  ///
198  /// This creates a function with the following signature:
199  ///
200  ///   void (const RsForEachStubParamStruct *p, uint32_t x1, uint32_t x2,
201  ///         uint32_t instep, uint32_t outstep)
202  ///
203  llvm::Function *createEmptyExpandedFunction(llvm::StringRef OldName) {
204    llvm::Type *ForEachStubPtrTy = getForeachStubTy()->getPointerTo();
205    llvm::Type *Int32Ty = llvm::Type::getInt32Ty(*C);
206
207    llvm::SmallVector<llvm::Type*, 8> ParamTys;
208    ParamTys.push_back(ForEachStubPtrTy);  // const RsForEachStubParamStruct *p
209    ParamTys.push_back(Int32Ty);           // uint32_t x1
210    ParamTys.push_back(Int32Ty);           // uint32_t x2
211    ParamTys.push_back(Int32Ty);           // uint32_t instep
212    ParamTys.push_back(Int32Ty);           // uint32_t outstep
213
214    llvm::FunctionType *FT =
215        llvm::FunctionType::get(llvm::Type::getVoidTy(*C), ParamTys, false);
216    llvm::Function *F =
217        llvm::Function::Create(FT, llvm::GlobalValue::ExternalLinkage,
218                               OldName + ".expand", M);
219
220    llvm::Function::arg_iterator AI = F->arg_begin();
221
222    AI->setName("p");
223    AI++;
224    AI->setName("x1");
225    AI++;
226    AI->setName("x2");
227    AI++;
228    AI->setName("arg_instep");
229    AI++;
230    AI->setName("arg_outstep");
231    AI++;
232
233    assert(AI == F->arg_end());
234
235    llvm::BasicBlock *Begin = llvm::BasicBlock::Create(*C, "Begin", F);
236    llvm::IRBuilder<> Builder(Begin);
237    Builder.CreateRetVoid();
238
239    return F;
240  }
241
242  /// @brief Create an empty loop
243  ///
244  /// Create a loop of the form:
245  ///
246  /// for (i = LowerBound; i < UpperBound; i++)
247  ///   ;
248  ///
249  /// After the loop has been created, the builder is set such that
250  /// instructions can be added to the loop body.
251  ///
252  /// @param Builder The builder to use to build this loop. The current
253  ///                position of the builder is the position the loop
254  ///                will be inserted.
255  /// @param LowerBound The first value of the loop iterator
256  /// @param UpperBound The maximal value of the loop iterator
257  /// @param LoopIV A reference that will be set to the loop iterator.
258  /// @return The BasicBlock that will be executed after the loop.
259  llvm::BasicBlock *createLoop(llvm::IRBuilder<> &Builder,
260                               llvm::Value *LowerBound,
261                               llvm::Value *UpperBound,
262                               llvm::PHINode **LoopIV) {
263    assert(LowerBound->getType() == UpperBound->getType());
264
265    llvm::BasicBlock *CondBB, *AfterBB, *HeaderBB;
266    llvm::Value *Cond, *IVNext;
267    llvm::PHINode *IV;
268
269    CondBB = Builder.GetInsertBlock();
270    AfterBB = llvm::SplitBlock(CondBB, Builder.GetInsertPoint(), this);
271    HeaderBB = llvm::BasicBlock::Create(*C, "Loop", CondBB->getParent());
272
273    // if (LowerBound < Upperbound)
274    //   goto LoopHeader
275    // else
276    //   goto AfterBB
277    CondBB->getTerminator()->eraseFromParent();
278    Builder.SetInsertPoint(CondBB);
279    Cond = Builder.CreateICmpULT(LowerBound, UpperBound);
280    Builder.CreateCondBr(Cond, HeaderBB, AfterBB);
281
282    // iv = PHI [CondBB -> LowerBound], [LoopHeader -> NextIV ]
283    // iv.next = iv + 1
284    // if (iv.next < Upperbound)
285    //   goto LoopHeader
286    // else
287    //   goto AfterBB
288    Builder.SetInsertPoint(HeaderBB);
289    IV = Builder.CreatePHI(LowerBound->getType(), 2, "X");
290    IV->addIncoming(LowerBound, CondBB);
291    IVNext = Builder.CreateNUWAdd(IV, Builder.getInt32(1));
292    IV->addIncoming(IVNext, HeaderBB);
293    Cond = Builder.CreateICmpULT(IVNext, UpperBound);
294    Builder.CreateCondBr(Cond, HeaderBB, AfterBB);
295    AfterBB->setName("Exit");
296    Builder.SetInsertPoint(HeaderBB->getFirstNonPHI());
297    *LoopIV = IV;
298    return AfterBB;
299  }
300
301public:
302  RSForEachExpandPass(const RSInfo::ExportForeachFuncListTy &pForeachFuncs,
303                      bool pEnableStepOpt)
304      : ModulePass(ID), M(NULL), C(NULL), mFuncs(pForeachFuncs),
305        mEnableStepOpt(pEnableStepOpt) {
306  }
307
308  /* Performs the actual optimization on a selected function. On success, the
309   * Module will contain a new function of the name "<NAME>.expand" that
310   * invokes <NAME>() in a loop with the appropriate parameters.
311   */
312  bool ExpandFunction(llvm::Function *F, uint32_t Signature) {
313    ALOGV("Expanding ForEach-able Function %s", F->getName().str().c_str());
314
315    if (!Signature) {
316      Signature = getRootSignature(F);
317      if (!Signature) {
318        // We couldn't determine how to expand this function based on its
319        // function signature.
320        return false;
321      }
322    }
323
324    llvm::DataLayout DL(M);
325
326    llvm::Type *Int32Ty = llvm::Type::getInt32Ty(*C);
327    llvm::Function *ExpandedFunc = createEmptyExpandedFunction(F->getName());
328
329    // Create and name the actual arguments to this expanded function.
330    llvm::SmallVector<llvm::Argument*, 8> ArgVec;
331    for (llvm::Function::arg_iterator B = ExpandedFunc->arg_begin(),
332                                      E = ExpandedFunc->arg_end();
333         B != E;
334         ++B) {
335      ArgVec.push_back(B);
336    }
337
338    if (ArgVec.size() != 5) {
339      ALOGE("Incorrect number of arguments to function: %zu",
340            ArgVec.size());
341      return false;
342    }
343    llvm::Value *Arg_p = ArgVec[0];
344    llvm::Value *Arg_x1 = ArgVec[1];
345    llvm::Value *Arg_x2 = ArgVec[2];
346    llvm::Value *Arg_instep = ArgVec[3];
347    llvm::Value *Arg_outstep = ArgVec[4];
348
349    llvm::Value *InStep = NULL;
350    llvm::Value *OutStep = NULL;
351
352    // Construct the actual function body.
353    llvm::IRBuilder<> Builder(ExpandedFunc->getEntryBlock().begin());
354
355    // Collect and construct the arguments for the kernel().
356    // Note that we load any loop-invariant arguments before entering the Loop.
357    llvm::Function::arg_iterator Args = F->arg_begin();
358
359    llvm::Type *InTy = NULL;
360    llvm::AllocaInst *AIn = NULL;
361    if (hasIn(Signature)) {
362      InTy = Args->getType();
363      AIn = Builder.CreateAlloca(InTy, 0, "AIn");
364      InStep = getStepValue(&DL, InTy, Arg_instep);
365      InStep->setName("instep");
366      Builder.CreateStore(Builder.CreatePointerCast(Builder.CreateLoad(
367          Builder.CreateStructGEP(Arg_p, 0)), InTy), AIn);
368      Args++;
369    }
370
371    llvm::Type *OutTy = NULL;
372    llvm::Value *OutBasePtr = NULL;
373    if (hasOut(Signature)) {
374      OutTy = Args->getType();
375      OutStep = getStepValue(&DL, OutTy, Arg_outstep);
376      OutStep->setName("outstep");
377      OutBasePtr = Builder.CreateLoad(Builder.CreateStructGEP(Arg_p, 1));
378      Args++;
379    }
380
381    llvm::Value *UsrData = NULL;
382    if (hasUsrData(Signature)) {
383      llvm::Type *UsrDataTy = Args->getType();
384      UsrData = Builder.CreatePointerCast(Builder.CreateLoad(
385          Builder.CreateStructGEP(Arg_p, 2)), UsrDataTy);
386      UsrData->setName("UsrData");
387      Args++;
388    }
389
390    if (hasX(Signature)) {
391      Args++;
392    }
393
394    llvm::Value *Y = NULL;
395    if (hasY(Signature)) {
396      Y = Builder.CreateLoad(Builder.CreateStructGEP(Arg_p, 5), "Y");
397      Args++;
398    }
399
400    bccAssert(Args == F->arg_end());
401
402    llvm::PHINode *IV;
403    createLoop(Builder, Arg_x1, Arg_x2, &IV);
404
405    // Populate the actual call to kernel().
406    llvm::SmallVector<llvm::Value*, 8> RootArgs;
407
408    llvm::Value *InPtr = NULL;
409    llvm::Value *OutPtr = NULL;
410
411    // Calculate the current output pointer
412    //
413    // We always calculate the output pointer with an GEP operating on i8
414    // values and only cast at the very end to OutTy. This is because the step
415    // between two values is given in bytes.
416    //
417    // TODO: We could further optimize the output by using a GEP operation of
418    // type 'OutTy' in cases where the element type of the allocation allows.
419    if (OutBasePtr) {
420      llvm::Value *OutOffset = Builder.CreateSub(IV, Arg_x1);
421      OutOffset = Builder.CreateMul(OutOffset, OutStep);
422      OutPtr = Builder.CreateGEP(OutBasePtr, OutOffset);
423      OutPtr = Builder.CreatePointerCast(OutPtr, OutTy);
424    }
425
426    if (AIn) {
427      InPtr = Builder.CreateLoad(AIn, "InPtr");
428      RootArgs.push_back(InPtr);
429    }
430
431    if (OutPtr) {
432      RootArgs.push_back(OutPtr);
433    }
434
435    if (UsrData) {
436      RootArgs.push_back(UsrData);
437    }
438
439    llvm::Value *X = IV;
440    if (hasX(Signature)) {
441      RootArgs.push_back(X);
442    }
443
444    if (Y) {
445      RootArgs.push_back(Y);
446    }
447
448    Builder.CreateCall(F, RootArgs);
449
450    if (InPtr) {
451      // InPtr += instep
452      llvm::Value *NewIn = Builder.CreateIntToPtr(Builder.CreateNUWAdd(
453          Builder.CreatePtrToInt(InPtr, Int32Ty), InStep), InTy);
454      Builder.CreateStore(NewIn, AIn);
455    }
456
457    return true;
458  }
459
460  /* Expand a pass-by-value kernel.
461   */
462  bool ExpandKernel(llvm::Function *F, uint32_t Signature) {
463    bccAssert(isKernel(Signature));
464    ALOGV("Expanding kernel Function %s", F->getName().str().c_str());
465
466    // TODO: Refactor this to share functionality with ExpandFunction.
467    llvm::DataLayout DL(M);
468
469    llvm::Type *Int32Ty = llvm::Type::getInt32Ty(*C);
470    llvm::Function *ExpandedFunc = createEmptyExpandedFunction(F->getName());
471
472    // Create and name the actual arguments to this expanded function.
473    llvm::SmallVector<llvm::Argument*, 8> ArgVec;
474    for (llvm::Function::arg_iterator B = ExpandedFunc->arg_begin(),
475                                      E = ExpandedFunc->arg_end();
476         B != E;
477         ++B) {
478      ArgVec.push_back(B);
479    }
480
481    if (ArgVec.size() != 5) {
482      ALOGE("Incorrect number of arguments to function: %zu",
483            ArgVec.size());
484      return false;
485    }
486    llvm::Value *Arg_p = ArgVec[0];
487    llvm::Value *Arg_x1 = ArgVec[1];
488    llvm::Value *Arg_x2 = ArgVec[2];
489    llvm::Value *Arg_instep = ArgVec[3];
490    llvm::Value *Arg_outstep = ArgVec[4];
491
492    llvm::Value *InStep = NULL;
493    llvm::Value *OutStep = NULL;
494
495    // Construct the actual function body.
496    llvm::IRBuilder<> Builder(ExpandedFunc->getEntryBlock().begin());
497
498    // Collect and construct the arguments for the kernel().
499    // Note that we load any loop-invariant arguments before entering the Loop.
500    llvm::Function::arg_iterator Args = F->arg_begin();
501
502    llvm::Type *OutTy = NULL;
503    bool PassOutByReference = false;
504    llvm::Value *OutBasePtr = NULL;
505    if (hasOut(Signature)) {
506      llvm::Type *OutBaseTy = F->getReturnType();
507      if (OutBaseTy->isVoidTy()) {
508        PassOutByReference = true;
509        OutTy = Args->getType();
510        Args++;
511      } else {
512        OutTy = OutBaseTy->getPointerTo();
513        // We don't increment Args, since we are using the actual return type.
514      }
515      OutStep = getStepValue(&DL, OutTy, Arg_outstep);
516      OutStep->setName("outstep");
517      OutBasePtr = Builder.CreateLoad(Builder.CreateStructGEP(Arg_p, 1));
518    }
519
520    llvm::Type *InBaseTy = NULL;
521    llvm::Type *InTy = NULL;
522    llvm::AllocaInst *AIn = NULL;
523    if (hasIn(Signature)) {
524      InBaseTy = Args->getType();
525      InTy =InBaseTy->getPointerTo();
526      AIn = Builder.CreateAlloca(InTy, 0, "AIn");
527      InStep = getStepValue(&DL, InTy, Arg_instep);
528      InStep->setName("instep");
529      Builder.CreateStore(Builder.CreatePointerCast(Builder.CreateLoad(
530          Builder.CreateStructGEP(Arg_p, 0)), InTy), AIn);
531      Args++;
532    }
533
534    // No usrData parameter on kernels.
535    bccAssert(!hasUsrData(Signature));
536
537    if (hasX(Signature)) {
538      Args++;
539    }
540
541    llvm::Value *Y = NULL;
542    if (hasY(Signature)) {
543      Y = Builder.CreateLoad(Builder.CreateStructGEP(Arg_p, 5), "Y");
544      Args++;
545    }
546
547    bccAssert(Args == F->arg_end());
548
549    llvm::PHINode *IV;
550    createLoop(Builder, Arg_x1, Arg_x2, &IV);
551
552    // Populate the actual call to kernel().
553    llvm::SmallVector<llvm::Value*, 8> RootArgs;
554
555    llvm::Value *InPtr = NULL;
556    llvm::Value *In = NULL;
557    llvm::Value *OutPtr = NULL;
558
559    // Calculate the current output pointer
560    //
561    // We always calculate the output pointer with an GEP operating on i8
562    // values and only cast at the very end to OutTy. This is because the step
563    // between two values is given in bytes.
564    //
565    // TODO: We could further optimize the output by using a GEP operation of
566    // type 'OutTy' in cases where the element type of the allocation allows.
567    if (OutBasePtr) {
568      llvm::Value *OutOffset = Builder.CreateSub(IV, Arg_x1);
569      OutOffset = Builder.CreateMul(OutOffset, OutStep);
570      OutPtr = Builder.CreateGEP(OutBasePtr, OutOffset);
571      OutPtr = Builder.CreatePointerCast(OutPtr, OutTy);
572    }
573
574    if (PassOutByReference) {
575      RootArgs.push_back(OutPtr);
576    }
577
578    if (AIn) {
579      InPtr = Builder.CreateLoad(AIn, "InPtr");
580      In = Builder.CreateLoad(InPtr, "In");
581      RootArgs.push_back(In);
582    }
583
584    llvm::Value *X = IV;
585    if (hasX(Signature)) {
586      RootArgs.push_back(X);
587    }
588
589    if (Y) {
590      RootArgs.push_back(Y);
591    }
592
593    llvm::Value *RetVal = Builder.CreateCall(F, RootArgs);
594
595    if (OutPtr && !PassOutByReference) {
596      Builder.CreateStore(RetVal, OutPtr);
597    }
598
599    if (InPtr) {
600      // InPtr += instep
601      llvm::Value *NewIn = Builder.CreateIntToPtr(Builder.CreateNUWAdd(
602          Builder.CreatePtrToInt(InPtr, Int32Ty), InStep), InTy);
603      Builder.CreateStore(NewIn, AIn);
604    }
605
606    return true;
607  }
608
609  virtual bool runOnModule(llvm::Module &M) {
610    bool Changed = false;
611    this->M = &M;
612    C = &M.getContext();
613
614    for (RSInfo::ExportForeachFuncListTy::const_iterator
615             func_iter = mFuncs.begin(), func_end = mFuncs.end();
616         func_iter != func_end; func_iter++) {
617      const char *name = func_iter->first;
618      uint32_t signature = func_iter->second;
619      llvm::Function *kernel = M.getFunction(name);
620      if (kernel && isKernel(signature)) {
621        Changed |= ExpandKernel(kernel, signature);
622      }
623      else if (kernel && kernel->getReturnType()->isVoidTy()) {
624        Changed |= ExpandFunction(kernel, signature);
625      }
626    }
627
628    return Changed;
629  }
630
631  virtual const char *getPassName() const {
632    return "ForEach-able Function Expansion";
633  }
634
635}; // end RSForEachExpandPass
636
637} // end anonymous namespace
638
639char RSForEachExpandPass::ID = 0;
640
641namespace bcc {
642
643llvm::ModulePass *
644createRSForEachExpandPass(const RSInfo::ExportForeachFuncListTy &pForeachFuncs,
645                          bool pEnableStepOpt){
646  return new RSForEachExpandPass(pForeachFuncs, pEnableStepOpt);
647}
648
649} // end namespace bcc
650