void GraphBuilder::visitVAStartNode(DSNode* N) { assert(N && "Null node as argument"); assert(FB && "No function for this graph?"); Module *M = FB->getParent(); assert(M && "No module for function"); Triple TargetTriple(M->getTargetTriple()); Triple::ArchType Arch = TargetTriple.getArch(); // Fetch the VANode associated with the func containing the call to va_start DSNodeHandle & VANH = G.getVANodeFor(*FB); // Make sure this NodeHandle has a node to go with it if (VANH.isNull()) VANH.mergeWith(createNode()); // Create a dsnode for an array of pointers to the VAInfo for this func DSNode * VAArray = createNode(); VAArray->setArrayMarker(); VAArray->foldNodeCompletely(); VAArray->setLink(0,VANH); //VAStart modifies its argument N->setModifiedMarker(); // For the architectures we support, build dsnodes that match // how we know va_list is used. switch (Arch) { case Triple::x86: // On x86, we have: // va_list as a pointer to an array of pointers to the variable arguments if (N->getSize() < 1) N->growSize(1); N->setLink(0, VAArray); break; case Triple::x86_64: // On x86_64, we have va_list as a struct {i32, i32, i8*, i8* } // The first i8* is where arguments generally go, but the second i8* can // be used also to pass arguments by register. // We model this by having both the i8*'s point to an array of pointers // to the arguments. if (N->getSize() < 24) N->growSize(24); //sizeof the va_list struct mentioned above N->setLink(8,VAArray); //first i8* N->setLink(16,VAArray); //second i8* break; default: // FIXME: For now we abort if we don't know how to handle this arch // Either add support for other architectures, or at least mark the // nodes unknown/incomplete or whichever results in the correct // conservative behavior in the general case assert(0 && "VAstart not supported on this architecture!"); //XXX: This might be good enough in those cases that we don't know //what the arch does N->setIncompleteMarker()->setUnknownMarker()->foldNodeCompletely(); } // XXX: We used to set the alloca marker for the DSNode passed to va_start. // Seems to me that you could allocate the va_list on the heap, so ignoring // for now. N->setModifiedMarker()->setVAStartMarker(); }
/// /// Method: visitIntrinsic() /// /// Description: /// Generate correct DSNodes for calls to LLVM intrinsic functions. /// /// Inputs: /// CS - The CallSite representing the call or invoke to the intrinsic. /// F - A pointer to the function called by the call site. /// /// Return value: /// true - This intrinsic is properly handled by this method. /// false - This intrinsic is not recognized by DSA. /// bool GraphBuilder::visitIntrinsic(CallSite CS, Function *F) { ++NumIntrinsicCall; // // If this is a debug intrinsic, then don't do any special processing. // if (isa<DbgInfoIntrinsic>(CS.getInstruction())) return true; switch (F->getIntrinsicID()) { case Intrinsic::vastart: { visitVAStartInst(CS); return true; } case Intrinsic::vacopy: { // Simply merge the two arguments to va_copy. // This results in loss of precision on the temporaries used to manipulate // the va_list, and so isn't a big deal. In theory we would build a // separate graph for this (like the one created in visitVAStartNode) // and only merge the node containing the variable arguments themselves. DSNodeHandle destNH = getValueDest(CS.getArgument(0)); DSNodeHandle srcNH = getValueDest(CS.getArgument(1)); destNH.mergeWith(srcNH); return true; } case Intrinsic::stacksave: { DSNode * Node = createNode(); Node->setAllocaMarker()->setIncompleteMarker()->setUnknownMarker(); Node->foldNodeCompletely(); setDestTo (*(CS.getInstruction()), Node); return true; } case Intrinsic::stackrestore: getValueDest(CS.getInstruction()).getNode()->setAllocaMarker() ->setIncompleteMarker() ->setUnknownMarker() ->foldNodeCompletely(); return true; case Intrinsic::vaend: case Intrinsic::memcpy: case Intrinsic::memmove: { // Merge the first & second arguments, and mark the memory read and // modified. DSNodeHandle RetNH = getValueDest(*CS.arg_begin()); RetNH.mergeWith(getValueDest(*(CS.arg_begin()+1))); if (DSNode *N = RetNH.getNode()) N->setModifiedMarker()->setReadMarker(); return true; } case Intrinsic::memset: // Mark the memory modified. if (DSNode *N = getValueDest(*CS.arg_begin()).getNode()) N->setModifiedMarker(); return true; case Intrinsic::eh_exception: { DSNode * Node = createNode(); Node->setIncompleteMarker(); Node->foldNodeCompletely(); setDestTo (*(CS.getInstruction()), Node); return true; } case Intrinsic::eh_selector: { for (CallSite::arg_iterator I = CS.arg_begin(), E = CS.arg_end(); I != E; ++I) { if (isa<PointerType>((*I)->getType())) { DSNodeHandle Ptr = getValueDest(*I); if(Ptr.getNode()) { Ptr.getNode()->setReadMarker(); Ptr.getNode()->setIncompleteMarker(); } } } return true; } case Intrinsic::eh_typeid_for: { DSNodeHandle Ptr = getValueDest(*CS.arg_begin()); Ptr.getNode()->setReadMarker(); Ptr.getNode()->setIncompleteMarker(); return true; } case Intrinsic::prefetch: return true; case Intrinsic::objectsize: return true; // // The return address/frame address aliases with the stack, // is type-unknown, and should // have the unknown flag set since we don't know where it goes. // case Intrinsic::returnaddress: case Intrinsic::frameaddress: { DSNode * Node = createNode(); Node->setAllocaMarker()->setIncompleteMarker()->setUnknownMarker(); Node->foldNodeCompletely(); setDestTo (*(CS.getInstruction()), Node); return true; } // Process lifetime intrinsics case Intrinsic::lifetime_start: case Intrinsic::lifetime_end: case Intrinsic::invariant_start: case Intrinsic::invariant_end: return true; default: { //ignore pointer free intrinsics if (!isa<PointerType>(F->getReturnType())) { bool hasPtr = false; for (Function::const_arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E && !hasPtr; ++I) if (isa<PointerType>(I->getType())) hasPtr = true; if (!hasPtr) return true; } DEBUG(errs() << "[dsa:local] Unhandled intrinsic: " << F->getName() << "\n"); assert(0 && "Unhandled intrinsic"); return false; } } }
// // Function: MergeConstantInitIntoNode() // // Description: // Merge the specified constant into the specified DSNode. // void GraphBuilder::MergeConstantInitIntoNode(DSNodeHandle &NH, Type* Ty, Constant *C) { // // Ensure a type-record exists... // DSNode *NHN = NH.getNode(); //NHN->mergeTypeInfo(Ty, NH.getOffset()); // // If we've found something of pointer type, create or find its DSNode and // make a link from the specified DSNode to the new DSNode describing the // pointer we've just found. // if (isa<PointerType>(Ty)) { NHN->mergeTypeInfo(Ty, NH.getOffset()); NH.addEdgeTo(getValueDest(C)); return; } // // If the type of the object (array element, structure field, etc.) is an // integer or floating point type, then just ignore it. It has no DSNode. // if (Ty->isIntOrIntVectorTy() || Ty->isFPOrFPVectorTy()) return; // // Handle aggregate constants. // if (ConstantArray *CA = dyn_cast<ConstantArray>(C)) { // // For an array, we don't worry about different elements pointing to // different objects; we essentially pretend that all array elements alias. // Type * ElementType = cast<ArrayType>(Ty)->getElementType(); for (unsigned i = 0, e = CA->getNumOperands(); i != e; ++i) { Constant * ConstElement = cast<Constant>(CA->getOperand(i)); MergeConstantInitIntoNode(NH, ElementType, ConstElement); } } else if (ConstantStruct *CS = dyn_cast<ConstantStruct>(C)) { // // For a structure, we need to merge each element of the constant structure // into the specified DSNode. However, we must also handle structures that // end with a zero-length array ([0 x sbyte]); this is a common C idiom // that continues to plague the world. // //NHN->mergeTypeInfo(Ty, NH.getOffset()); const StructLayout *SL = TD.getStructLayout(cast<StructType>(Ty)); for (unsigned i = 0, e = CS->getNumOperands(); i != e; ++i) { DSNode *NHN = NH.getNode(); if (SL->getElementOffset(i) < SL->getSizeInBytes()) { // // Get the type and constant value of this particular element of the // constant structure. // Type * ElementType = cast<StructType>(Ty)->getElementType(i); Constant * ConstElement = cast<Constant>(CS->getOperand(i)); // // Get the offset (in bytes) into the memory object that we're // analyzing. // unsigned offset = NH.getOffset()+(unsigned)SL->getElementOffset(i); NHN->mergeTypeInfo(ElementType, offset); // // Create a new DSNodeHandle. This DSNodeHandle will point to the same // DSNode as the one we're constructing for our caller; however, it // will point into a different offset into that DSNode. // DSNodeHandle NewNH (NHN, offset); assert ((NHN->isNodeCompletelyFolded() || (NewNH.getOffset() == offset)) && "Need to resize DSNode!"); // // Recursively merge in this element of the constant struture into the // DSNode. // MergeConstantInitIntoNode(NewNH, ElementType, ConstElement); } else if (SL->getElementOffset(i) == SL->getSizeInBytes()) { // // If this is one of those cute structures that ends with a zero-length // array, just fold the DSNode now and get it over with. // DEBUG(errs() << "Zero size element at end of struct\n" ); NHN->foldNodeCompletely(); } else { assert(0 && "type was smaller than offsets of struct layout indicate"); } } } else if (isa<ConstantAggregateZero>(C) || isa<UndefValue>(C)) { // // Undefined values and NULL pointers have no DSNodes, so they do nothing. // } else { assert(0 && "Unknown constant type!"); } }