Beispiel #1
0
void llvm::PointerMayBeCaptured(const Value *V, CaptureTracker *Tracker) {
  assert(V->getType()->isPointerTy() && "Capture is for pointers only!");
  SmallVector<Use*, Threshold> Worklist;
  SmallSet<Use*, Threshold> Visited;
  int Count = 0;

  for (Value::const_use_iterator UI = V->use_begin(), UE = V->use_end();
       UI != UE; ++UI) {
    // If there are lots of uses, conservatively say that the value
    // is captured to avoid taking too much compile time.
    if (Count++ >= Threshold)
      return Tracker->tooManyUses();

    Use *U = &UI.getUse();
    if (!Tracker->shouldExplore(U)) continue;
    Visited.insert(U);
    Worklist.push_back(U);
  }

  while (!Worklist.empty()) {
    Use *U = Worklist.pop_back_val();
    Instruction *I = cast<Instruction>(U->getUser());
    V = U->get();

    switch (I->getOpcode()) {
    case Instruction::Call:
    case Instruction::Invoke: {
      CallSite CS(I);
      // Not captured if the callee is readonly, doesn't return a copy through
      // its return value and doesn't unwind (a readonly function can leak bits
      // by throwing an exception or not depending on the input value).
      if (CS.onlyReadsMemory() && CS.doesNotThrow() && I->getType()->isVoidTy())
        break;

      // Not captured if only passed via 'nocapture' arguments.  Note that
      // calling a function pointer does not in itself cause the pointer to
      // be captured.  This is a subtle point considering that (for example)
      // the callee might return its own address.  It is analogous to saying
      // that loading a value from a pointer does not cause the pointer to be
      // captured, even though the loaded value might be the pointer itself
      // (think of self-referential objects).
      CallSite::arg_iterator B = CS.arg_begin(), E = CS.arg_end();
      for (CallSite::arg_iterator A = B; A != E; ++A)
        if (A->get() == V && !CS.doesNotCapture(A - B))
          // The parameter is not marked 'nocapture' - captured.
          if (Tracker->captured(U))
            return;
      break;
    }
    case Instruction::Load:
      // Loading from a pointer does not cause it to be captured.
      break;
    case Instruction::VAArg:
      // "va-arg" from a pointer does not cause it to be captured.
      break;
    case Instruction::Store:
      if (V == I->getOperand(0))
        // Stored the pointer - conservatively assume it may be captured.
        if (Tracker->captured(U))
          return;
      // Storing to the pointee does not cause the pointer to be captured.
      break;
    case Instruction::BitCast:
    case Instruction::GetElementPtr:
    case Instruction::PHI:
    case Instruction::Select:
      // The original value is not captured via this if the new value isn't.
      for (Instruction::use_iterator UI = I->use_begin(), UE = I->use_end();
           UI != UE; ++UI) {
        Use *U = &UI.getUse();
        if (Visited.insert(U))
          if (Tracker->shouldExplore(U))
            Worklist.push_back(U);
      }
      break;
    case Instruction::ICmp:
      // Don't count comparisons of a no-alias return value against null as
      // captures. This allows us to ignore comparisons of malloc results
      // with null, for example.
      if (ConstantPointerNull *CPN =
          dyn_cast<ConstantPointerNull>(I->getOperand(1)))
        if (CPN->getType()->getAddressSpace() == 0)
          if (isNoAliasCall(V->stripPointerCastsSafe()))
            break;
      // Otherwise, be conservative. There are crazy ways to capture pointers
      // using comparisons.
      if (Tracker->captured(U))
        return;
      break;
    default:
      // Something else - be conservative and say it is captured.
      if (Tracker->captured(U))
        return;
      break;
    }
  }

  // All uses examined.
}
Beispiel #2
0
set<Strator::StratorWorker::LockSet>& Strator::StratorWorker::traverseStatement(const Function& f, 
		const BasicBlock::const_iterator& inst,
		const Strator::StratorWorker::LockSet& entryLockSet,
		Strator::StratorWorker::LockSet lockSet){
//	cerr << "@: ";
//	llvm::errs() << *inst;
//	cerr << endl;
	//if (f.getName() == "queueDelete")
	//	printMultithreaded();
	if(stratorStatementMap.find(&(*inst)) == stratorStatementMap.end()){
		stratorStatementMap[&(*inst)] = new StratorStatement();
	}
	StratorStatement* sStmt= stratorStatementMap[&(*inst)];

	/// Check the statement cache, if the lockset is in the cache, don't analyze
	if(sStmt->statementCache.isInCache(entryLockSet, lockSet)){
		set<Strator::StratorWorker::LockSet>* emptySet = new set<Strator::StratorWorker::LockSet>();
		return *emptySet;
	}

	/// Otherwise add (entryLockSet, lockSet) to the cache
	sStmt->statementCache.addToCache(entryLockSet, lockSet);

	/// Terminator instructions for our analysis:
	/// Ret
	/// IndirectBr: Probably this is one such instruction for us,
	/// as we evaluate more systems, we'll see what happens if we use it
	if(inst->getOpcode() == Instruction::IndirectBr){
		printLocation(*inst);
		assert(false && "We hit an indirect branch\n");
	}

	if(inst->getOpcode() == Instruction::Ret){
		set<Strator::StratorWorker::LockSet>* returnSet = new set<Strator::StratorWorker::LockSet>();
		returnSet->insert(lockSet);
		return *returnSet;
	}

#undef DEBUG_TYPE
#define DEBUG_TYPE "strator-parval"
	if(inst->getOpcode() == Instruction::BitCast){
		DEBUG(errs() << *inst << " = " << *inst->getOperand(0) << "\n");
		parentValue[&*inst] = inst->getOperand(0);
	}

	vector<LockSet>* workingList = new vector<StratorWorker::LockSet>();

	/// TODO: What do we do with invoke?
	if(inst->getOpcode() == Instruction::Call) {
		const CallInst* callInst = dyn_cast<CallInst>(&(*inst));
		assert(callInst && "Call inst pointer is NULL!!!, this shouldn't have happened");
		Function* calledFunc = callInst->getCalledFunction();

		StratorFunction* sFunc = NULL;
		if(UpwardPropagationLevel != 0 ){
			sFunc = getStratorFunction(calledFunc);
			sFunc->currentCaller = &f;
		}

		/// Since we don't have a lock intrinsic or anything like that,
		/// we will discover locks and unlocks upon function calls.
		assert((((int)inst->getNumOperands())-1) >= 0 &&
				"Something wrong with the operands of call instruction");
		if(isMultiThreadedAPI(inst->getOperand(inst->getNumOperands() -1 ))){
			multithreadedFunctionMap[f.getName().str()] = true;
			/// Now check if we also want to propagete the "multithreadedness" to callers:
			/// If the called function is a thread-related function, then the parent function(s)
			/// are added to a a multithreadedAPI set depending on the propagation level which is
			/// checked in subsequent calls to isMultiThreadedAPI
			if(UpwardPropagationLevel != 0){
				/// move up to the parents of this call to mark all of them as multithreaded
				for(unsigned i=0; i<UpwardPropagationLevel; ++i){
					const Function* caller = sFunc->currentCaller;
					if(!caller)
						break;
					else {
						/// The upper level function becomes a multithreaded API, son any
						/// subsequent caller of such functions also become multithreaded.
						multithreadedAPISet.insert(caller);
						/// They also become multithreaded functions themselves
						multithreadedFunctionMap[caller->getName().str()] = true;
						sFunc = getStratorFunction(caller);
					}
				}
			}
		}

		if(calledFunc){
			if (isLock(calledFunc)){
				Lock l = getLockValue(&(*inst));

				if(PrintLockUnlock)
					cerr << "lock inst:" << l << endl;

				if(l)
					lockSet.insert(l);
			} else if (isUnLock(calledFunc)){
				Lock l = getLockValue(&(*inst));

				if(PrintLockUnlock)
					cerr << "unlock inst:" << l << endl;

				if(l)
					lockSet.erase(l);
			} else{
				bool atThreadCreationFunction = false;
				/// Now check if we have a resolved call, that is a direct function call
				/// We have a resolved function call
				if(isThreadCreationFunction(calledFunc->getName().str())) {
					/// If the created function is a thread creation function we
					/// need to find the worker function operand and traverse it
					atThreadCreationFunction = true;
					calledFunc = getThreadWorkerFunction(callInst);
				}

				/// Check if the current function is a multithreaded API
				if(multithreadedFunctionMap[f.getName().str()]){
					/// If so, the current callee also becomes multithreaded
					multithreadedFunctionMap[calledFunc->getName().str()] = true;
				}

#undef DEBUG_TYPE
#define DEBUG_TYPE "strator-parval"
				/// We pass some values to the called function, updating flow sensitive value map
				CallSite cs(const_cast<CallInst*>(callInst));
				Function::arg_iterator calleeIt;
				CallSite::arg_iterator callerIt;
				if(atThreadCreationFunction) {
					/// this is only supported for pthread right now
					assert(cs.arg_size() == 4 && "pthread_create should have 4 operands!");
					assert(calledFunc->arg_size() == 1 && "pthread thread worker function should have 1 operand");
					DEBUG(errs() << *calledFunc->arg_begin() << " = " <<  *cs.getArgument(3) << "\n");
					parentValue[calledFunc->arg_begin()] = cs.getArgument(3);
				}else{
					for (callerIt = cs.arg_begin(), calleeIt = calledFunc->arg_begin();
							callerIt != cs.arg_end() && calleeIt != calledFunc->arg_end();
							callerIt++, calleeIt++){
							DEBUG(errs() <<  *calleeIt << " = " << *(callerIt->get()) << "\n");
							parentValue[calleeIt] = callerIt->get();
					}
				}


				set<StratorWorker::LockSet>& functionLockSets = traverseFunction(*calledFunc, lockSet);
				/// Maybe the called function was multi threaded: If so, the caller becomes multi
				/// threaded from this point on if the following two lines are uncommented.
				/// However, this support adds a large number of false positives
				/*
          if(multithreadedFunctionMap[calledFunc->getName().str()])
          multithreadedFunctionMap[f.getName().str()] = true;
				 */
				/// arguments of callee are no longer valid, updating flow sensitive value map
				for (calleeIt = calledFunc->arg_begin(); calleeIt != calledFunc->arg_end(); calleeIt++){
					parentValue.erase(calleeIt);
				}

				workingList->insert(workingList->begin(), functionLockSets.begin(), functionLockSets.end());
			}
		} else {
			/// call is not resolved, process the lockset at hand as the working set
			workingList->insert(workingList->begin(), lockSet);
		}
	}

	/// TODO: We can leverage the llvm function onlyreadsmemory maybe? At least look at the code
	if(inst->getOpcode() == Instruction::Load || inst->getOpcode() == Instruction::Store){
		detectRaces(*inst, (inst->getOpcode() == Instruction::Store), lockSet);
	}

	/// If this is the last instruction of the current BB
	/// it means from here we have successor nodes in the control flow.
	set<StratorWorker::LockSet>* returnSet = new set<StratorWorker::LockSet>();
	BasicBlock* bb = const_cast<BasicBlock*>(inst->getParent());
	/// TODO: try to check for terminator instruction here
	vector<StratorWorker::LockSet>::iterator workingListLockset;

	if(workingList->size() > 0){
		if(&(bb->back()) == &(*inst)){
			for(workingListLockset = workingList->begin();
					workingListLockset != workingList->end(); ++workingListLockset)
				for(succ_iterator child = succ_begin(bb), end = succ_end(bb);
						child != end; ++child){
					BasicBlock::iterator firstInstr = child->begin();
					set<Strator::StratorWorker::LockSet>& statementLockSets = traverseStatement(f, firstInstr, entryLockSet, *workingListLockset);
					returnSet->insert(statementLockSets.begin(), statementLockSets.end());
				}
		} else{
			/// not the last instruction, simply instruction pointer to the next instruction
			BasicBlock::const_iterator nextInst = inst;
			++nextInst;
			for(workingListLockset = workingList->begin();
					workingListLockset != workingList->end(); ++workingListLockset){
				set<Strator::StratorWorker::LockSet>& statementLockSets = traverseStatement(f, nextInst, entryLockSet, *workingListLockset);
				returnSet->insert(statementLockSets.begin(), statementLockSets.end());
			}
		}
	} else{
		if(&(bb->back()) == &(*inst)){
			for(succ_iterator child = succ_begin(bb), end = succ_end(bb);
					child != end; ++child){
				BasicBlock::iterator firstInstr = child->begin();
				set<LockSet>& statementLockSets = traverseStatement(f, firstInstr, entryLockSet, lockSet);

				returnSet->insert(statementLockSets.begin(), statementLockSets.end());
			}
		} else{
			BasicBlock::const_iterator nextInst = inst;
			++nextInst;
			set<Strator::StratorWorker::LockSet>& statementLockSets = traverseStatement(f, nextInst, entryLockSet, lockSet);

			returnSet->insert(statementLockSets.begin(), statementLockSets.end());
		}
	}

	return *returnSet;
}