void TracingNoGiri::visitCallInst(CallInst &CI) { // Attempt to get the called function. Function *CalledFunc = CI.getCalledFunction(); if (!CalledFunc) return; // Do not instrument calls to tracing run-time functions or debug functions. if (isTracerFunction(CalledFunc)) return; if (!CalledFunc->getName().str().compare(0,9,"llvm.dbg.")) return; // Instrument external calls which can have invariants on its return value if (CalledFunc->isDeclaration() && CalledFunc->isIntrinsic()) { // Instrument special external calls which loads/stores // e.g. strlen(), strcpy(), memcpy() etc. visitSpecialCall(CI); return; } // If the called value is inline assembly code, then don't instrument it. if (isa<InlineAsm>(CI.getCalledValue()->stripPointerCasts())) return; instrumentLock(&CI); // Get the ID of the store instruction. Value *CallID = ConstantInt::get(Int32Type, lsNumPass->getID(&CI)); // Get the called function value and cast it to a void pointer. Value *FP = castTo(CI.getCalledValue(), VoidPtrType, "", &CI); // Create the call to the run-time to record the call instruction. std::vector<Value *> args = make_vector<Value *>(CallID, FP, 0); // Do not add calls to function call stack for external functions // as return records won't be used/needed for them, so call a special record function // FIXME!!!! Do we still need it after adding separate return records???? Instruction *RC; if (CalledFunc->isDeclaration()) RC = CallInst::Create(RecordExtCall, args, "", &CI); else RC = CallInst::Create(RecordCall, args, "", &CI); instrumentUnlock(RC); // Create the call to the run-time to record the return of call instruction. CallInst *CallInst = CallInst::Create(RecordReturn, args, "", &CI); CI.moveBefore(CallInst); instrumentLock(CallInst); instrumentUnlock(CallInst); ++NumCalls; // Update statistics // The best way to handle external call is to set a flag before calling ext fn and // use that to determine if an internal function is called from ext fn. It flag can be // reset afterwards and restored to its original value before returning to ext code. // FIXME!!!! LATER #if 0 if (CalledFunc->isDeclaration() && CalledFunc->getName().str() == "pthread_create") { // If pthread_create is called then handle it specially as it calls // functions externally and add an extra call for the externally // called functions with the same id so that returns can match with it. // In addition to a function call to pthread_create. // Get the external function pointer operand and cast it to a void pointer Value *FP = castTo(CI.getOperand(2), VoidPtrType, "", &CI); // Create the call to the run-time to record the call instruction. std::vector<Value *> argsExt = make_vector<Value *>(CallID, FP, 0); CallInst = CallInst::Create(RecordCall, argsExt, "", &CI); CI.moveBefore(CallInst); // Update statistics ++Calls; // For, both external functions and internal/ext functions called from // external functions, return records are not useful as they won't be used. // Since, we won't create return records for them, simply update the call // stack to mark the end of function call. //args = make_vector<Value *>(CallID, FP, 0); //CallInst::Create(RecordExtCallRet, args.begin(), args.end(), "", &CI); // Create the call to the run-time to record the return of call instruction. CallInst::Create(RecordReturn, argsExt, "", &CI); } #endif // Instrument special external calls which loads/stores // like strlen, strcpy, memcpy etc. visitSpecialCall(CI); }
/// performLocalRetainMotion - Scan forward from the specified retain, moving it /// later in the function if possible, over instructions that provably can't /// release the object. If we get to a release of the object, zap both. /// /// NOTE: this handles both objc_retain and swift_retain. /// static bool performLocalRetainMotion(CallInst &Retain, BasicBlock &BB, SwiftRCIdentity *RC) { // FIXME: Call classifier should identify the object for us. Too bad C++ // doesn't have nice Swift-style enums. Value *RetainedObject = RC->getSwiftRCIdentityRoot(Retain.getArgOperand(0)); BasicBlock::iterator BBI = Retain.getIterator(), BBE = BB.getTerminator()->getIterator(); bool isObjCRetain = Retain.getCalledFunction()->getName() == "objc_retain"; bool MadeProgress = false; // Scan until we get to the end of the block. for (++BBI; BBI != BBE; ++BBI) { Instruction &CurInst = *BBI; // Classify the instruction. This switch does a "break" when the instruction // can be skipped and is interesting, and a "continue" when it is a retain // of the same pointer. switch (classifyInstruction(CurInst)) { // These instructions should not reach here based on the pass ordering. // i.e. LLVMARCOpt -> LLVMContractOpt. case RT_RetainN: case RT_UnknownRetainN: case RT_BridgeRetainN: case RT_ReleaseN: case RT_UnknownReleaseN: case RT_BridgeReleaseN: llvm_unreachable("These are only created by LLVMARCContract !"); case RT_NoMemoryAccessed: case RT_AllocObject: case RT_CheckUnowned: // Skip over random instructions that don't touch memory. They don't need // protection by retain/release. break; case RT_FixLifetime: // This only stops release motion. Retains can move over it. break; case RT_Retain: case RT_UnknownRetain: case RT_BridgeRetain: case RT_RetainUnowned: case RT_ObjCRetain: { // swift_retain(obj) //CallInst &ThisRetain = cast<CallInst>(CurInst); //Value *ThisRetainedObject = ThisRetain.getArgOperand(0); // If we see a retain of the same object, we can skip over it, but we // can't count it as progress. Just pushing a retain(x) past a retain(y) // doesn't change the program. continue; } case RT_UnknownRelease: case RT_BridgeRelease: case RT_ObjCRelease: case RT_Release: { // If we get to a release that is provably to this object, then we can zap // it and the retain. CallInst &ThisRelease = cast<CallInst>(CurInst); Value *ThisReleasedObject = ThisRelease.getArgOperand(0); ThisReleasedObject = RC->getSwiftRCIdentityRoot(ThisReleasedObject); if (ThisReleasedObject == RetainedObject) { Retain.eraseFromParent(); ThisRelease.eraseFromParent(); if (isObjCRetain) { ++NumObjCRetainReleasePairs; } else { ++NumRetainReleasePairs; } return true; } // Otherwise, if this is some other pointer, we can only ignore it if we // can prove that the two objects don't alias. // Retain.dump(); ThisRelease.dump(); BB.getParent()->dump(); goto OutOfLoop; } case RT_Unknown: // Loads cannot affect the retain. if (isa<LoadInst>(CurInst)) continue; // Load, store, memcpy etc can't do a release. if (isa<LoadInst>(CurInst) || isa<StoreInst>(CurInst) || isa<MemIntrinsic>(CurInst)) break; // CurInst->dump(); BBI->dump(); // Otherwise, we get to something unknown/unhandled. Bail out for now. goto OutOfLoop; } // If the switch did a break, we made some progress moving this retain. MadeProgress = true; } OutOfLoop: // If we were able to move the retain down, move it now. // TODO: This is where we'd plug in some global algorithms someday. if (MadeProgress) { Retain.moveBefore(&*BBI); return true; } return false; }
bool TracingNoGiri::visitSpecialCall(CallInst &CI) { Function *CalledFunc = CI.getCalledFunction(); // We do not support indirect calls to special functions. if (CalledFunc == nullptr) return false; // Do not consider a function special if it has a function body; in this // case, the programmer has supplied his or her version of the function, and // we will instrument it. if (!CalledFunc->isDeclaration()) return false; // Check the name of the function against a list of known special functions. std::string name = CalledFunc->getName().str(); if (name.substr(0,12) == "llvm.memset.") { instrumentLock(&CI); // Get the destination pointer and cast it to a void pointer. Value *dstPointer = CI.getOperand(0); dstPointer = castTo(dstPointer, VoidPtrType, dstPointer->getName(), &CI); // Get the number of bytes that will be written into the buffer. Value *NumElts = CI.getOperand(2); // Get the ID of the external funtion call instruction. Value *CallID = ConstantInt::get(Int32Type, lsNumPass->getID(&CI)); // Create the call to the run-time to record the external call instruction. std::vector<Value *> args = make_vector(CallID, dstPointer, NumElts, 0); CallInst::Create(RecordStore, args, "", &CI); instrumentUnlock(&CI); ++NumExtFuns; // Update statistics return true; } else if (name.substr(0,12) == "llvm.memcpy." || name.substr(0,13) == "llvm.memmove." || name == "strcpy") { instrumentLock(&CI); /* Record Load src, [CI] Load dst [CI] */ // Get the destination and source pointers and cast them to void pointers. Value *dstPointer = CI.getOperand(0); Value *srcPointer = CI.getOperand(1); dstPointer = castTo(dstPointer, VoidPtrType, dstPointer->getName(), &CI); srcPointer = castTo(srcPointer, VoidPtrType, srcPointer->getName(), &CI); // Get the ID of the ext fun call instruction. Value *CallID = ConstantInt::get(Int32Type, lsNumPass->getID(&CI)); // Create the call to the run-time to record the loads and stores of // external call instruction. if(name == "strcpy") { // FIXME: If the tracer function should be inserted before or after???? std::vector<Value *> args = make_vector(CallID, srcPointer, 0); CallInst::Create(RecordStrLoad, args, "", &CI); args = make_vector(CallID, dstPointer, 0); CallInst *recStore = CallInst::Create(RecordStrStore, args, "", &CI); CI.moveBefore(recStore); } else { // get the num elements to be transfered Value *NumElts = CI.getOperand(2); std::vector<Value *> args = make_vector(CallID, srcPointer, NumElts, 0); CallInst::Create(RecordLoad, args, "", &CI); args = make_vector(CallID, dstPointer, NumElts, 0); CallInst::Create(RecordStore, args, "", &CI); } instrumentUnlock(&CI); ++NumExtFuns; // Update statistics return true; } else if (name == "strcat") { /* Record Load dst, Load Src, Store dst-end before call inst */ instrumentLock(&CI); // Get the destination and source pointers and cast them to void pointers. Value *dstPointer = CI.getOperand(0); Value *srcPointer = CI.getOperand(1); dstPointer = castTo(dstPointer, VoidPtrType, dstPointer->getName(), &CI); srcPointer = castTo(srcPointer, VoidPtrType, srcPointer->getName(), &CI); // Get the ID of the ext fun call instruction. Value *CallID = ConstantInt::get(Int32Type, lsNumPass->getID(&CI)); // Create the call to the run-time to record the loads and stores of // external call instruction. // CHECK: If the tracer function should be inserted before or after???? std::vector<Value *> args = make_vector(CallID, dstPointer, 0); CallInst::Create(RecordStrLoad, args, "", &CI); args = make_vector(CallID, srcPointer, 0); CallInst::Create(RecordStrLoad, args, "", &CI); // Record the addresses before concat as they will be lost after concat args = make_vector(CallID, dstPointer, srcPointer, 0); CallInst::Create(RecordStrcatStore, args, "", &CI); instrumentUnlock(&CI); ++NumExtFuns; // Update statistics return true; } else if (name == "strlen") { /* Record Load */ instrumentLock(&CI); // Get the destination and source pointers and cast them to void pointers. Value *srcPointer = CI.getOperand(0); srcPointer = castTo(srcPointer, VoidPtrType, srcPointer->getName(), &CI); // Get the ID of the ext fun call instruction. Value *CallID = ConstantInt::get(Int32Type, lsNumPass->getID(&CI)); std::vector<Value *> args = make_vector(CallID, srcPointer, 0); CallInst::Create(RecordStrLoad, args, "", &CI); instrumentUnlock(&CI); ++NumExtFuns; // Update statistics return true; } else if (name == "calloc") { instrumentLock(&CI); // Get the number of bytes that will be written into the buffer. Value *NumElts = BinaryOperator::Create(BinaryOperator::Mul, CI.getOperand(0), CI.getOperand(1), "calloc par1 * par2", &CI); // Get the destination pointer and cast it to a void pointer. // Instruction * dstPointerInst; Value *dstPointer = castTo(&CI, VoidPtrType, CI.getName(), &CI); /* // To move after call inst, we need to know if cast is a constant expr or inst if ((dstPointerInst = dyn_cast<Instruction>(dstPointer))) { CI.moveBefore(dstPointerInst); // dstPointerInst->insertAfter(&CI); // ((Instruction *)NumElts)->insertAfter(dstPointerInst); } else { CI.moveBefore((Instruction *)NumElts); // ((Instruction *)NumElts)->insertAfter(&CI); } dstPointer = dstPointerInst; // Assign to dstPointer for instrn or non-instrn values */ // Get the ID of the external funtion call instruction. Value *CallID = ConstantInt::get(Int32Type, lsNumPass->getID(&CI)); // // Create the call to the run-time to record the external call instruction. // std::vector<Value *> args = make_vector(CallID, dstPointer, NumElts, 0); CallInst *recStore = CallInst::Create(RecordStore, args, "", &CI); CI.moveBefore(recStore); //recStore->insertAfter((Instruction *)NumElts); // Moove cast, #byte computation and store to after call inst CI.moveBefore(cast<Instruction>(NumElts)); instrumentUnlock(&CI); ++NumExtFuns; // Update statistics return true; } else if (name == "tolower" || name == "toupper") { // Not needed as there are no loads and stores /* } else if (name == "strncpy/itoa/stdarg/scanf/fscanf/sscanf/fread/complex/strftime/strptime/asctime/ctime") { */ } else if (name == "fscanf") { // TODO // In stead of parsing format string, can we use the type of the arguments?? } else if (name == "sscanf") { // TODO } else if (name == "sprintf") { instrumentLock(&CI); // Get the pointer to the destination buffer. Value *dstPointer = CI.getOperand(0); dstPointer = castTo(dstPointer, VoidPtrType, dstPointer->getName(), &CI); // Get the ID of the call instruction. Value *CallID = ConstantInt::get(Int32Type, lsNumPass->getID(&CI)); // Scan through the arguments looking for what appears to be a character // string. Generate load records for each of these strings. for (unsigned index = 2; index < CI.getNumOperands(); ++index) { if (CI.getOperand(index)->getType() == VoidPtrType) { // Create the call to the run-time to record the load from the string. // What about other loads?? Value *Ptr = CI.getOperand(index); std::vector<Value *> args = make_vector(CallID, Ptr, 0); CallInst::Create(RecordStrLoad, args, "", &CI); ++NumLoadStrings; // Update statistics } } // Create the call to the run-time to record the external call instruction. std::vector<Value *> args = make_vector(CallID, dstPointer, 0); CallInst *recStore = CallInst::Create(RecordStrStore, args, "", &CI); CI.moveBefore(recStore); instrumentUnlock(&CI); ++NumStoreStrings; // Update statistics return true; } else if (name == "fgets") { instrumentLock(&CI); // Get the pointer to the destination buffer. Value * dstPointer = CI.getOperand(0); dstPointer = castTo(dstPointer, VoidPtrType, dstPointer->getName(), &CI); // Get the ID of the ext fun call instruction. Value * CallID = ConstantInt::get(Int32Type, lsNumPass->getID(&CI)); // Create the call to the run-time to record the external call instruction. std::vector<Value *> args = make_vector(CallID, dstPointer, 0); CallInst *recStore = CallInst::Create(RecordStrStore, args, "", &CI); CI.moveBefore(recStore); instrumentUnlock(&CI); // Update statistics ++NumStoreStrings; return true; } return false; }
/// performLocalReleaseMotion - Scan backwards from the specified release, /// moving it earlier in the function if possible, over instructions that do not /// access the released object. If we get to a retain or allocation of the /// object, zap both. static bool performLocalReleaseMotion(CallInst &Release, BasicBlock &BB, SwiftRCIdentity *RC) { // FIXME: Call classifier should identify the object for us. Too bad C++ // doesn't have nice Swift-style enums. Value *ReleasedObject = RC->getSwiftRCIdentityRoot(Release.getArgOperand(0)); BasicBlock::iterator BBI = Release.getIterator(); // Scan until we get to the top of the block. while (BBI != BB.begin()) { --BBI; // Don't analyze PHI nodes. We can't move retains before them and they // aren't "interesting". if (isa<PHINode>(BBI) || // If we found the instruction that defines the value we're releasing, // don't push the release past it. &*BBI == Release.getArgOperand(0)) { ++BBI; goto OutOfLoop; } switch (classifyInstruction(*BBI)) { // These instructions should not reach here based on the pass ordering. // i.e. LLVMARCOpt -> LLVMContractOpt. case RT_UnknownRetainN: case RT_BridgeRetainN: case RT_RetainN: case RT_UnknownReleaseN: case RT_BridgeReleaseN: case RT_ReleaseN: llvm_unreachable("These are only created by LLVMARCContract !"); case RT_NoMemoryAccessed: // Skip over random instructions that don't touch memory. They don't need // protection by retain/release. continue; case RT_UnknownRelease: case RT_BridgeRelease: case RT_ObjCRelease: case RT_Release: { // If we get to a release, we can generally ignore it and scan past it. // However, if we get to a release of obviously the same object, we stop // scanning here because it should have already be moved as early as // possible, so there is no reason to move its friend to the same place. // // NOTE: If this occurs frequently, maybe we can have a release(Obj, N) // API to drop multiple retain counts at once. CallInst &ThisRelease = cast<CallInst>(*BBI); Value *ThisReleasedObject = ThisRelease.getArgOperand(0); ThisReleasedObject = RC->getSwiftRCIdentityRoot(ThisReleasedObject); if (ThisReleasedObject == ReleasedObject) { //Release.dump(); ThisRelease.dump(); BB.getParent()->dump(); ++BBI; goto OutOfLoop; } continue; } case RT_UnknownRetain: case RT_BridgeRetain: case RT_ObjCRetain: case RT_Retain: { // swift_retain(obj) CallInst &Retain = cast<CallInst>(*BBI); Value *RetainedObject = Retain.getArgOperand(0); RetainedObject = RC->getSwiftRCIdentityRoot(RetainedObject); // Since we canonicalized earlier, we know that if our retain has any // uses, they were replaced already. This assertion documents this // assumption. assert(Retain.use_empty() && "Retain should have been canonicalized to " "have no uses."); // If the retain and release are to obviously pointer-equal objects, then // we can delete both of them. We have proven that they do not protect // anything of value. if (RetainedObject == ReleasedObject) { Retain.eraseFromParent(); Release.eraseFromParent(); ++NumRetainReleasePairs; return true; } // Otherwise, this is a retain of an object that is not statically known // to be the same object. It may still be dynamically the same object // though. In this case, we can't move the release past it. // TODO: Strengthen analysis. //Release.dump(); ThisRelease.dump(); BB.getParent()->dump(); ++BBI; goto OutOfLoop; } case RT_AllocObject: { // %obj = swift_alloc(...) CallInst &Allocation = cast<CallInst>(*BBI); // If this is an allocation of an unrelated object, just ignore it. // TODO: This is not safe without proving the object being released is not // related to the allocated object. Consider something silly like this: // A = allocate() // B = bitcast A to object // release(B) if (ReleasedObject != &Allocation) { // Release.dump(); BB.getParent()->dump(); ++BBI; goto OutOfLoop; } // If this is a release right after an allocation of the object, then we // can zap both. Allocation.replaceAllUsesWith(UndefValue::get(Allocation.getType())); Allocation.eraseFromParent(); Release.eraseFromParent(); ++NumAllocateReleasePairs; return true; } case RT_FixLifetime: case RT_RetainUnowned: case RT_CheckUnowned: case RT_Unknown: // Otherwise, we have reached something that we do not understand. Do not // attempt to shorten the lifetime of this object beyond this point so we // are conservative. ++BBI; goto OutOfLoop; } } OutOfLoop: // If we got to the top of the block, (and if the instruction didn't start // there) move the release to the top of the block. // TODO: This is where we'd plug in some global algorithms someday. if (&*BBI != &Release) { Release.moveBefore(&*BBI); return true; } return false; }