Exemplo n.º 1
0
void LowerIntrinsics::AutomaticallyRootValue(AllocaInst &AI, Type *Ty,
                                             ArrayRef<Value *> Indices) {
  Module *M = AI.getParent()->getParent()->getParent();
  LLVMContext &C = M->getContext();
  Type *Int32Ty = Type::getInt32Ty(C);

  switch (Ty->getTypeID()) {
  case Type::PointerTyID: {
    if (cast<PointerType>(Ty)->getAddressSpace() < 1)
      break;

    // Create the GEP, if necessary.
    Instruction *BaseInst = &AI;
    if (Indices.size() > 1) {
      BaseInst = GetElementPtrInst::Create(&AI, Indices);
      BaseInst->insertAfter(&AI);
    }

    // Cast the value to an i8** to make a type-compatible intrinsic call.
    Type *PtrTy = PointerType::get(Type::getInt8PtrTy(C), 0);
    Instruction *GCRootArg = new BitCastInst(BaseInst, PtrTy);
    GCRootArg->insertAfter(BaseInst);

    // Cast the addrspace of the root to i8* to make type-compatible with call.
    Constant *AddressSpace =
      ConstantInt::get(Type::getInt64Ty(C), cast<PointerType>(Ty)->getAddressSpace());
    Constant *GCMetadataArg =
      ConstantExpr::getIntToPtr(AddressSpace, Type::getInt8PtrTy(C));

    // Create an intrinsic call.
    Value *Args[2];
    Args[0] = GCRootArg;
    Args[1] = GCMetadataArg;
    Function *GCRootFn = Intrinsic::getDeclaration(M, Intrinsic::gcroot);
    CallInst *Call = CallInst::Create(GCRootFn, Args);
    Call->insertAfter(GCRootArg);
    break;
  }

  case Type::StructTyID:
  case Type::ArrayTyID:
  case Type::VectorTyID: {
    // Skip auto-rooting structs for because we explicitly root these
    // by allocaing pointers on the stack.
    /*
    SmallVector<Value *, 8> NewIndices(Indices.begin(), Indices.end());
    NewIndices.push_back(ConstantInt::get(Int32Ty, 0));
    for (unsigned i = 0; i < Ty->getNumContainedTypes(); ++i) {
      NewIndices[NewIndices.size() - 1] = ConstantInt::get(Int32Ty, i);
      AutomaticallyRootValue(AI, Ty->getContainedType(i), NewIndices);
    }
    */
    break;
  }

  default:
    break;
  }
}
Exemplo n.º 2
0
/// Given a LoadInst LI this adds assume(LI != null) after it.
static void addAssumeNonNull(AssumptionCache *AC, LoadInst *LI) {
  Function *AssumeIntrinsic =
      Intrinsic::getDeclaration(LI->getModule(), Intrinsic::assume);
  ICmpInst *LoadNotNull = new ICmpInst(ICmpInst::ICMP_NE, LI,
                                       Constant::getNullValue(LI->getType()));
  LoadNotNull->insertAfter(LI);
  CallInst *CI = CallInst::Create(AssumeIntrinsic, {LoadNotNull});
  CI->insertAfter(LoadNotNull);
  AC->registerAssumption(CI);
}
Exemplo n.º 3
0
/**
 * Inserts new call instruction.
 * @param CalleeF function to be called
 * @param args arguments of the function to be called
 * @param rw_rule relevant rewrite rule
 * @param currentInstr current instruction
 * @param Iiterator pointer to instructions iterator
 */
void InsertCallInstruction(Function* CalleeF, vector<Value *> args,
                           RewriteRule rw_rule, Instruction *currentInstr,
                           inst_iterator *Iiterator) {
	// Create new call instruction
	CallInst *newInstr = CallInst::Create(CalleeF, args);

    // duplicate the metadata of the instruction for which we
    // instrument the code, some passes (e.g. inliner) can
    // break the code when there's an instruction without metadata
    // when all other instructions have metadata
    if (currentInstr->hasMetadata()) {
        CloneMetadata(currentInstr, newInstr);
    } else if (const DISubprogram *DS = currentInstr->getParent()->getParent()->getSubprogram()) {
        // no metadata? then it is going to be the instrumentation
        // of alloca or such at the beggining of function,
        // so just add debug loc of the beginning of the function
        newInstr->setDebugLoc(DebugLoc::get(DS->getLine(), 0, DS));
    }

	if(rw_rule.where == InstrumentPlacement::BEFORE) {
		// Insert before
		newInstr->insertBefore(currentInstr);
		logger.log_insertion("before", CalleeF, currentInstr);
	}
	else if(rw_rule.where == InstrumentPlacement::AFTER) {
		// Insert after
		newInstr->insertAfter(currentInstr);
		logger.log_insertion("after", CalleeF, currentInstr);
	}
	else if(rw_rule.where == InstrumentPlacement::REPLACE) {
		// TODO: Make the functions use the iterator instead of
		// the instruction then check this works
		// In the end we move the iterator to the newInst position
		// so we can safely remove the sequence of instructions being
		// replaced
		newInstr->insertAfter(currentInstr);
		inst_iterator helper(*Iiterator);
		*Iiterator = ++helper;
		EraseInstructions(currentInstr, rw_rule.foundInstrs.size());
		logger.log_insertion(rw_rule.foundInstrs, rw_rule.newInstr.instruction);
	}
}
Exemplo n.º 4
0
void DuettoNativeRewriter::rewriteConstructorImplementation(Module& M, Function& F)
{
	//Copy the code in a function with the right signature
	Function* newFunc=getReturningConstructor(M, &F);
	if(!newFunc->empty())
		return;

	//Visit each instruction and take note of the ones that needs to be replaced
	Function::const_iterator B=F.begin();
	Function::const_iterator BE=F.end();
	ValueToValueMapTy valueMap;
	CallInst* lowerConstructor = NULL;
	const CallInst* oldLowerConstructor = NULL;
	for(;B!=BE;++B)
	{
		BasicBlock::const_iterator I=B->begin();
		BasicBlock::const_iterator IE=B->end();
		for(;I!=IE;++I)
		{
			if(I->getOpcode()!=Instruction::Call)
				continue;
			const CallInst* callInst=cast<CallInst>(&(*I));
			Function* f=callInst->getCalledFunction();
			if(!f)
				continue;
			const char* startOfType;
			const char* endOfType;
			if(!DuettoNativeRewriter::isBuiltinConstructor(f->getName().data(), startOfType, endOfType))
				continue;
			//Check that the constructor is for 'this'
			if(callInst->getOperand(0)!=F.arg_begin())
				continue;
			//If this is another constructor for the same type, change it to a
			//returning constructor and use it as the 'this' argument
			Function* newFunc = getReturningConstructor(M, f);
			llvm::SmallVector<Value*, 4> newArgs;
			for(unsigned i=1;i<callInst->getNumArgOperands();i++)
				newArgs.push_back(callInst->getArgOperand(i));
			lowerConstructor = CallInst::Create(newFunc, newArgs);
			oldLowerConstructor = callInst;
			break;
		}
		if(lowerConstructor)
			break;
	}

	//Clone the linkage first
	newFunc->setLinkage(F.getLinkage());
	Function::arg_iterator origArg=++F.arg_begin();
	Function::arg_iterator newArg=newFunc->arg_begin();
	valueMap.insert(make_pair(F.arg_begin(), lowerConstructor));

	for(unsigned i=1;i<F.arg_size();i++)
	{
		valueMap.insert(make_pair(&(*origArg), &(*newArg)));
		++origArg;
		++newArg;
	}
	SmallVector<ReturnInst*, 4> returns;
	CloneFunctionInto(newFunc, &F, valueMap, false, returns);

	//Find the right place to add the base construtor call
	assert(lowerConstructor->getNumArgOperands()<=1 && "Native constructors with multiple args are not supported");
	Instruction* callPred = NULL;
	if (lowerConstructor->getNumArgOperands()==1 && Instruction::classof(lowerConstructor->getArgOperand(0)))
	{
		//Switch the argument to the one in the new func
		lowerConstructor->setArgOperand(0, valueMap[lowerConstructor->getArgOperand(0)]);
		callPred = cast<Instruction>(lowerConstructor->getArgOperand(0));
	}
	else
		callPred = &newFunc->getEntryBlock().front();

	//Add add it
	lowerConstructor->insertAfter(callPred);

	//Override the returs values
	for(unsigned i=0;i<returns.size();i++)
	{
		Instruction* newInst = ReturnInst::Create(M.getContext(),lowerConstructor);
		newInst->insertBefore(returns[i]);
		returns[i]->removeFromParent();
	}
	//Recursively move all the users of the lower constructor after the call itself
	Instruction* insertPoint = lowerConstructor->getNextNode();
	SmallVector<Value*, 4> usersQueue(lowerConstructor->getNumUses());
	unsigned int i;
	Value::use_iterator it;
	for(i=usersQueue.size()-1,it=lowerConstructor->use_begin();it!=lowerConstructor->use_end();++it,i--)
		usersQueue[i]=it->getUser();

	SmallSet<Instruction*, 4> movedInstructions;
	while(!usersQueue.empty())
	{
		Instruction* cur=dyn_cast<Instruction>(usersQueue.pop_back_val());
		if(!cur)
			continue;
		if(movedInstructions.count(cur))
			continue;
		movedInstructions.insert(cur);
		cur->moveBefore(insertPoint);
		//Add users of this instrucution as well
		usersQueue.resize(usersQueue.size()+cur->getNumUses());
		for(i=usersQueue.size()-1,it=cur->use_begin();it!=cur->use_end();++it,i--)
			usersQueue[i]=it->getUser();
	}
	cast<Instruction>(valueMap[oldLowerConstructor])->eraseFromParent();
}
Exemplo n.º 5
0
bool TypeChecksOpt::runOnModule(Module &M) {
  TS = &getAnalysis<dsa::TypeSafety<TDDataStructures> >();

  // Create the necessary prototypes
  VoidTy = IntegerType::getVoidTy(M.getContext());
  Int8Ty = IntegerType::getInt8Ty(M.getContext());
  Int32Ty = IntegerType::getInt32Ty(M.getContext());
  Int64Ty = IntegerType::getInt64Ty(M.getContext());
  VoidPtrTy = PointerType::getUnqual(Int8Ty);
  TypeTagTy = Int8Ty;
  TypeTagPtrTy = PointerType::getUnqual(TypeTagTy);

  Constant *memsetF = M.getOrInsertFunction ("llvm.memset.i64", VoidTy,
                                             VoidPtrTy,
                                             Int8Ty,
                                             Int64Ty,
                                             Int32Ty,
                                             NULL);
  trackGlobal = M.getOrInsertFunction("trackGlobal",
                                      VoidTy,
                                      VoidPtrTy,/*ptr*/
                                      TypeTagTy,/*type*/
                                      Int64Ty,/*size*/
                                      Int32Ty,/*tag*/
                                      NULL);
  trackInitInst = M.getOrInsertFunction("trackInitInst",
                                        VoidTy,
                                        VoidPtrTy,/*ptr*/
                                        Int64Ty,/*size*/
                                        Int32Ty,/*tag*/
                                        NULL);
  trackUnInitInst = M.getOrInsertFunction("trackUnInitInst",
                                          VoidTy,
                                          VoidPtrTy,/*ptr*/
                                          Int64Ty,/*size*/
                                          Int32Ty,/*tag*/
                                          NULL);
  trackStoreInst = M.getOrInsertFunction("trackStoreInst",
                                         VoidTy,
                                         VoidPtrTy,/*ptr*/
                                         TypeTagTy,/*type*/
                                         Int64Ty,/*size*/
                                         Int32Ty,/*tag*/
                                         NULL);
  checkTypeInst = M.getOrInsertFunction("checkType",
                                        VoidTy,
                                        TypeTagTy,/*type*/
                                        Int64Ty,/*size*/
                                        TypeTagPtrTy,
                                        VoidPtrTy,/*ptr*/
                                        Int32Ty,/*tag*/
                                        NULL);
  copyTypeInfo = M.getOrInsertFunction("copyTypeInfo",
                                       VoidTy,
                                       VoidPtrTy,/*dest ptr*/
                                       VoidPtrTy,/*src ptr*/
                                       Int64Ty,/*size*/
                                       Int32Ty,/*tag*/
                                       NULL);
  setTypeInfo = M.getOrInsertFunction("setTypeInfo",
                                      VoidTy,
                                      VoidPtrTy,/*dest ptr*/
                                      TypeTagPtrTy,/*metadata*/
                                      Int64Ty,/*size*/
                                      TypeTagTy,
                                      VoidPtrTy,
                                      Int32Ty,/*tag*/
                                      NULL);
  trackStringInput = M.getOrInsertFunction("trackStringInput",
                                           VoidTy,
                                           VoidPtrTy,
                                           Int32Ty,
                                           NULL);
  getTypeTag = M.getOrInsertFunction("getTypeTag",
                                     VoidTy,
                                     VoidPtrTy, /*ptr*/
                                     Int64Ty, /*size*/
                                     TypeTagPtrTy, /*dest for type tag*/
                                     Int32Ty, /*tag*/
                                     NULL);
  MallocFunc = M.getFunction("malloc");

  for(Value::use_iterator User = trackGlobal->use_begin(); User != trackGlobal->use_end(); ++User) {
    CallInst *CI = dyn_cast<CallInst>(*User);
    assert(CI);
    if(TS->isTypeSafe(CI->getOperand(1)->stripPointerCasts(), CI->getParent()->getParent())) {
      std::vector<Value*>Args;
      Args.push_back(CI->getOperand(1));
      Args.push_back(CI->getOperand(3));
      Args.push_back(CI->getOperand(4));
      CallInst::Create(trackInitInst, Args, "", CI);
      toDelete.push_back(CI);
    }
  }

  for(Value::use_iterator User = checkTypeInst->use_begin(); User != checkTypeInst->use_end(); ++User) {
    CallInst *CI = dyn_cast<CallInst>(*User);
    assert(CI);

    if(TS->isTypeSafe(CI->getOperand(4)->stripPointerCasts(), CI->getParent()->getParent())) {
      toDelete.push_back(CI);
    }
  }

  for(Value::use_iterator User = trackStoreInst->use_begin(); User != trackStoreInst->use_end(); ++User) {
    CallInst *CI = dyn_cast<CallInst>(*User);
    assert(CI);

    if(TS->isTypeSafe(CI->getOperand(1)->stripPointerCasts(), CI->getParent()->getParent())) {
      toDelete.push_back(CI);
    }
  }

  // for alloca's if they are type known
  // assume initialized with TOP
  for(Value::use_iterator User = trackUnInitInst->use_begin(); User != trackUnInitInst->use_end(); ) {
    CallInst *CI = dyn_cast<CallInst>(*(User++));
    assert(CI);

    // check if operand is an alloca inst.
    if(TS->isTypeSafe(CI->getOperand(1)->stripPointerCasts(), CI->getParent()->getParent())) {
      CI->setCalledFunction(trackInitInst);

      if(AllocaInst *AI = dyn_cast<AllocaInst>(CI->getOperand(1)->stripPointerCasts())) {
        // Initialize the allocation to NULL
        std::vector<Value *> Args2;
        Args2.push_back(CI->getOperand(1));
        Args2.push_back(ConstantInt::get(Int8Ty, 0));
        Args2.push_back(CI->getOperand(2));
        Args2.push_back(ConstantInt::get(Int32Ty, AI->getAlignment()));
        CallInst::Create(memsetF, Args2, "", CI);
      }
    }
  }

  if(MallocFunc) {
    for(Value::use_iterator User = MallocFunc->use_begin(); User != MallocFunc->use_end(); User ++) {
      CallInst *CI = dyn_cast<CallInst>(*User);
      if(!CI)
        continue;
      if(TS->isTypeSafe(CI, CI->getParent()->getParent())){
        CastInst *BCI = BitCastInst::CreatePointerCast(CI, VoidPtrTy);
        CastInst *Size = CastInst::CreateSExtOrBitCast(CI->getOperand(1), Int64Ty);
        Size->insertAfter(CI);
        BCI->insertAfter(Size);
        std::vector<Value *>Args;
        Args.push_back(BCI);
        Args.push_back(Size);
        Args.push_back(ConstantInt::get(Int32Ty, 0));
        CallInst *CINew = CallInst::Create(trackInitInst, Args);
        CINew->insertAfter(BCI);
      }
    }
  }

  // also do for mallocs/calloc/other allocators???
  // other allocators??

  for(Value::use_iterator User = copyTypeInfo->use_begin(); User != copyTypeInfo->use_end(); ++User) {
    CallInst *CI = dyn_cast<CallInst>(*User);
    assert(CI);

    if(TS->isTypeSafe(CI->getOperand(1)->stripPointerCasts(), CI->getParent()->getParent())) {
      std::vector<Value*> Args;
      Args.push_back(CI->getOperand(1));
      Args.push_back(CI->getOperand(3)); // size
      Args.push_back(CI->getOperand(4));
      CallInst::Create(trackInitInst, Args, "", CI);
      toDelete.push_back(CI);
    }
  }
  for(Value::use_iterator User = setTypeInfo->use_begin(); User != setTypeInfo->use_end(); ++User) {
    CallInst *CI = dyn_cast<CallInst>(*User);
    assert(CI);

    if(TS->isTypeSafe(CI->getOperand(1)->stripPointerCasts(), CI->getParent()->getParent())) {
      std::vector<Value*> Args;
      Args.push_back(CI->getOperand(1));
      Args.push_back(CI->getOperand(3)); // size
      Args.push_back(CI->getOperand(6));
      CallInst::Create(trackInitInst, Args, "", CI);
      toDelete.push_back(CI);
    }
  }

  for(Value::use_iterator User = getTypeTag->use_begin(); User != getTypeTag->use_end(); ++User) {
    CallInst *CI = dyn_cast<CallInst>(*User);
    assert(CI);
    if(TS->isTypeSafe(CI->getOperand(1)->stripPointerCasts(), CI->getParent()->getParent())) {
      AllocaInst *AI = dyn_cast<AllocaInst>(CI->getOperand(3)->stripPointerCasts());
      assert(AI);
      std::vector<Value*>Args;
      Args.push_back(CI->getOperand(3));
      Args.push_back(ConstantInt::get(Int8Ty, 255));
      Args.push_back(CI->getOperand(2));
      Args.push_back(ConstantInt::get(Int32Ty, AI->getAlignment()));
      CallInst::Create(memsetF, Args, "", CI);
      toDelete.push_back(CI);
    }
  }

  numSafe += toDelete.size();

  while(!toDelete.empty()) {
    Instruction *I = toDelete.back();
    toDelete.pop_back();
    I->eraseFromParent();
  }

  return (numSafe > 0);
}
Exemplo n.º 6
0
bool partition::runOnLoop(Loop* L, LPPassManager &LPM) {
    
    errs() << "***************************  Loop encountered: ************************" << '\n' << L->getHeader()->getName() << '\n';
    
    if (function->getName() != "main")
        return false;


    IntegerType* int32Ty = Type::getInt32Ty(*context);
    IntegerType* int64Ty = Type::getInt64Ty(*context);
    PointerType* voidPtrTy = Type::getInt8PtrTy(*context);

    FunctionType* funcTy = FunctionType::get(int32Ty, false);
   
    Constant* func1_c;
    Function* func1;

    func1_c = module->getOrInsertFunction("func1", funcTy);
    func1 = cast<Function>(func1_c);


    Function* pro = module->getFunction("produce");
    Function* con = module->getFunction("consume");
    
    BasicBlock* func1EntryBlock = BasicBlock::Create(*context, "entry.func1", func1);
    AllocaInst* i_var = new AllocaInst(int32Ty, NULL, 4, "i", func1EntryBlock);
    
    Value* liveIn;
    BasicBlock *forCond, *forBody, *forInc;
    ValueToValueMapTy VMap;
    std::map<BasicBlock *, BasicBlock *> BlockMap;
    
    for (Loop::block_iterator BB = L->block_begin(), BBe = L->block_end(); BB != BBe; ++BB) {
        BasicBlock* func1Block = CloneBasicBlock(*BB, VMap, ".func1", func1);
        BlockMap[*BB] = func1Block;

        if ((*BB)->getName() == "for.cond") 
            forCond = func1Block;
        if ((*BB)->getName() == "for.body") 
            forBody = func1Block;
        if ((*BB)->getName() == "for.inc") 
            forInc = func1Block;

        for (BasicBlock::iterator it = func1Block->begin(), ite = func1Block->end(); it != ite; ++it) {
            for (User::op_iterator oit = it->op_begin(), oite = it->op_end(); oit != oite; ++oit) {
                if (VMap[*oit] != NULL) {
                    *oit = VMap[*oit];
                } else {
                    Constant* cons = dyn_cast<Constant>(*oit);
                    BranchInst* br = dyn_cast<BranchInst>(it);
                    if (cons == NULL && br == NULL) {
                        liveIn = *oit;
                        *oit = i_var;
                    }
                }
               
            }
        }

        if ((*BB)->getName() == "for.body") {
            Instruction* term = (*BB)->getTerminator();
            term->removeFromParent();
            for (int i = 0; i < 7; i++) {
                (*BB)->back().eraseFromParent();
            }
            term->insertAfter(&(*BB)->back());
            (*BB)->front().eraseFromParent();
            LoadInst* load = new LoadInst(liveIn, "", false, 4, term); 

            std::vector<Value *> produce_args;
            CastInst* cast = CastInst::CreateIntegerCast(load, int64Ty, true);
            cast->insertAfter(load);
            produce_args.push_back(cast);
            ConstantInt* val = ConstantInt::get(int32Ty, (uint32_t) 3);
            produce_args.push_back(val);
            CallInst::Create(pro, ArrayRef<Value*>(produce_args), "", term);

            produce_args.pop_back();
            val = ConstantInt::get(int32Ty, (uint32_t) 2);
            produce_args.push_back(val);
            CallInst::Create(pro, ArrayRef<Value*>(produce_args), "", term);
        }
    
    }

    // set branch instructions to restructure the CFG in created function
    BasicBlock* func1EndBlock = BasicBlock::Create(*context, "if.end.func1", func1); 
    BasicBlock* garbageBB = BasicBlock::Create(*context, "garbage", func1);
    ConstantInt* retVal_g = ConstantInt::get(int32Ty, (uint32_t) 0);
    ReturnInst* ret_g = ReturnInst::Create(*context, retVal_g, garbageBB);

    
    for (Function::iterator fit = func1->begin(), fite = func1->end(); fit != fite; ++fit) {
        if (fit->getTerminator() == NULL || fit->getName() == "garbage")
            continue;
      
        BranchInst* br = dyn_cast<BranchInst>(fit->getTerminator());
        int numSuccessors = br->getNumSuccessors();
        
        for (int i = 0; i < numSuccessors; i++) {
            BasicBlock* successor = br->getSuccessor(i);
            
            if (BlockMap[successor] != NULL) {
                
                br->setSuccessor(i, BlockMap[successor]);
            } 
            else {
                br->setSuccessor(i, func1EndBlock);
            }
            
        }
/*
        if (fit->getName() == "for.body.func1") {
            for (int i = 0; i < 4; i++) {
                BasicBlock::iterator it = fit->begin();
                it->moveBefore(ret_g);
            }
        }
        */
    }
    garbageBB->eraseFromParent();

    BranchInst* br = dyn_cast<BranchInst>(forBody->getTerminator());
    br->setSuccessor(0, forCond);
    forInc->eraseFromParent();


    // Create return instruction for func1EndBlock and set a branch from loop header to func1EndBlock
    ConstantInt* retVal = ConstantInt::get(int32Ty, (uint32_t) 0);
    ReturnInst* ret1 = ReturnInst::Create(*context, retVal, func1EndBlock);
    BasicBlock* loopHeader = BlockMap.at(L->getHeader());
    BranchInst* brInst = BranchInst::Create(loopHeader, func1EntryBlock);
    
    // add produce function call
    std::vector<Value *> produce_args;
    ConstantInt* val = ConstantInt::get(int64Ty, (uint64_t) 0);
    produce_args.push_back(val);
    val = ConstantInt::get(int32Ty, (uint32_t) 5);
    produce_args.push_back(val);
    CallInst::Create(pro, ArrayRef<Value*>(produce_args), "", func1EndBlock->getTerminator());
    
    // add consume function call
    int q_id = 2;
    for (Value::use_iterator uit = i_var->use_begin(), uite = i_var->use_end(); uit != uite; ++uit) {
        std::vector<Value *> consume_args;
        ConstantInt* val = ConstantInt::get(int32Ty, (uint32_t) q_id); 
        consume_args.push_back(val);
        CallInst* call = CallInst::Create(con, ArrayRef<Value*>(consume_args));
        Instruction* inst = dyn_cast<Instruction>(*uit);
        call->insertAfter(inst);
        CastInst* cast = CastInst::CreateIntegerCast(call, int32Ty, true);
        cast->insertAfter(call);
        (*uit)->replaceAllUsesWith(cast);
        inst->eraseFromParent();
        q_id++;
    }

    i_var->eraseFromParent();

    // add produce and consume function calls to main thread
    // transmit the function pointer to created function by a produce call
    BasicBlock* loopPreheader = L->getLoopPreheader();
    produce_args.clear();
    CastInst* cast = CastInst::CreatePointerCast(func1, int64Ty);
    cast->insertBefore(loopPreheader->getTerminator());
    produce_args.push_back(cast);
    val = ConstantInt::get(int32Ty, (uint32_t) 0);
    produce_args.push_back(val);
    CallInst::Create(pro, ArrayRef<Value*>(produce_args), "", loopPreheader->getTerminator());
  
    // transmit induction variable to created function by a produce call
    Instruction* load = &L->getHeader()->front();
    produce_args.clear();
    cast = CastInst::CreateIntegerCast(load, int64Ty, true);
    cast->insertAfter(load);
    produce_args.push_back(cast);
    val = ConstantInt::get(int32Ty, (uint32_t) 4);
    produce_args.push_back(val);
    CallInst::Create(pro, ArrayRef<Value*>(produce_args))->insertAfter(cast);



    return true;
}