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. }
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; }