1*609089f2SHongbin Zheng //===------ LoopGenerators.cpp - IR helper to create loops ---------------===// 2*609089f2SHongbin Zheng // 3*609089f2SHongbin Zheng // The LLVM Compiler Infrastructure 4*609089f2SHongbin Zheng // 5*609089f2SHongbin Zheng // This file is distributed under the University of Illinois Open Source 6*609089f2SHongbin Zheng // License. See LICENSE.TXT for details. 7*609089f2SHongbin Zheng // 8*609089f2SHongbin Zheng //===----------------------------------------------------------------------===// 9*609089f2SHongbin Zheng // 10*609089f2SHongbin Zheng // This file contains functions to create scalar and OpenMP parallel loops 11*609089f2SHongbin Zheng // as LLVM-IR. 12*609089f2SHongbin Zheng // 13*609089f2SHongbin Zheng //===----------------------------------------------------------------------===// 14*609089f2SHongbin Zheng 15*609089f2SHongbin Zheng #include "polly/LoopGenerators.h" 16*609089f2SHongbin Zheng #include "polly/ScopDetection.h" 17*609089f2SHongbin Zheng 18*609089f2SHongbin Zheng #include "llvm/Module.h" 19*609089f2SHongbin Zheng #include "llvm/Analysis/Dominators.h" 20*609089f2SHongbin Zheng #include "llvm/Target/TargetData.h" 21*609089f2SHongbin Zheng #include "llvm/Transforms/Utils/BasicBlockUtils.h" 22*609089f2SHongbin Zheng 23*609089f2SHongbin Zheng using namespace llvm; 24*609089f2SHongbin Zheng 25*609089f2SHongbin Zheng Value *createLoop(Value *LB, Value *UB, Value *Stride, 26*609089f2SHongbin Zheng IRBuilder<> *Builder, Pass *P, BasicBlock **AfterBlock) { 27*609089f2SHongbin Zheng DominatorTree &DT = P->getAnalysis<DominatorTree>(); 28*609089f2SHongbin Zheng Function *F = Builder->GetInsertBlock()->getParent(); 29*609089f2SHongbin Zheng LLVMContext &Context = F->getContext(); 30*609089f2SHongbin Zheng 31*609089f2SHongbin Zheng BasicBlock *PreheaderBB = Builder->GetInsertBlock(); 32*609089f2SHongbin Zheng BasicBlock *HeaderBB = BasicBlock::Create(Context, "polly.loop_header", F); 33*609089f2SHongbin Zheng BasicBlock *BodyBB = BasicBlock::Create(Context, "polly.loop_body", F); 34*609089f2SHongbin Zheng BasicBlock *AfterBB = SplitBlock(PreheaderBB, Builder->GetInsertPoint()++, P); 35*609089f2SHongbin Zheng AfterBB->setName("polly.loop_after"); 36*609089f2SHongbin Zheng 37*609089f2SHongbin Zheng PreheaderBB->getTerminator()->setSuccessor(0, HeaderBB); 38*609089f2SHongbin Zheng DT.addNewBlock(HeaderBB, PreheaderBB); 39*609089f2SHongbin Zheng 40*609089f2SHongbin Zheng Builder->SetInsertPoint(HeaderBB); 41*609089f2SHongbin Zheng 42*609089f2SHongbin Zheng // Use the type of upper and lower bound. 43*609089f2SHongbin Zheng assert(LB->getType() == UB->getType() 44*609089f2SHongbin Zheng && "Different types for upper and lower bound."); 45*609089f2SHongbin Zheng 46*609089f2SHongbin Zheng IntegerType *LoopIVType = dyn_cast<IntegerType>(UB->getType()); 47*609089f2SHongbin Zheng assert(LoopIVType && "UB is not integer?"); 48*609089f2SHongbin Zheng 49*609089f2SHongbin Zheng // IV 50*609089f2SHongbin Zheng PHINode *IV = Builder->CreatePHI(LoopIVType, 2, "polly.loopiv"); 51*609089f2SHongbin Zheng IV->addIncoming(LB, PreheaderBB); 52*609089f2SHongbin Zheng 53*609089f2SHongbin Zheng Stride = Builder->CreateZExtOrBitCast(Stride, LoopIVType); 54*609089f2SHongbin Zheng Value *IncrementedIV = Builder->CreateAdd(IV, Stride, "polly.next_loopiv"); 55*609089f2SHongbin Zheng 56*609089f2SHongbin Zheng // Exit condition. 57*609089f2SHongbin Zheng Value *CMP; 58*609089f2SHongbin Zheng CMP = Builder->CreateICmpSLE(IV, UB); 59*609089f2SHongbin Zheng 60*609089f2SHongbin Zheng Builder->CreateCondBr(CMP, BodyBB, AfterBB); 61*609089f2SHongbin Zheng DT.addNewBlock(BodyBB, HeaderBB); 62*609089f2SHongbin Zheng 63*609089f2SHongbin Zheng Builder->SetInsertPoint(BodyBB); 64*609089f2SHongbin Zheng Builder->CreateBr(HeaderBB); 65*609089f2SHongbin Zheng IV->addIncoming(IncrementedIV, BodyBB); 66*609089f2SHongbin Zheng DT.changeImmediateDominator(AfterBB, HeaderBB); 67*609089f2SHongbin Zheng 68*609089f2SHongbin Zheng Builder->SetInsertPoint(BodyBB->begin()); 69*609089f2SHongbin Zheng *AfterBlock = AfterBB; 70*609089f2SHongbin Zheng 71*609089f2SHongbin Zheng return IV; 72*609089f2SHongbin Zheng } 73*609089f2SHongbin Zheng 74*609089f2SHongbin Zheng void OMPGenerator::createCallParallelLoopStart(Value *SubFunction, 75*609089f2SHongbin Zheng Value *SubfunctionParam, 76*609089f2SHongbin Zheng Value *NumberOfThreads, 77*609089f2SHongbin Zheng Value *LowerBound, 78*609089f2SHongbin Zheng Value *UpperBound, 79*609089f2SHongbin Zheng Value *Stride) { 80*609089f2SHongbin Zheng Module *M = getModule(); 81*609089f2SHongbin Zheng const char *Name = "GOMP_parallel_loop_runtime_start"; 82*609089f2SHongbin Zheng Function *F = M->getFunction(Name); 83*609089f2SHongbin Zheng 84*609089f2SHongbin Zheng // If F is not available, declare it. 85*609089f2SHongbin Zheng if (!F) { 86*609089f2SHongbin Zheng Type *LongTy = getIntPtrTy(); 87*609089f2SHongbin Zheng GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 88*609089f2SHongbin Zheng 89*609089f2SHongbin Zheng Type *Params[] = { 90*609089f2SHongbin Zheng PointerType::getUnqual(FunctionType::get(Builder.getVoidTy(), 91*609089f2SHongbin Zheng Builder.getInt8PtrTy(), 92*609089f2SHongbin Zheng false)), 93*609089f2SHongbin Zheng Builder.getInt8PtrTy(), 94*609089f2SHongbin Zheng Builder.getInt32Ty(), 95*609089f2SHongbin Zheng LongTy, 96*609089f2SHongbin Zheng LongTy, 97*609089f2SHongbin Zheng LongTy, 98*609089f2SHongbin Zheng }; 99*609089f2SHongbin Zheng 100*609089f2SHongbin Zheng FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Params, false); 101*609089f2SHongbin Zheng F = Function::Create(Ty, Linkage, Name, M); 102*609089f2SHongbin Zheng } 103*609089f2SHongbin Zheng 104*609089f2SHongbin Zheng Value *Args[] = { 105*609089f2SHongbin Zheng SubFunction, 106*609089f2SHongbin Zheng SubfunctionParam, 107*609089f2SHongbin Zheng NumberOfThreads, 108*609089f2SHongbin Zheng LowerBound, 109*609089f2SHongbin Zheng UpperBound, 110*609089f2SHongbin Zheng Stride, 111*609089f2SHongbin Zheng }; 112*609089f2SHongbin Zheng 113*609089f2SHongbin Zheng Builder.CreateCall(F, Args); 114*609089f2SHongbin Zheng } 115*609089f2SHongbin Zheng 116*609089f2SHongbin Zheng Value *OMPGenerator::createCallLoopNext(Value *LowerBoundPtr, 117*609089f2SHongbin Zheng Value *UpperBoundPtr) { 118*609089f2SHongbin Zheng Module *M = getModule(); 119*609089f2SHongbin Zheng const char *Name = "GOMP_loop_runtime_next"; 120*609089f2SHongbin Zheng Function *F = M->getFunction(Name); 121*609089f2SHongbin Zheng 122*609089f2SHongbin Zheng // If F is not available, declare it. 123*609089f2SHongbin Zheng if (!F) { 124*609089f2SHongbin Zheng Type *LongPtrTy = PointerType::getUnqual(getIntPtrTy()); 125*609089f2SHongbin Zheng GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 126*609089f2SHongbin Zheng 127*609089f2SHongbin Zheng Type *Params[] = { 128*609089f2SHongbin Zheng LongPtrTy, 129*609089f2SHongbin Zheng LongPtrTy, 130*609089f2SHongbin Zheng }; 131*609089f2SHongbin Zheng 132*609089f2SHongbin Zheng FunctionType *Ty = FunctionType::get(Builder.getInt8Ty(), Params, false); 133*609089f2SHongbin Zheng F = Function::Create(Ty, Linkage, Name, M); 134*609089f2SHongbin Zheng } 135*609089f2SHongbin Zheng 136*609089f2SHongbin Zheng Value *Args[] = { 137*609089f2SHongbin Zheng LowerBoundPtr, 138*609089f2SHongbin Zheng UpperBoundPtr, 139*609089f2SHongbin Zheng }; 140*609089f2SHongbin Zheng 141*609089f2SHongbin Zheng Value *Return = Builder.CreateCall(F, Args); 142*609089f2SHongbin Zheng Return = Builder.CreateICmpNE(Return, Builder.CreateZExt(Builder.getFalse(), 143*609089f2SHongbin Zheng Return->getType())); 144*609089f2SHongbin Zheng return Return; 145*609089f2SHongbin Zheng } 146*609089f2SHongbin Zheng 147*609089f2SHongbin Zheng void OMPGenerator::createCallParallelEnd() { 148*609089f2SHongbin Zheng const char *Name = "GOMP_parallel_end"; 149*609089f2SHongbin Zheng Module *M = getModule(); 150*609089f2SHongbin Zheng Function *F = M->getFunction(Name); 151*609089f2SHongbin Zheng 152*609089f2SHongbin Zheng // If F is not available, declare it. 153*609089f2SHongbin Zheng if (!F) { 154*609089f2SHongbin Zheng GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 155*609089f2SHongbin Zheng 156*609089f2SHongbin Zheng FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), false); 157*609089f2SHongbin Zheng F = Function::Create(Ty, Linkage, Name, M); 158*609089f2SHongbin Zheng } 159*609089f2SHongbin Zheng 160*609089f2SHongbin Zheng Builder.CreateCall(F); 161*609089f2SHongbin Zheng } 162*609089f2SHongbin Zheng 163*609089f2SHongbin Zheng void OMPGenerator::createCallLoopEndNowait() { 164*609089f2SHongbin Zheng const char *Name = "GOMP_loop_end_nowait"; 165*609089f2SHongbin Zheng Module *M = getModule(); 166*609089f2SHongbin Zheng Function *F = M->getFunction(Name); 167*609089f2SHongbin Zheng 168*609089f2SHongbin Zheng // If F is not available, declare it. 169*609089f2SHongbin Zheng if (!F) { 170*609089f2SHongbin Zheng GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 171*609089f2SHongbin Zheng 172*609089f2SHongbin Zheng FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), false); 173*609089f2SHongbin Zheng F = Function::Create(Ty, Linkage, Name, M); 174*609089f2SHongbin Zheng } 175*609089f2SHongbin Zheng 176*609089f2SHongbin Zheng Builder.CreateCall(F); 177*609089f2SHongbin Zheng } 178*609089f2SHongbin Zheng 179*609089f2SHongbin Zheng IntegerType *OMPGenerator::getIntPtrTy() { 180*609089f2SHongbin Zheng return P->getAnalysis<TargetData>().getIntPtrType(Builder.getContext()); 181*609089f2SHongbin Zheng } 182*609089f2SHongbin Zheng 183*609089f2SHongbin Zheng Module *OMPGenerator::getModule() { 184*609089f2SHongbin Zheng return Builder.GetInsertBlock()->getParent()->getParent(); 185*609089f2SHongbin Zheng } 186*609089f2SHongbin Zheng 187*609089f2SHongbin Zheng Function *OMPGenerator::createSubfunctionDefinition() { 188*609089f2SHongbin Zheng Module *M = getModule(); 189*609089f2SHongbin Zheng Function *F = Builder.GetInsertBlock()->getParent(); 190*609089f2SHongbin Zheng std::vector<Type*> Arguments(1, Builder.getInt8PtrTy()); 191*609089f2SHongbin Zheng FunctionType *FT = FunctionType::get(Builder.getVoidTy(), Arguments, false); 192*609089f2SHongbin Zheng Function *FN = Function::Create(FT, Function::InternalLinkage, 193*609089f2SHongbin Zheng F->getName() + ".omp_subfn", M); 194*609089f2SHongbin Zheng // Do not run any polly pass on the new function. 195*609089f2SHongbin Zheng P->getAnalysis<polly::ScopDetection>().markFunctionAsInvalid(FN); 196*609089f2SHongbin Zheng 197*609089f2SHongbin Zheng Function::arg_iterator AI = FN->arg_begin(); 198*609089f2SHongbin Zheng AI->setName("omp.userContext"); 199*609089f2SHongbin Zheng 200*609089f2SHongbin Zheng return FN; 201*609089f2SHongbin Zheng } 202*609089f2SHongbin Zheng 203*609089f2SHongbin Zheng Value *OMPGenerator::loadValuesIntoStruct(SetVector<Value*> &Values) { 204*609089f2SHongbin Zheng std::vector<Type*> Members; 205*609089f2SHongbin Zheng 206*609089f2SHongbin Zheng for (unsigned i = 0; i < Values.size(); i++) 207*609089f2SHongbin Zheng Members.push_back(Values[i]->getType()); 208*609089f2SHongbin Zheng 209*609089f2SHongbin Zheng StructType *Ty = StructType::get(Builder.getContext(), Members); 210*609089f2SHongbin Zheng Value *Struct = Builder.CreateAlloca(Ty, 0, "omp.userContext"); 211*609089f2SHongbin Zheng 212*609089f2SHongbin Zheng for (unsigned i = 0; i < Values.size(); i++) { 213*609089f2SHongbin Zheng Value *Address = Builder.CreateStructGEP(Struct, i); 214*609089f2SHongbin Zheng Builder.CreateStore(Values[i], Address); 215*609089f2SHongbin Zheng } 216*609089f2SHongbin Zheng 217*609089f2SHongbin Zheng return Struct; 218*609089f2SHongbin Zheng } 219*609089f2SHongbin Zheng 220*609089f2SHongbin Zheng void OMPGenerator::extractValuesFromStruct(SetVector<Value*> OldValues, 221*609089f2SHongbin Zheng Value *Struct, 222*609089f2SHongbin Zheng ValueToValueMapTy &Map) { 223*609089f2SHongbin Zheng for (unsigned i = 0; i < OldValues.size(); i++) { 224*609089f2SHongbin Zheng Value *Address = Builder.CreateStructGEP(Struct, i); 225*609089f2SHongbin Zheng Value *NewValue = Builder.CreateLoad(Address); 226*609089f2SHongbin Zheng Map.insert(std::make_pair<Value*, Value*>(OldValues[i], NewValue)); 227*609089f2SHongbin Zheng } 228*609089f2SHongbin Zheng } 229*609089f2SHongbin Zheng 230*609089f2SHongbin Zheng Value *OMPGenerator::createSubfunction(Value *Stride, Value *StructData, 231*609089f2SHongbin Zheng SetVector<Value*> Data, 232*609089f2SHongbin Zheng ValueToValueMapTy &Map, 233*609089f2SHongbin Zheng Function **SubFunction) { 234*609089f2SHongbin Zheng Function *FN = createSubfunctionDefinition(); 235*609089f2SHongbin Zheng 236*609089f2SHongbin Zheng BasicBlock *PrevBB, *HeaderBB, *ExitBB, *CheckNextBB, *LoadIVBoundsBB, 237*609089f2SHongbin Zheng *AfterBB; 238*609089f2SHongbin Zheng Value *LowerBoundPtr, *UpperBoundPtr, *UserContext, *Ret1, *HasNextSchedule, 239*609089f2SHongbin Zheng *LowerBound, *UpperBound, *IV; 240*609089f2SHongbin Zheng Type *IntPtrTy = getIntPtrTy(); 241*609089f2SHongbin Zheng LLVMContext &Context = FN->getContext(); 242*609089f2SHongbin Zheng 243*609089f2SHongbin Zheng // Store the previous basic block. 244*609089f2SHongbin Zheng PrevBB = Builder.GetInsertBlock(); 245*609089f2SHongbin Zheng 246*609089f2SHongbin Zheng // Create basic blocks. 247*609089f2SHongbin Zheng HeaderBB = BasicBlock::Create(Context, "omp.setup", FN); 248*609089f2SHongbin Zheng ExitBB = BasicBlock::Create(Context, "omp.exit", FN); 249*609089f2SHongbin Zheng CheckNextBB = BasicBlock::Create(Context, "omp.checkNext", FN); 250*609089f2SHongbin Zheng LoadIVBoundsBB = BasicBlock::Create(Context, "omp.loadIVBounds", FN); 251*609089f2SHongbin Zheng 252*609089f2SHongbin Zheng DominatorTree &DT = P->getAnalysis<DominatorTree>(); 253*609089f2SHongbin Zheng DT.addNewBlock(HeaderBB, PrevBB); 254*609089f2SHongbin Zheng DT.addNewBlock(ExitBB, HeaderBB); 255*609089f2SHongbin Zheng DT.addNewBlock(CheckNextBB, HeaderBB); 256*609089f2SHongbin Zheng DT.addNewBlock(LoadIVBoundsBB, HeaderBB); 257*609089f2SHongbin Zheng 258*609089f2SHongbin Zheng // Fill up basic block HeaderBB. 259*609089f2SHongbin Zheng Builder.SetInsertPoint(HeaderBB); 260*609089f2SHongbin Zheng LowerBoundPtr = Builder.CreateAlloca(IntPtrTy, 0, "omp.lowerBoundPtr"); 261*609089f2SHongbin Zheng UpperBoundPtr = Builder.CreateAlloca(IntPtrTy, 0, "omp.upperBoundPtr"); 262*609089f2SHongbin Zheng UserContext = Builder.CreateBitCast(FN->arg_begin(), StructData->getType(), 263*609089f2SHongbin Zheng "omp.userContext"); 264*609089f2SHongbin Zheng 265*609089f2SHongbin Zheng extractValuesFromStruct(Data, UserContext, Map); 266*609089f2SHongbin Zheng Builder.CreateBr(CheckNextBB); 267*609089f2SHongbin Zheng 268*609089f2SHongbin Zheng // Add code to check if another set of iterations will be executed. 269*609089f2SHongbin Zheng Builder.SetInsertPoint(CheckNextBB); 270*609089f2SHongbin Zheng Ret1 = createCallLoopNext(LowerBoundPtr, UpperBoundPtr); 271*609089f2SHongbin Zheng HasNextSchedule = Builder.CreateTrunc(Ret1, Builder.getInt1Ty(), 272*609089f2SHongbin Zheng "omp.hasNextScheduleBlock"); 273*609089f2SHongbin Zheng Builder.CreateCondBr(HasNextSchedule, LoadIVBoundsBB, ExitBB); 274*609089f2SHongbin Zheng 275*609089f2SHongbin Zheng // Add code to to load the iv bounds for this set of iterations. 276*609089f2SHongbin Zheng Builder.SetInsertPoint(LoadIVBoundsBB); 277*609089f2SHongbin Zheng LowerBound = Builder.CreateLoad(LowerBoundPtr, "omp.lowerBound"); 278*609089f2SHongbin Zheng UpperBound = Builder.CreateLoad(UpperBoundPtr, "omp.upperBound"); 279*609089f2SHongbin Zheng 280*609089f2SHongbin Zheng // Subtract one as the upper bound provided by openmp is a < comparison 281*609089f2SHongbin Zheng // whereas the codegenForSequential function creates a <= comparison. 282*609089f2SHongbin Zheng UpperBound = Builder.CreateSub(UpperBound, ConstantInt::get(IntPtrTy, 1), 283*609089f2SHongbin Zheng "omp.upperBoundAdjusted"); 284*609089f2SHongbin Zheng 285*609089f2SHongbin Zheng Builder.CreateBr(CheckNextBB); 286*609089f2SHongbin Zheng Builder.SetInsertPoint(--Builder.GetInsertPoint()); 287*609089f2SHongbin Zheng IV = createLoop(LowerBound, UpperBound, Stride, &Builder, P, &AfterBB); 288*609089f2SHongbin Zheng 289*609089f2SHongbin Zheng BasicBlock::iterator LoopBody = Builder.GetInsertPoint(); 290*609089f2SHongbin Zheng Builder.SetInsertPoint(AfterBB->begin()); 291*609089f2SHongbin Zheng 292*609089f2SHongbin Zheng // Add code to terminate this openmp subfunction. 293*609089f2SHongbin Zheng Builder.SetInsertPoint(ExitBB); 294*609089f2SHongbin Zheng createCallLoopEndNowait(); 295*609089f2SHongbin Zheng Builder.CreateRetVoid(); 296*609089f2SHongbin Zheng 297*609089f2SHongbin Zheng Builder.SetInsertPoint(LoopBody); 298*609089f2SHongbin Zheng *SubFunction = FN; 299*609089f2SHongbin Zheng 300*609089f2SHongbin Zheng return IV; 301*609089f2SHongbin Zheng } 302*609089f2SHongbin Zheng 303*609089f2SHongbin Zheng Value *OMPGenerator::createParallelLoop(Value *LowerBound, Value *UpperBound, 304*609089f2SHongbin Zheng Value *Stride, 305*609089f2SHongbin Zheng SetVector<Value*> &Values, 306*609089f2SHongbin Zheng ValueToValueMapTy &Map, 307*609089f2SHongbin Zheng BasicBlock::iterator *LoopBody) { 308*609089f2SHongbin Zheng Value *Struct, *IV, *SubfunctionParam, *NumberOfThreads; 309*609089f2SHongbin Zheng Function *SubFunction; 310*609089f2SHongbin Zheng 311*609089f2SHongbin Zheng Struct = loadValuesIntoStruct(Values); 312*609089f2SHongbin Zheng 313*609089f2SHongbin Zheng BasicBlock::iterator PrevInsertPoint = Builder.GetInsertPoint(); 314*609089f2SHongbin Zheng IV = createSubfunction(Stride, Struct, Values, Map, &SubFunction); 315*609089f2SHongbin Zheng *LoopBody = Builder.GetInsertPoint(); 316*609089f2SHongbin Zheng Builder.SetInsertPoint(PrevInsertPoint); 317*609089f2SHongbin Zheng 318*609089f2SHongbin Zheng // Create call for GOMP_parallel_loop_runtime_start. 319*609089f2SHongbin Zheng SubfunctionParam = Builder.CreateBitCast(Struct, Builder.getInt8PtrTy(), 320*609089f2SHongbin Zheng "omp_data"); 321*609089f2SHongbin Zheng 322*609089f2SHongbin Zheng NumberOfThreads = Builder.getInt32(0); 323*609089f2SHongbin Zheng 324*609089f2SHongbin Zheng // Add one as the upper bound provided by openmp is a < comparison 325*609089f2SHongbin Zheng // whereas the codegenForSequential function creates a <= comparison. 326*609089f2SHongbin Zheng UpperBound = Builder.CreateAdd(UpperBound, 327*609089f2SHongbin Zheng ConstantInt::get(getIntPtrTy(), 1)); 328*609089f2SHongbin Zheng 329*609089f2SHongbin Zheng createCallParallelLoopStart(SubFunction, SubfunctionParam, NumberOfThreads, 330*609089f2SHongbin Zheng LowerBound, UpperBound, Stride); 331*609089f2SHongbin Zheng Builder.CreateCall(SubFunction, SubfunctionParam); 332*609089f2SHongbin Zheng createCallParallelEnd(); 333*609089f2SHongbin Zheng 334*609089f2SHongbin Zheng return IV; 335*609089f2SHongbin Zheng } 336