void TDDataStructures::ComputePostOrder(const Function &F, DenseSet<DSGraph*> &Visited, std::vector<DSGraph*> &PostOrder) { if (F.isDeclaration()) return; DSGraph* G = getOrCreateGraph(&F); if (!Visited.insert(G).second) return; // Recursively traverse all of the callee graphs. svset<const Function*> Callees; // Go through all of the callsites in this graph and find all callees // Here we're trying to capture all possible callees so that we can ensure // each function has all possible callers inlined into it. for (DSGraph::fc_iterator CI = G->fc_begin(), E = G->fc_end(); CI != E; ++CI) { // Direct calls are easy, no reason to look at DSCallGraph // or anything to do with SCC's if (CI->isDirectCall()) { ComputePostOrder(*CI->getCalleeFunc(), Visited, PostOrder); } else { // Otherwise, ask the DSCallGraph for the full set of possible // callees for this callsite. // This includes all members of the SCC's of those callees, // and well as others in F's SCC, since we must assume // any indirect call might be intra-SCC. callgraph.addFullFunctionSet(CI->getCallSite(), Callees); } } for (svset<const Function*>::iterator I = Callees.begin(), E = Callees.end(); I != E; ++I) ComputePostOrder(**I, Visited, PostOrder); PostOrder.push_back(G); }
/// InlineCallersIntoGraph - Inline all of the callers of the specified DS graph /// into it, then recompute completeness of nodes in the resultant graph. void TDDataStructures::InlineCallersIntoGraph(DSGraph* DSG) { // Inline caller graphs into this graph. First step, get the list of call // sites that call into this graph. std::vector<CallerCallEdge> EdgesFromCaller; std::map<DSGraph*, std::vector<CallerCallEdge> >::iterator CEI = CallerEdges.find(DSG); if (CEI != CallerEdges.end()) { std::swap(CEI->second, EdgesFromCaller); CallerEdges.erase(CEI); } // Sort the caller sites to provide a by-caller-graph ordering. std::sort(EdgesFromCaller.begin(), EdgesFromCaller.end()); // Merge information from the globals graph into this graph. FIXME: This is // stupid. Instead of us cloning information from the GG into this graph, // then having RemoveDeadNodes clone it back, we should do all of this as a // post-pass over all of the graphs. We need to take cloning out of // removeDeadNodes and gut removeDeadNodes at the same time first though. :( cloneGlobalsInto(DSG, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); DEBUG(errs() << "[TD] Inlining callers into '" << DSG->getFunctionNames() << "'\n"); DSG->maskIncompleteMarkers(); // Iteratively inline caller graphs into this graph. while (!EdgesFromCaller.empty()) { DSGraph* CallerGraph = EdgesFromCaller.back().CallerGraph; // Iterate through all of the call sites of this graph, cloning and merging // any nodes required by the call. ReachabilityCloner RC(DSG, CallerGraph, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); // Inline all call sites from this caller graph. do { const DSCallSite &CS = *EdgesFromCaller.back().CS; const Function &CF = *EdgesFromCaller.back().CalledFunction; DEBUG(errs() << " [TD] Inlining graph into Fn '" << CF.getName().str() << "' from "); if (CallerGraph->getReturnNodes().empty()) { DEBUG(errs() << "SYNTHESIZED INDIRECT GRAPH"); } else { DEBUG(errs() << "Fn '" << CS.getCallSite().getInstruction()-> getParent()->getParent()->getName().str() << "'"); } DEBUG(errs() << ": " << CF.getFunctionType()->getNumParams() << " args\n"); // Get the formal argument and return nodes for the called function and // merge them with the cloned subgraph. DSCallSite T1 = DSG->getCallSiteForArguments(CF); RC.mergeCallSite(T1, CS); ++NumTDInlines; EdgesFromCaller.pop_back(); } while (!EdgesFromCaller.empty() && EdgesFromCaller.back().CallerGraph == CallerGraph); } // Next, now that this graph is finalized, we need to recompute the // incompleteness markers for this graph and remove unreachable nodes. // If any of the functions is externally callable, treat everything in its // SCC as externally callable. bool isExternallyCallable = false; for (DSGraph::retnodes_iterator I = DSG->retnodes_begin(), E = DSG->retnodes_end(); I != E; ++I) if (ExternallyCallable.count(I->first)) { isExternallyCallable = true; break; } // Recompute the Incomplete markers. Depends on whether args are complete unsigned IncFlags = DSGraph::IgnoreFormalArgs; IncFlags |= DSGraph::IgnoreGlobals | DSGraph::MarkVAStart; DSG->markIncompleteNodes(IncFlags); // If this graph contains functions that are externally callable, now is the time to mark // their arguments and return values as external. At this point TD is inlining all caller information, // and that means External callers too. unsigned ExtFlags = isExternallyCallable ? DSGraph::MarkFormalsExternal : DSGraph::DontMarkFormalsExternal; DSG->computeExternalFlags(ExtFlags); DSG->computeIntPtrFlags(); cloneIntoGlobals(DSG, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); // // Delete dead nodes. Treat globals that are unreachable as dead also. // // FIXME: // Do not delete unreachable globals as the comment describes. For its // alignment checks on the results of load instructions, SAFECode must be // able to find the DSNode of both the result of the load as well as the // pointer dereferenced by the load. If we remove unreachable globals, then // if the dereferenced pointer is a global, its DSNode will not reachable // from the local graph's scalar map, and chaos ensues. // // So, for now, just remove dead nodes but leave the globals alone. // DSG->removeDeadNodes(0); // We are done with computing the current TD Graph! Finally, before we can // finish processing this function, we figure out which functions it calls and // records these call graph edges, so that we have them when we process the // callee graphs. if (DSG->fc_begin() == DSG->fc_end()) return; // Loop over all the call sites and all the callees at each call site, and add // edges to the CallerEdges structure for each callee. for (DSGraph::fc_iterator CI = DSG->fc_begin(), E = DSG->fc_end(); CI != E; ++CI) { // Handle direct calls efficiently. if (CI->isDirectCall()) { if (!CI->getCalleeFunc()->isDeclaration() && !DSG->getReturnNodes().count(CI->getCalleeFunc())) CallerEdges[getOrCreateGraph(CI->getCalleeFunc())] .push_back(CallerCallEdge(DSG, &*CI, CI->getCalleeFunc())); continue; } svset<const Function*> AllCallees; std::vector<const Function*> Callees; // Get the list of callees callgraph.addFullFunctionSet(CI->getCallSite(), AllCallees); // Filter all non-declarations, and calls within this DSGraph for (svset<const Function*>::iterator I = AllCallees.begin(), E = AllCallees.end(); I != E; ++I) { const Function *F = *I; if (!F->isDeclaration() && getDSGraph(**I) != DSG) Callees.push_back(F); } AllCallees.clear(); // If there is exactly one callee from this call site, remember the edge in // CallerEdges. if (Callees.size() == 1) { const Function * Callee = Callees[0]; CallerEdges[getOrCreateGraph(Callee)] .push_back(CallerCallEdge(DSG, &*CI, Callee)); } if (Callees.size() <= 1) continue; // Otherwise, there are multiple callees from this call site, so it must be // an indirect call. Chances are that there will be other call sites with // this set of targets. If so, we don't want to do M*N inlining operations, // so we build up a new, private, graph that represents the calls of all // calls to this set of functions. std::map<std::vector<const Function*>, DSGraph*>::iterator IndCallRecI = IndCallMap.lower_bound(Callees); // If we already have this graph, recycle it. if (IndCallRecI != IndCallMap.end() && IndCallRecI->first == Callees) { DEBUG(errs() << " [TD] *** Reuse of indcall graph for " << Callees.size() << " callees!\n"); DSGraph * IndCallGraph = IndCallRecI->second; assert(IndCallGraph->getFunctionCalls().size() == 1); // Merge the call into the CS already in the IndCallGraph ReachabilityCloner RC(IndCallGraph, DSG, 0); RC.mergeCallSite(IndCallGraph->getFunctionCalls().front(), *CI); } else { // Otherwise, create a new DSGraph to represent this. DSGraph* IndCallGraph = new DSGraph(DSG->getGlobalECs(), DSG->getDataLayout(), *TypeSS); // Clone over the call into the new DSGraph ReachabilityCloner RC(IndCallGraph, DSG, 0); DSCallSite ClonedCS = RC.cloneCallSite(*CI); // Add the cloned CS to the graph, as if it were an original call. IndCallGraph->getFunctionCalls().push_back(ClonedCS); // Save this graph for use later, should we need it. IndCallRecI = IndCallMap.insert(IndCallRecI, std::make_pair(Callees, IndCallGraph)); // Additionally, make sure that each of the callees inlines this graph // exactly once. DSCallSite *NCS = &IndCallGraph->getFunctionCalls().front(); for (unsigned i = 0, e = Callees.size(); i != e; ++i) { DSGraph* CalleeGraph = getDSGraph(*Callees[i]); if (CalleeGraph != DSG) CallerEdges[CalleeGraph].push_back(CallerCallEdge(IndCallGraph, NCS, Callees[i])); } } } }
/// InlineCallersIntoGraph - Inline all of the callers of the specified DS graph /// into it, then recompute completeness of nodes in the resultant graph. void TDDataStructures::InlineCallersIntoGraph(DSGraph* DSG) { // Inline caller graphs into this graph. First step, get the list of call // sites that call into this graph. std::vector<CallerCallEdge> EdgesFromCaller; std::map<DSGraph*, std::vector<CallerCallEdge> >::iterator CEI = CallerEdges.find(DSG); if (CEI != CallerEdges.end()) { std::swap(CEI->second, EdgesFromCaller); CallerEdges.erase(CEI); } // Sort the caller sites to provide a by-caller-graph ordering. std::sort(EdgesFromCaller.begin(), EdgesFromCaller.end()); // Merge information from the globals graph into this graph. FIXME: This is // stupid. Instead of us cloning information from the GG into this graph, // then having RemoveDeadNodes clone it back, we should do all of this as a // post-pass over all of the graphs. We need to take cloning out of // removeDeadNodes and gut removeDeadNodes at the same time first though. :( { DSGraph* GG = DSG->getGlobalsGraph(); ReachabilityCloner RC(DSG, GG, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); for (DSScalarMap::global_iterator GI = DSG->getScalarMap().global_begin(), E = DSG->getScalarMap().global_end(); GI != E; ++GI) RC.getClonedNH(GG->getNodeForValue(*GI)); } DEBUG(errs() << "[TD] Inlining callers into '" << DSG->getFunctionNames() << "'\n"); // Iteratively inline caller graphs into this graph. while (!EdgesFromCaller.empty()) { DSGraph* CallerGraph = EdgesFromCaller.back().CallerGraph; // Iterate through all of the call sites of this graph, cloning and merging // any nodes required by the call. ReachabilityCloner RC(DSG, CallerGraph, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); // Inline all call sites from this caller graph. do { const DSCallSite &CS = *EdgesFromCaller.back().CS; const Function &CF = *EdgesFromCaller.back().CalledFunction; DEBUG(errs() << " [TD] Inlining graph into Fn '" << CF.getNameStr() << "' from "); if (CallerGraph->getReturnNodes().empty()) { DEBUG(errs() << "SYNTHESIZED INDIRECT GRAPH"); } else { DEBUG(errs() << "Fn '" << CS.getCallSite().getInstruction()-> getParent()->getParent()->getNameStr() << "'"); } DEBUG(errs() << ": " << CF.getFunctionType()->getNumParams() << " args\n"); // Get the formal argument and return nodes for the called function and // merge them with the cloned subgraph. DSCallSite T1 = DSG->getCallSiteForArguments(CF); RC.mergeCallSite(T1, CS); ++NumTDInlines; EdgesFromCaller.pop_back(); } while (!EdgesFromCaller.empty() && EdgesFromCaller.back().CallerGraph == CallerGraph); } { DSGraph* GG = DSG->getGlobalsGraph(); ReachabilityCloner RC(GG, DSG, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); for (DSScalarMap::global_iterator GI = DSG->getScalarMap().global_begin(), E = DSG->getScalarMap().global_end(); GI != E; ++GI) RC.getClonedNH(DSG->getNodeForValue(*GI)); } // Next, now that this graph is finalized, we need to recompute the // incompleteness markers for this graph and remove unreachable nodes. DSG->maskIncompleteMarkers(); // If any of the functions has incomplete incoming arguments, don't mark any // of them as complete. bool HasIncompleteArgs = false; for (DSGraph::retnodes_iterator I = DSG->retnodes_begin(), E = DSG->retnodes_end(); I != E; ++I) if (ArgsRemainIncomplete.count(I->first)) { HasIncompleteArgs = true; break; } // Recompute the Incomplete markers. Depends on whether args are complete unsigned Flags = HasIncompleteArgs ? DSGraph::MarkFormalArgs : DSGraph::IgnoreFormalArgs; Flags |= DSGraph::IgnoreGlobals | DSGraph::MarkVAStart; DSG->markIncompleteNodes(Flags); // Delete dead nodes. Treat globals that are unreachable as dead also. DSG->removeDeadNodes(DSGraph::RemoveUnreachableGlobals); // We are done with computing the current TD Graph! Finally, before we can // finish processing this function, we figure out which functions it calls and // records these call graph edges, so that we have them when we process the // callee graphs. if (DSG->fc_begin() == DSG->fc_end()) return; // Loop over all the call sites and all the callees at each call site, and add // edges to the CallerEdges structure for each callee. for (DSGraph::fc_iterator CI = DSG->fc_begin(), E = DSG->fc_end(); CI != E; ++CI) { // Handle direct calls efficiently. if (CI->isDirectCall()) { if (!CI->getCalleeFunc()->isDeclaration() && !DSG->getReturnNodes().count(CI->getCalleeFunc())) CallerEdges[getOrFetchDSGraph(CI->getCalleeFunc())] .push_back(CallerCallEdge(DSG, &*CI, CI->getCalleeFunc())); continue; } Instruction *CallI = CI->getCallSite().getInstruction(); // For each function in the invoked function list at this call site... calleeTy::iterator IPI = callee.begin(CallI), IPE = callee.end(CallI); // Skip over all calls to this graph (SCC calls). while (IPI != IPE && getDSGraph(*IPI) == DSG) ++IPI; // All SCC calls? if (IPI == IPE) continue; const Function *FirstCallee = *IPI; ++IPI; // Skip over more SCC calls. while (IPI != IPE && getDSGraph(*IPI) == DSG) ++IPI; // If there is exactly one callee from this call site, remember the edge in // CallerEdges. if (IPI == IPE) { if (!FirstCallee->isDeclaration()) CallerEdges[getOrFetchDSGraph(FirstCallee)] .push_back(CallerCallEdge(DSG, &*CI, FirstCallee)); continue; } // Otherwise, there are multiple callees from this call site, so it must be // an indirect call. Chances are that there will be other call sites with // this set of targets. If so, we don't want to do M*N inlining operations, // so we build up a new, private, graph that represents the calls of all // calls to this set of functions. std::vector<const Function*> Callees; for (calleeTy::iterator I = callee.begin(CallI), E = callee.end(CallI); I != E; ++I) if (!(*I)->isDeclaration()) Callees.push_back(*I); std::sort(Callees.begin(), Callees.end()); std::map<std::vector<const Function*>, DSGraph*>::iterator IndCallRecI = IndCallMap.lower_bound(Callees); DSGraph *IndCallGraph; // If we already have this graph, recycle it. if (IndCallRecI != IndCallMap.end() && IndCallRecI->first == Callees) { DEBUG(errs() << " [TD] *** Reuse of indcall graph for " << Callees.size() << " callees!\n"); IndCallGraph = IndCallRecI->second; } else { // Otherwise, create a new DSGraph to represent this. IndCallGraph = new DSGraph(DSG->getGlobalECs(), DSG->getTargetData(), GlobalsGraph); // Make a nullary dummy call site, which will eventually get some content // merged into it. The actual callee function doesn't matter here, so we // just pass it something to keep the ctor happy. std::vector<DSNodeHandle> ArgDummyVec; DSCallSite DummyCS(CI->getCallSite(), DSNodeHandle(), Callees[0]/*dummy*/, ArgDummyVec); IndCallGraph->getFunctionCalls().push_back(DummyCS); IndCallRecI = IndCallMap.insert(IndCallRecI, std::make_pair(Callees, IndCallGraph)); // Additionally, make sure that each of the callees inlines this graph // exactly once. DSCallSite *NCS = &IndCallGraph->getFunctionCalls().front(); for (unsigned i = 0, e = Callees.size(); i != e; ++i) { DSGraph* CalleeGraph = getDSGraph(Callees[i]); if (CalleeGraph != DSG) CallerEdges[CalleeGraph].push_back(CallerCallEdge(IndCallGraph, NCS, Callees[i])); } } // Now that we know which graph to use for this, merge the caller // information into the graph, based on information from the call site. ReachabilityCloner RC(IndCallGraph, DSG, 0); RC.mergeCallSite(IndCallGraph->getFunctionCalls().front(), *CI); } }
/// visitGraph - Visit the functions in the specified graph, updating the /// specified lattice values for all of their uses. /// void StructureFieldVisitorBase:: visitGraph(DSGraph &DSG, std::multimap<DSNode*, LatticeValue*> &NodeLVs) { assert(!NodeLVs.empty() && "No lattice values to compute!"); // To visit a graph, first step, we visit the instruction making up each // function in the graph, but ignore calls when processing them. We handle // call nodes explicitly by looking at call nodes in the graph if needed. We // handle instructions before calls to avoid interprocedural analysis if we // can drive lattice values to bottom early. // SFVInstVisitor IV(DSG, Callbacks, NodeLVs); for (DSGraph::retnodes_iterator FI = DSG.retnodes_begin(), E = DSG.retnodes_end(); FI != E; ++FI) for (Function::iterator BB = FI->first->begin(), E = FI->first->end(); BB != E; ++BB) for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ++I) if (IV.visit(*I) && NodeLVs.empty()) return; // Nothing left to analyze. // Keep track of which actual direct callees are handled. std::set<Function*> CalleesHandled; // Once we have visited all of the instructions in the function bodies, if // there are lattice values that have not been driven to bottom, see if any of // the nodes involved are passed into function calls. If so, we potentially // have to recursively traverse the call graph. for (DSGraph::fc_iterator CS = DSG.fc_begin(), E = DSG.fc_end(); CS != E; ++CS) { // Figure out the mapping from a node in the caller (potentially several) // nodes in the callee. DSGraph::NodeMapTy CallNodeMap; Instruction *TheCall = CS->getCallSite().getInstruction(); // If this is an indirect function call, assume nothing gets passed through // it. FIXME: THIS IS BROKEN! Just get the ECG for the fn ptr if it's not // direct. if (CS->isIndirectCall()) continue; // If this is an external function call, it cannot be involved with this // node, because otherwise the node would be marked incomplete! if (CS->getCalleeFunc()->isExternal()) continue; // If we can handle this function call, remove it from the set of direct // calls found by the visitor. CalleesHandled.insert(CS->getCalleeFunc()); std::vector<DSNodeHandle> Args; DSGraph *CG = &ECG.getDSGraph(*CS->getCalleeFunc()); CG->getFunctionArgumentsForCall(CS->getCalleeFunc(), Args); if (!CS->getRetVal().isNull()) DSGraph::computeNodeMapping(Args[0], CS->getRetVal(), CallNodeMap); for (unsigned i = 0, e = CS->getNumPtrArgs(); i != e; ++i) { if (i == Args.size()-1) break; DSGraph::computeNodeMapping(Args[i+1], CS->getPtrArg(i), CallNodeMap); } Args.clear(); // The mapping we just computed maps from nodes in the callee to nodes in // the caller, so we can't query it efficiently. Instead of going through // the trouble of inverting the map to do this (linear time with the size of // the mapping), we just do a linear search to see if any affected nodes are // passed into this call. bool CallCanModifyDataFlow = false; for (DSGraph::NodeMapTy::iterator MI = CallNodeMap.begin(), E = CallNodeMap.end(); MI != E; ++MI) if (NodeLVs.count(MI->second.getNode())) // Okay, the node is passed in, check to see if the call might do // something interesting to it (i.e. if analyzing the call can produce // anything other than "top"). if ((CallCanModifyDataFlow = NodeCanPossiblyBeInteresting(MI->first, Callbacks))) break; // If this function call cannot impact the analysis (either because the // nodes we are tracking are not passed into the call, or the DSGraph for // the callee tells us that analysis of the callee can't provide interesting // information), ignore it. if (!CallCanModifyDataFlow) continue; // Okay, either compute analysis results for the callee function, or reuse // results previously computed. std::multimap<DSNode*, LatticeValue*> &CalleeFacts = getCalleeFacts(*CG); // Merge all of the facts for the callee into the facts for the caller. If // this reduces anything in the caller to 'bottom', remove them. for (DSGraph::NodeMapTy::iterator MI = CallNodeMap.begin(), E = CallNodeMap.end(); MI != E; ++MI) { // If we have Lattice facts in the caller for this node in the callee, // merge any information from the callee into the caller. // If the node is not accessed in the callee at all, don't update. if (MI->first->getType() == Type::VoidTy) continue; // If there are no data-flow facts live in the caller for this node, don't // both processing it. std::multimap<DSNode*, LatticeValue*>::iterator NLVI = NodeLVs.find(MI->second.getNode()); if (NLVI == NodeLVs.end()) continue; // Iterate over all of the lattice values that have corresponding fields // in the callee, merging in information as we go. Be careful about the // fact that the callee may get passed the address of a substructure and // other funny games. //if (CalleeFacts.count(const_cast<DSNode*>(MI->first)) == 0) { DSNode *CalleeNode = const_cast<DSNode*>(MI->first); unsigned CalleeNodeOffset = MI->second.getOffset(); while (NLVI->first == MI->second.getNode()) { // Figure out what offset in the callee this field would land. unsigned FieldOff = NLVI->second->getFieldOffset()+CalleeNodeOffset; // If the field is not within the callee node, ignore it. if (FieldOff >= CalleeNode->getSize()) { ++NLVI; continue; } // Okay, check to see if we have a lattice value for the field at offset // FieldOff in the callee node. const LatticeValue *CalleeLV = 0; std::multimap<DSNode*, LatticeValue*>::iterator CFI = CalleeFacts.lower_bound(CalleeNode); for (; CFI != CalleeFacts.end() && CFI->first == CalleeNode; ++CFI) if (CFI->second->getFieldOffset() == FieldOff) { CalleeLV = CFI->second; // Found it! break; } // If we don't, the lattice value hit bottom and we should remove the // lattice value in the caller. if (!CalleeLV) { delete NLVI->second; // The lattice value hit bottom. NodeLVs.erase(NLVI++); continue; } // Finally, if we did find a corresponding entry, merge the information // into the caller's lattice value and keep going. if (NLVI->second->mergeInValue(CalleeLV)) { // Okay, merging these two caused the caller value to hit bottom. // Remove it. delete NLVI->second; // The lattice value hit bottom. NodeLVs.erase(NLVI++); } ++NLVI; // We successfully merged in some information! } // If we ran out of facts to prove, just exit. if (NodeLVs.empty()) return; } } // The local analysis pass inconveniently discards many local function calls // from the graph if they are to known functions. Loop over direct function // calls not handled above and visit them as appropriate. while (!IV.DirectCallSites.empty()) { Instruction *Call = *IV.DirectCallSites.begin(); IV.DirectCallSites.erase(IV.DirectCallSites.begin()); // Is this one actually handled by DSA? if (CalleesHandled.count(cast<Function>(Call->getOperand(0)))) continue; // Collect the pointers involved in this call. std::vector<Value*> Pointers; if (isa<PointerType>(Call->getType())) Pointers.push_back(Call); for (unsigned i = 1, e = Call->getNumOperands(); i != e; ++i) if (isa<PointerType>(Call->getOperand(i)->getType())) Pointers.push_back(Call->getOperand(i)); // If this is an intrinsic function call, figure out which one. unsigned IID = cast<Function>(Call->getOperand(0))->getIntrinsicID(); for (unsigned i = 0, e = Pointers.size(); i != e; ++i) { // If any of our lattice values are passed into this call, which is // specially handled by the local analyzer, inform the lattice function. DSNode *N = DSG.getNodeForValue(Pointers[i]).getNode(); for (std::multimap<DSNode*, LatticeValue*>::iterator LVI = NodeLVs.lower_bound(N); LVI != NodeLVs.end() && LVI->first == N;) { bool AtBottom = false; switch (IID) { default: AtBottom = LVI->second->visitRecognizedCall(*Call); break; case Intrinsic::memset: if (Callbacks & Visit::Stores) AtBottom = LVI->second->visitMemSet(*cast<CallInst>(Call)); break; } if (AtBottom) { delete LVI->second; NodeLVs.erase(LVI++); } else { ++LVI; } } } } }