// // Function: makeFSParameterCallsComplete() // // Description: // Finds calls to sc.fsparameter and fills in the completeness byte which // is the last argument to such call. The second argument to the function // is the one which is analyzed for completeness. // // Inputs: // M - Reference to the the module to analyze // void CompleteChecks::makeFSParameterCallsComplete(Module &M) { Function *sc_fsparameter = M.getFunction("sc.fsparameter"); if (sc_fsparameter == NULL) return; std::set<CallInst *> toComplete; // // Iterate over all uses of sc.fsparameter and discover which have a complete // pointer argument. // for (Function::use_iterator i = sc_fsparameter->use_begin(); i != sc_fsparameter->use_end(); ++i) { CallInst *CI; CI = dyn_cast<CallInst>(*i); if (CI == 0 || CI->getCalledFunction() != sc_fsparameter) continue; // // Get the parent function to which this call belongs. // Function *P = CI->getParent()->getParent(); Value *PtrOperand = CI->getOperand(2); DSNode *N = getDSNodeHandle(PtrOperand, P).getNode(); if (N == 0 || N->isExternalNode() || N->isIncompleteNode() || N->isUnknownNode() || N->isPtrToIntNode() || N->isIntToPtrNode()) { continue; } toComplete.insert(CI); } // // Fill in a 1 for each call instruction that has a complete pointer // argument. // Type *int8 = Type::getInt8Ty(M.getContext()); Constant *complete = ConstantInt::get(int8, 1); for (std::set<CallInst *>::iterator i = toComplete.begin(); i != toComplete.end(); ++i) { CallInst *CI = *i; CI->setOperand(4, complete); } return; }
bool DSGraphStats::isNodeForValueUntyped(Value *V, unsigned Offset, const Function *F) { DSNodeHandle NH = getNodeHandleForValue(V); if(!NH.getNode()){ return true; } else { DSNode *N = NH.getNode(); if (N->isNodeCompletelyFolded()){ ++NumFoldedAccess; return true; } if ( N->isExternalNode()){ ++NumExternalAccesses; return true; } if ( N->isIncompleteNode()){ ++NumIncompleteAccesses; return true; } if (N->isUnknownNode()){ ++NumUnknownAccesses; return true; } if (N->isIntToPtrNode()){ ++NumI2PAccesses; return true; } // it is a complete node, now check how many types are present int count = 0; unsigned offset = NH.getOffset() + Offset; if (N->type_begin() != N->type_end()) for (DSNode::TyMapTy::const_iterator ii = N->type_begin(), ee = N->type_end(); ii != ee; ++ii) { if(ii->first != offset) continue; count += ii->second->size(); } if (count ==0) ++NumTypeCount0Accesses; else if(count == 1) ++NumTypeCount1Accesses; else if(count == 2) ++NumTypeCount2Accesses; else if(count == 3) ++NumTypeCount3Accesses; else ++NumTypeCount4Accesses; DEBUG(assert(TS->isTypeSafe(V,F))); } return false; }
void AllButUnreachableFromMemoryHeuristic::AssignToPools ( const DSNodeList_t &NodesToPA, Function *F, DSGraph* G, std::vector<OnePool> &ResultPools) { // Build a set of all nodes that are reachable from another node in the // graph. Here we ignore scalar nodes that are only globals as they are // often global pointers to big arrays. std::set<const DSNode*> ReachableFromMemory; for (DSGraph::node_iterator I = G->node_begin(), E = G->node_end(); I != E; ++I) { DSNode *N = I; #if 0 // // Ignore nodes that are just globals and not arrays. // if (N->isArray() || N->isHeapNode() || N->isAllocaNode() || N->isUnknownNode()) #endif // If a node is marked, all children are too. if (!ReachableFromMemory.count(N)) { for (DSNode::iterator NI = N->begin(), E = N->end(); NI != E; ++NI) { // // Sometimes this results in a NULL DSNode. Skip it if that is the // case. // if (!(*NI)) continue; // // Do a depth-first iteration over the DSGraph starting with this // child node. // for (df_ext_iterator<const DSNode*> DI = df_ext_begin(*NI, ReachableFromMemory), E = df_ext_end(*NI, ReachableFromMemory); DI != E; ++DI) /*empty*/; } } } // Only pool allocate a node if it is reachable from a memory object (itself // included). for (unsigned i = 0, e = NodesToPA.size(); i != e; ++i) if (ReachableFromMemory.count(NodesToPA[i])) ResultPools.push_back(OnePool(NodesToPA[i])); }
// // TODO // template<class dsa> bool TypeSafety<dsa>::isFieldDisjoint (const GlobalValue * V, unsigned offset) { // // Get the DSNode for the specified value. // DSNodeHandle DH = getDSNodeHandle (V); DSNode *node = DH.getNode(); //unsigned offset = DH.getOffset(); DEBUG(errs() << " check fields overlap at: " << offset << "\n"); // // If there is no DSNode, claim that it is not type safe. // if (DH.isNull()) { return false; } // // If the DSNode is completely folded, then we know for sure that it is not // type-safe. // if (node->isNodeCompletelyFolded()) return false; // // If the memory object represented by this DSNode can be manipulated by // external code or DSA has otherwise not finished analyzing all operations // on it, declare it type-unsafe. // if (node->isExternalNode() || node->isIncompleteNode()) return false; // // If the pointer to the memory object came from some source not understood // by DSA or somehow came from/escapes to the realm of integers, declare it // type-unsafe. // if (node->isUnknownNode() || node->isIntToPtrNode() || node->isPtrToIntNode()) { return false; } return !((NodeInfo[node])[offset]); }
// // Function: makeCStdLibCallsComplete() // // Description: // Fills in completeness information for all calls of a given CStdLib function // assumed to be of the form: // // pool_X(POOL *p1, ..., POOL *pN, void *a1, ..., void *aN, ..., uint8_t c); // // Specifically, this function assumes that there are as many pointer arguments // to check as there are initial pool arguments, and the pointer arguments // follow the pool arguments in corresponding order. Also, it is assumed that // the final argument to the function is a byte sized bit vector. // // This function fills in this final byte with a constant value whose ith // bit is set exactly when the ith pointer argument is complete. // // Inputs: // // F - A pointer to the CStdLib function appearing in the module // (non-null). // PoolArgs - The number of initial pool arguments for which a // corresponding pointer value requires a completeness check // (required to be at most 8). // void CompleteChecks::makeCStdLibCallsComplete(Function *F, unsigned PoolArgs) { assert(F != 0 && "Null function argument!"); assert(PoolArgs <= 8 && \ "Only up to 8 arguments are supported by CStdLib completeness checks!"); Value::use_iterator U = F->use_begin(); Value::use_iterator E = F->use_end(); // // Hold the call instructions that need changing. // typedef std::pair<CallInst *, uint8_t> VectorReplacement; std::set<VectorReplacement> callsToChange; Type *int8ty = Type::getInt8Ty(F->getContext()); FunctionType *F_type = F->getFunctionType(); // // Verify the type of the function is as expected. // // There should be as many pointer parameters to check for completeness // as there are pool parameters. The last parameter should be a byte. // assert(F_type->getNumParams() >= PoolArgs * 2 && \ "Not enough arguments to transformed CStdLib function call!"); for (unsigned arg = PoolArgs; arg < PoolArgs * 2; ++arg) assert(isa<PointerType>(F_type->getParamType(arg)) && \ "Expected pointer argument to function!"); // // This is the position of the vector operand in the call. // unsigned vect_position = F_type->getNumParams(); assert(F_type->getParamType(vect_position - 1) == int8ty && \ "Last parameter to the function should be a byte!"); // // Iterate over all calls of the function in the module, computing the // vectors for each call as it is found. // for (; U != E; ++U) { CallInst *CI; if ((CI = dyn_cast<CallInst>(*U)) && \ CI->getCalledValue()->stripPointerCasts() == F) { uint8_t vector = 0x0; // // Get the parent function to which this instruction belongs. // Function *P = CI->getParent()->getParent(); // // Iterate over the pointer arguments that need completeness checking // and build the completeness vector. // for (unsigned arg = 0; arg < PoolArgs; ++arg) { bool complete = true; // // Go past all the pool arguments to get the pointer to check. // Value *V = CI->getOperand(1 + PoolArgs + arg); // // Check for completeness of the pointer using DSA and // set the bit in the vector accordingly. // DSNode *N; if ((N = getDSNodeHandle(V, P).getNode()) && (N->isExternalNode() || N->isIncompleteNode() || N->isUnknownNode() || N->isIntToPtrNode() || N->isPtrToIntNode()) ) { complete = false; } if (complete) vector |= (1 << arg); } // // Add the instruction and vector to the set of instructions to change. // callsToChange.insert(VectorReplacement(CI, vector)); } } // // Iterate over all call instructions that need changing, modifying the // final operand of the call to hold the bit vector value. // std::set<VectorReplacement>::iterator change = callsToChange.begin(); std::set<VectorReplacement>::iterator change_end = callsToChange.end(); while (change != change_end) { Constant *vect_value = ConstantInt::get(int8ty, change->second); change->first->setOperand(vect_position, vect_value); ++change; } return; }
// // Method: findGlobalPoolNodes() // // Description: // This method finds DSNodes that are reachable from globals and that need a // pool. The Automatic Pool Allocation transform will use the returned // information to build global pools for the DSNodes in question. // // For efficiency, this method also determines which DSNodes should be in the // same pool. // // Outputs: // Nodes - The DSNodes that are both reachable from globals and which should // have global pools will be *added* to this container. // void AllNodesHeuristic::findGlobalPoolNodes (DSNodeSet_t & Nodes) { // Get the globals graph for the program. DSGraph* GG = Graphs->getGlobalsGraph(); // // Get all of the nodes reachable from globals. // DenseSet<const DSNode*> GlobalNodes; GetNodesReachableFromGlobals (GG, GlobalNodes); // // Create a global pool for each global DSNode. // for (DenseSet<const DSNode *>::iterator NI = GlobalNodes.begin(); NI != GlobalNodes.end(); ++NI) { const DSNode * N = *NI; PoolMap[N] = OnePool(N); } // // Now find all DSNodes belonging to function-local DSGraphs which are // mirrored in the globals graph. These DSNodes require a global pool, too, // but must use the same pool as the one assigned to the corresponding global // DSNode. // for (Module::iterator F = M->begin(); F != M->end(); ++F) { // // Ignore functions that have no DSGraph. // if (!(Graphs->hasDSGraph(*F))) continue; // // Compute a mapping between local DSNodes and DSNodes in the globals // graph. // DSGraph* G = Graphs->getDSGraph(*F); DSGraph::NodeMapTy NodeMap; G->computeGToGGMapping (NodeMap); // // Scan through all DSNodes in the local graph. If a local DSNode has a // corresponding DSNode in the globals graph that is reachable from a // global, then add the local DSNode to the set of DSNodes reachable from // a global. // DSGraph::node_iterator ni = G->node_begin(); for (; ni != G->node_end(); ++ni) { DSNode * N = ni; DSNode * GGN = NodeMap[N].getNode(); assert (!GGN || GlobalNodes.count (GGN)); if (GGN && GlobalNodes.count (GGN)) PoolMap[GGN].NodesInPool.push_back (N); } } // // Scan through all the local graphs looking for DSNodes which may be // reachable by a global. These nodes may not end up in the globals graph // because of the fact that DSA doesn't actually know what is happening to // them. // // FIXME: I believe this code causes a condition in which a local DSNode is // given a local pool in one function but not in other functions. // Someone needs to investigate whether DSA is being consistent here, // and if not, if that inconsistency is correct. // #if 0 for (Module::iterator F = M->begin(); F != M->end(); ++F) { if (F->isDeclaration()) continue; DSGraph* G = Graphs->getDSGraph(*F); for (DSGraph::node_iterator I = G->node_begin(), E = G->node_end(); I != E; ++I) { DSNode * Node = I; if (Node->isExternalNode() || Node->isUnknownNode()) { GlobalNodes.insert (Node); } } } #endif // // Copy the values into the output container. Note that DenseSet has no // iterator traits (or whatever allows us to treat DenseSet has a generic // container), so we have to use a loop to copy values from the DenseSet into // the output container. // // Note that we do not copy local DSNodes into the output container; we // merely copy those nodes in the globals graph. // for (DenseSet<const DSNode*>::iterator I = GlobalNodes.begin(), E = GlobalNodes.end(); I != E; ++I) { Nodes.insert (*I); } return; }