void WherePostcondition::PrintUI(OutStream &out) const { PEdge *edge = m_frame->CFG()->GetSingleOutgoingEdge(m_point); Location *loc = m_frame->CFG()->GetPointLocation(m_point); if (edge->IsLoop()) { const char *loop_name = edge->AsLoop()->GetLoopId()->LoopName(); out << "LoopInvariant [" << loop_name << "]"; } else { out << "Postcondition ["; PEdgeCall *nedge = edge->AsCall(); Exp *function = nedge->GetFunction(); function->PrintUI(out, true); out << ":" << loc->Line() << "]"; } if (m_bit) { Bit *new_bit = BitConvertExitClobber(m_bit); out << " :: "; new_bit->PrintUI(out, true); new_bit->DecRef(); } }
// compute the points in the CFG reachable from the entry point. void GetEntryReachable(BlockCFG *cfg) { // worklist items are reachable points whose outgoing edges have // not been examined Vector<PPoint> worklist; PPoint entry = cfg->GetEntryPoint(); entry_reach_table->Insert(entry); worklist.PushBack(entry); while (!worklist.Empty()) { PPoint back = worklist.Back(); worklist.PopBack(); const Vector<PEdge*>& outgoing = cfg->GetOutgoingEdges(back); for (size_t oind = 0; oind < outgoing.Size(); oind++) { PEdge *edge = outgoing[oind]; PPoint next = edge->GetTarget(); // already did this target if (entry_reach_table->Lookup(next)) continue; entry_reach_table->Insert(next); worklist.PushBack(next); } } }
void CopyCFGPointsEdges(BlockCFG *old_cfg, BlockCFG *new_cfg) { // duplicate the CFG's points list. for (size_t pind = 0; pind < old_cfg->GetPointCount(); pind++) { Location *loc = old_cfg->GetPointLocation(pind + 1); loc->IncRef(); new_cfg->AddPoint(loc); } // set the entry/exit points. new_cfg->SetEntryPoint(old_cfg->GetEntryPoint()); new_cfg->SetExitPoint(old_cfg->GetExitPoint()); // duplicate the CFG's loop heads list. for (size_t lind = 0; lind < old_cfg->GetLoopHeadCount(); lind++) { const LoopHead &head = old_cfg->GetLoopHead(lind); if (head.end_location) head.end_location->IncRef(); new_cfg->AddLoopHead(head.point, head.end_location); } // duplicate the CFG's edges list. for (size_t eind = 0; eind < old_cfg->GetEdgeCount(); eind++) { PEdge *edge = old_cfg->GetEdge(eind); edge->IncRef(); new_cfg->AddEdge(edge); } }
// get the result of transitively following skip edges from point PPoint FollowSkipEdges(BlockCFG *cfg, PPoint point) { PPoint cur = point; bool changed = true; while (changed) { changed = false; const Vector<PEdge*> &outgoing = cfg->GetOutgoingEdges(cur); if (outgoing.Size() == 1) { PEdge *edge = outgoing[0]; if (edge->IsSkip()) { PPoint next = edge->GetTarget(); // watch out for skip edges aborting the function. if (next) { cur = next; changed = true; } } } } return cur; }
// marks the points in cfg which are isomorphic to points in the loop_cfg // invoked by cfg at the specified edge. code in a syntactic loop body // will be reflected in CFGs for both the loop and its parent if it may // reach both the recursive loop edge and a loop exit point. this common // code will be isomorphic between the two CFGs. void GetLoopIsomorphicPoints(BlockCFG *cfg, PEdge *loop_edge, BlockCFG *loop_cfg) { // mapping from points in cfg to isomorphic points in loop_cfg. PPointListHash remapping; // worklist items are isomorphic points whose outgoing edges have not // been examined. Vector<PPoint> worklist; PPoint target = loop_edge->GetTarget(); remapping.Insert(target, loop_cfg->GetEntryPoint()); cfg->AddLoopIsomorphic(target); worklist.PushBack(target); while (!worklist.Empty()) { PPoint cfg_point = worklist.Back(); worklist.PopBack(); PPoint loop_point = remapping.LookupSingle(cfg_point); const Vector<PEdge*> &cfg_outgoing = cfg->GetOutgoingEdges(cfg_point); const Vector<PEdge*> &loop_outgoing = loop_cfg->GetOutgoingEdges(loop_point); for (size_t eind = 0; eind < cfg_outgoing.Size(); eind++) { PEdge *edge = cfg_outgoing[eind]; PPoint target = edge->GetTarget(); // check for an existing remapping entry. some isomorphic points have // multiple incoming edges. we don't need to check all such incoming // edges; if any edge is isomorphic, they all will be. if (remapping.Lookup(target, false)) continue; // look for an equivalent outgoing edge from the loop. PPoint loop_target = 0; for (size_t lind = 0; lind < loop_outgoing.Size(); lind++) { PEdge *loop_edge = loop_outgoing[lind]; if (PEdge::CompareInner(edge, loop_edge) == 0) { loop_target = loop_edge->GetTarget(); break; } } if (!loop_target) { Assert(edge->IsAssume()); continue; } remapping.Insert(target, loop_target); cfg->AddLoopIsomorphic(target); worklist.PushBack(target); } } }
void R_s_k_2::draw_pedges(const float line_width) { int nb_edges = 0; int nb_pinned = 0; int nb_cyclic = 0; int nb_discart = 0; FT min_value = (std::numeric_limits<FT>::max)(); FT max_value = -(std::numeric_limits<FT>::max)(); std::vector<FT> values; std::vector<Edge> edges; for (Finite_edges_iterator ei = m_dt.finite_edges_begin(); ei != m_dt.finite_edges_end(); ++ei) { Edge edge = *ei; for (unsigned int i = 0; i < 2; ++i) { if (m_dt.is_pinned(edge)) { nb_pinned++; continue; } if (m_dt.is_target_cyclic(edge)) { nb_cyclic++; continue; } PEdge pedge; bool ok = create_pedge(edge, pedge); if (ok) { edges.push_back(edge); values.push_back(pedge.priority()); min_value = (std::min)(min_value, values.back()); max_value = (std::max)(max_value, values.back()); } else { nb_discart++; viewer->glColor3f(1.0, 0.0, 1.0); draw_edge(edge); } edge = m_dt.twin_edge(edge); nb_edges++; } } if (min_value == max_value) max_value += 1.0; std::size_t N = values.size(); for (unsigned int i = 0; i < N; ++i) draw_one_pedge(edges[i], values[i], min_value, max_value, line_width); std::cout << "There are: " << N << " pedges" << " x " << nb_discart << " discarted" << " x " << nb_pinned << " pinned" << " x " << nb_cyclic << " cyclic" << " = " << nb_edges << " edges" << std::endl; }
// fill in the body of loophead. points in the body are those which // are reachable from loophead over non-backedges, and which themselves // reach a backedge for loophead. note that in the case of loop nesting, // a point may be contained in the body of multiple loops. // if any irreducible edges are found (edges incoming to a body point // other than loophead whose source is not in the body), those edges // are added to irreducible_edges void GetLoopBody(BlockCFG *cfg, PPoint loophead, Vector<PEdge*> *irreducible_edges) { Vector<PPoint> *body_list = body_list_table->Lookup(loophead, true); Assert(body_list->Empty()); // worklist items are points which reach a loop backedge but whose // incoming edges have not yet been examined. Vector<PPoint> worklist; const Vector<PEdge*> &head_incoming = cfg->GetIncomingEdges(loophead); for (size_t iind = 0; iind < head_incoming.Size(); iind++) { PEdge *edge = head_incoming[iind]; PPoint source = edge->GetSource(); if (backedge_table->Lookup(edge)) { Assert(reach_table->Lookup(PPointPair(loophead, source))); if (!body_table->Insert(PPointPair(loophead, source))) { body_list->PushBack(source); worklist.PushBack(source); } } } // this should only be called on loops that have actual backedges Assert(!worklist.Empty()); while (!worklist.Empty()) { PPoint back = worklist.Back(); worklist.PopBack(); if (back == loophead) continue; const Vector<PEdge*> &incoming = cfg->GetIncomingEdges(back); for (size_t iind = 0; iind < incoming.Size(); iind++) { PEdge *edge = incoming[iind]; PPoint source = edge->GetSource(); if (reach_table->Lookup(PPointPair(loophead, source))) { if (!body_table->Insert(PPointPair(loophead, source))) { body_list->PushBack(source); worklist.PushBack(source); } } else if (entry_reach_table->Lookup(source)) { // the source is not reachable from the loophead. // this is an irreducible edge. irreducible_edges->PushBack(edge); } } } }
void PGraph::showGraph(){ for(int i = 0; i < nodes.size(); i++){ cout<<nodes[i]->getIndex()<<endl; for (PEdgeListIterator it = edges[i].begin(); it != edges[i].end(); ++it){ PEdge * e = *it; cout<<" "<<e->getFromNode()<<","<<e->getToNode()<<endl; } } }
list<PNode *> PGraph::getNeighbors(PNode* observer) { int observerIndex = observer->getIndex(); PEdgeList edgesNeighbors = edges[observerIndex]; list<PNode *> neighbors; for(PEdgeListIterator it = edgesNeighbors.begin(); it != edgesNeighbors.end(); ++it) { PEdge * e = *it; if (nodes[e->getFromNode()] != observer) { neighbors.push_back(nodes[e->getFromNode()]); } else { neighbors.push_back(nodes[e->getToNode()]); } } return neighbors; }
void WherePostcondition::PrintHook(OutStream &out) const { BlockId *id = m_frame->CFG()->GetId(); Variable *func_var = id->BaseVar(); PEdge *edge = m_frame->CFG()->GetSingleOutgoingEdge(m_point); if (edge->IsLoop()) { PEdgeLoop *nedge = edge->AsLoop(); out << nedge->GetLoopId()->Loop()->Value() << " " << func_var->GetName()->Value(); } else { PEdgeCall *nedge = edge->AsCall(); if (Variable *callee = nedge->GetDirectFunction()) { // direct call, just one hook function. out << "post " << callee->GetName()->Value(); } else { // indirect call, one hook function for each callee. CallEdgeSet *callees = CalleeCache.Lookup(func_var); bool found_callee = false; if (callees) { for (size_t eind = 0; eind < callees->GetEdgeCount(); eind++) { const CallEdge &edge = callees->GetEdge(eind); if (edge.where.id == id && edge.where.point == m_point) { if (found_callee) out << "$"; // add the separator found_callee = true; out << "post " << edge.callee->GetName()->Value(); } } } CalleeCache.Release(func_var); } } }
// get the set of points reachable from loophead over paths // that do not go through a backedge. if loophead itself is // reachable, it is irreducible and those new edges to it are added // as backedges. return value is true iff the loop is irreducible. bool GetLoopReachable(BlockCFG *cfg, PPoint loophead) { // worklist items are points in reach_table whose outgoing edges // have not been examined Vector<PPoint> worklist; if (!entry_reach_table->Lookup(loophead)) return false; reach_table->Insert(PPointPair(loophead, loophead)); worklist.PushBack(loophead); bool found_irreducible = false; while (!worklist.Empty()) { PPoint back = worklist.Back(); worklist.PopBack(); const Vector<PEdge*>& outgoing = cfg->GetOutgoingEdges(back); for (size_t oind = 0; oind < outgoing.Size(); oind++) { PEdge *edge = outgoing[oind]; PPoint next = edge->GetTarget(); if (backedge_table->Lookup(edge)) continue; if (next == loophead) { // we're in an irreducible loop. add the new edge to backedge_table. backedge_table->Insert(edge); found_irreducible = true; continue; } if (!reach_table->Insert(PPointPair(loophead, next))) worklist.PushBack(next); } } return found_irreducible; }
void R_s_k_2::draw_relevance(const float line_width, const int nb) { MultiIndex mindex; FT min_value = (std::numeric_limits<FT>::max)(); FT max_value = -(std::numeric_limits<FT>::max)(); unsigned int nb_initial = 0; for (Finite_edges_iterator ei = m_dt.finite_edges_begin(); ei != m_dt.finite_edges_end(); ++ei) { Edge edge = *ei; if (m_dt.is_ghost(edge)) continue; FT value = m_dt.get_edge_relevance(edge); // >= 0 nb_initial++; min_value = (std::min)(min_value, value); max_value = (std::max)(max_value, value); mindex.insert(PEdge(edge, value)); } if (min_value == max_value) max_value += 1.0; viewer->glLineWidth(line_width); int nb_remove = (std::min)(nb, int(mindex.size())); viewer->glColor3d(0.5, 0.1, 0.1); for (int i = 0; i < nb_remove; ++i) { PEdge pedge = *(mindex.get<1>()).begin(); (mindex.get<0>()).erase(pedge); } viewer->glColor3d(0.0, 0.5, 0.0); while (!mindex.empty()) { PEdge pedge = *(mindex.get<1>()).begin(); (mindex.get<0>()).erase(pedge); draw_edge(pedge.edge()); } }
bool CheckFrame(CheckerState *state, CheckerFrame *frame, CheckerPropagate *propagate) { Assert(!state->GetReportKind()); BlockMemory *mcfg = frame->Memory(); BlockCFG *cfg = mcfg->GetCFG(); BlockId *id = cfg->GetId(); if (checker_verbose.IsSpecified()) { logout << "CHECK: " << frame << ": Entering " << id << endl; if (propagate) propagate->Print(); } Where *where = propagate ? propagate->m_where : NULL; // check if we should terminate the search at this point (with or without // generating a report). if (where && where->IsNone()) { WhereNone *nwhere = where->AsNone(); ReportKind kind = nwhere->GetReportKind(); if (kind == RK_None) { if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Ignoring" << endl; return false; } else { if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Propagation failed" << endl; state->SetReport(kind); return true; } } // check for other propagations on the stack with frames for the same block, // and block the recursion if we exceed the checker's depth. we assume that // if we're ever going to terminate in the presence of recursion, we will // do so quickly. if (propagate) { if (uint32_t depth = checker_depth.UIntValue()) { Vector<CheckerFrame*> recurse_frames; for (size_t ind = 0; ind < state->m_stack.Size(); ind++) { CheckerFrame *other_frame = state->m_stack[ind]->m_frame; if (other_frame != frame && other_frame->Memory() == mcfg && !recurse_frames.Contains(other_frame)) recurse_frames.PushBack(other_frame); } if (recurse_frames.Size() >= depth) { state->SetReport(RK_Recursion); return true; } } } // check if we are propagating into some callee. if (where && where->IsPostcondition()) { WherePostcondition *nwhere = where->AsPostcondition(); // expand the callee at the specified point. PPoint point = nwhere->GetPoint(); PEdge *edge = cfg->GetSingleOutgoingEdge(point); if (edge->IsLoop()) { // expanding data from a loop. first try the case that the loop // does not execute at all. if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Trying to skip loop at " << point << endl; state->PushContext(); if (CheckSkipLoop(state, frame, point, nwhere)) return true; state->PopContext(); } if (BlockId *callee = edge->GetDirectCallee()) { // easy case, there is only a single callee. if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Expanding single callee at " << point << ": " << callee << endl; state->PushContext(); if (CheckSingleCallee(state, frame, point, nwhere, callee, true)) return true; state->PopContext(); } else { // iterate through all the possible callees Variable *function = id->BaseVar(); CallEdgeSet *callees = CalleeCache.Lookup(function); Vector<Variable*> callee_vars; if (callees) { for (size_t eind = 0; eind < callees->GetEdgeCount(); eind++) { const CallEdge &edge = callees->GetEdge(eind); if (edge.where.id == id && edge.where.point == point) callee_vars.PushBack(edge.callee); } } SortVector<Variable*,Variable>(&callee_vars); for (size_t cind = 0; cind < callee_vars.Size(); cind++) { Variable *callee = callee_vars[cind]; if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Expanding indirect callee at " << point << ": " << callee << endl; callee->IncRef(); BlockId *callee_id = BlockId::Make(B_Function, callee); state->PushContext(); if (CheckSingleCallee(state, frame, point, nwhere, callee_id, false)) { CalleeCache.Release(function); return true; } state->PopContext(); } if (callee_vars.Empty()) { if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": No callees to expand at " << point << endl; } CalleeCache.Release(function); } return false; } // any precondition we have to propagate up to the callers. WherePrecondition *precondition = NULL; if (where) precondition = where->IfPrecondition(); // whether we will be reconnecting to the caller without any // propagation information. bool reconnect_caller = false; if (precondition) { Bit *bit = precondition->GetBit(); WherePrecondition *dupe_precondition = new WherePrecondition(mcfg, bit); state->m_precondition_list.PushBack(dupe_precondition); } else { // we will propagate to the caller regardless if there is already a caller // hooked up or if we are inside a loop body. if (frame->GetCaller().id != NULL) reconnect_caller = true; if (frame->Kind() == B_Loop) reconnect_caller = true; } if (propagate && reconnect_caller) { // check to see if we are delaying any heap propagation. if (where->IsInvariant()) { Assert(state->m_delayed_propagate_heap == NULL); state->m_delayed_propagate_heap = propagate; } } else if (!precondition && !reconnect_caller) { // check to see if we are performing heap propagation. if (state->m_delayed_propagate_heap) { Assert(propagate == NULL); CheckerPropagate *heap_propagate = state->m_delayed_propagate_heap; state->m_delayed_propagate_heap = NULL; WhereInvariant *invariant = heap_propagate->m_where->AsInvariant(); if (CheckHeapWrites(state, frame, heap_propagate->m_frame, invariant)) return true; state->m_delayed_propagate_heap = heap_propagate; return false; } else if (where && where->IsInvariant()) { return CheckHeapWrites(state, frame, frame, where->AsInvariant()); } Assert(propagate); // don't need to expand the callers or anything else. // we can finally terminate propagation with an error report. if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Nothing to expand, finishing" << endl; state->SetReport(RK_Finished); return true; } if (frame->GetCaller().id != NULL) { // just propagate to the existing caller. if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Returning to caller" << endl; state->PushContext(); if (CheckSingleCaller(state, frame, precondition, frame->GetCaller())) return true; state->PopContext(); } else if (id->Kind() == B_Function) { // propagate to all callers to the function. Variable *function = id->BaseVar(); CallEdgeSet *callers = CallerCache.Lookup(function); Vector<BlockPPoint> caller_points; for (size_t eind = 0; callers && eind < callers->GetEdgeCount(); eind++) { const CallEdge &edge = callers->GetEdge(eind); Assert(edge.callee == function); caller_points.PushBack(edge.where); } SortVector<BlockPPoint,BlockPPoint>(&caller_points); for (size_t cind = 0; cind < caller_points.Size(); cind++) { BlockPPoint caller = caller_points[cind]; if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Checking caller: " << caller << endl; state->PushContext(); if (CheckSingleCaller(state, frame, precondition, caller)) { CallerCache.Release(function); return true; } state->PopContext(); } if (caller_points.Empty()) { if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": No callers to expand" << endl; } CallerCache.Release(function); } else if (id->Kind() == B_Loop) { // check all possible callers of the loop. unroll an iteration before // checking the parents so that if we can't figure out a sufficient // condition for the loop we will stop exploration quickly. // unroll another iteration of the loop. if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Unrolling loop iteration" << endl; state->PushContext(); BlockPPoint recursive_caller(id, cfg->GetExitPoint()); if (CheckSingleCaller(state, frame, precondition, recursive_caller)) return true; state->PopContext(); // check the parents which can initially invoke this loop. if (frame->GetLoopParent().id != NULL) { if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Checking existing loop parent: " << frame->GetLoopParent() << endl; state->PushContext(); if (CheckSingleCaller(state, frame, precondition, frame->GetLoopParent())) return true; state->PopContext(); } else { for (size_t pind = 0; pind < cfg->GetLoopParentCount(); pind++) { BlockPPoint where = cfg->GetLoopParent(pind); if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Checking loop parent: " << where << endl; state->PushContext(); if (CheckSingleCaller(state, frame, precondition, where)) return true; state->PopContext(); } } } else if (id->Kind() == B_Initializer) { // initializers don't have callers, can just ignore this. // TODO: should address why this code is being reached in the first place. if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Initializer has no callers" << endl; return false; } else { // unknown type of block. Assert(false); } // if we set the state's delayed heap propagation then unset it. if (propagate && state->m_delayed_propagate_heap == propagate) state->m_delayed_propagate_heap = NULL; return false; }
void GetMatchingHeapWrites(const EscapeAccess &heap_write, Vector<HeapWriteInfo> *writes) { BlockId *id = heap_write.where.id; BlockMemory *mcfg = GetBlockMemory(id); if (mcfg == NULL) { logout << "WARNING: Missing memory: '" << id << "'" << endl; return; } BlockCFG *cfg = mcfg->GetCFG(); // for incremental analysis, make sure the write CFG uses the right version. // as for checking callers, if the CFG has changed but the new one still // has a matching write, we will see an escape access for the new CFG. if (cfg->GetVersion() != heap_write.where.version) { if (checker_verbose.IsSpecified()) logout << "CHECK: Write is an older version: " << heap_write.where.id << ": " << heap_write.where.version << endl; mcfg->DecRef(); return; } PPoint point = heap_write.where.point; PPoint exit_point = mcfg->GetCFG()->GetExitPoint(); // find a point-relative lvalue written at the write point with // the sanitized representation from the heap_write trace. // TODO: we only match against direct assignments in the CFG for now, // ignoring structural copies (which are simple recursive writes). PEdge *edge = cfg->GetSingleOutgoingEdge(point); Exp *point_lval = NULL; if (PEdgeAssign *nedge = edge->IfAssign()) point_lval = nedge->GetLeftSide(); else if (PEdgeCall *nedge = edge->IfCall()) point_lval = nedge->GetReturnValue(); bool lval_matches = false; if (point_lval) { if (Exp *new_point_lval = Trace::SanitizeExp(point_lval)) { lval_matches = (new_point_lval == heap_write.target->GetValue()); new_point_lval->DecRef(); } } if (!lval_matches) { mcfg->DecRef(); return; } // it would be nice to remove Val() expressions from this list, but we can't // do that as lvalues in memory assignments can contain Val and we want to // see the effects of those assignments. TODO: fix. GuardExpVector lval_res; mcfg->TranslateExp(TRK_Point, point, point_lval, &lval_res); for (size_t ind = 0; ind < lval_res.Size(); ind++) { const GuardExp &lv = lval_res[ind]; HeapWriteInfo info; info.mcfg = mcfg; info.lval = lv.exp; info.base_lval = point_lval; // look for a condition where the lvalue is not written. GuardExpVector exit_vals; info.mcfg->GetValComplete(info.lval, NULL, exit_point, &exit_vals); for (size_t ind = 0; ind < exit_vals.Size(); ind++) { const GuardExp &val = exit_vals[ind]; // exclude cases where the lvalue refers to its value at block entry. if (ExpDrf *nval = val.exp->IfDrf()) { if (nval->GetTarget() == info.lval) info.exclude.PushBack(val.guard); } } if (!writes->Contains(info)) { info.mcfg->IncRef(writes); info.lval->IncRef(writes); info.base_lval->IncRef(writes); IncRefVector<Bit>(info.exclude, writes); writes->PushBack(info); } } mcfg->DecRef(); }
void BlockModset::ComputeModset(BlockMemory *mcfg, bool indirect) { static BaseTimer compute_timer("modset_compute"); Timer _timer(&compute_timer); // get any indirect callees for this function, provided they have been // computed and stored in the callee database (indirect is set). CallEdgeSet *indirect_callees = NULL; if (indirect) indirect_callees = CalleeCache.Lookup(m_id->BaseVar()); BlockCFG *cfg = mcfg->GetCFG(); for (size_t eind = 0; eind < cfg->GetEdgeCount(); eind++) { PEdge *edge = cfg->GetEdge(eind); PPoint point = edge->GetSource(); if (edge->IsAssign() || edge->IsCall()) { // process direct assignments along this edge. const Vector<GuardAssign>* assigns = mcfg->GetAssigns(point); if (assigns) { for (size_t aind = 0; aind < assigns->Size(); aind++) { const GuardAssign &gasn = assigns->At(aind); ProcessUpdatedLval(mcfg, gasn.left, NULL, true, false); Exp *use_lval = NULL; Exp *kind = mcfg->GetTerminateAssign(point, gasn.left, gasn.right, &use_lval); if (kind) { ProcessUpdatedLval(mcfg, use_lval, kind, false, false); kind->DecRef(); } } } } // pull in modsets from the direct and indirect callees of the edge. if (BlockId *callee = edge->GetDirectCallee()) { ComputeModsetCall(mcfg, edge, callee, NULL); callee->DecRef(); } else if (edge->IsCall() && indirect_callees) { for (size_t ind = 0; ind < indirect_callees->GetEdgeCount(); ind++) { const CallEdge &cedge = indirect_callees->GetEdge(ind); // when comparing watch out for the case that this is a temporary // modset and does not share the same block kind as the edge point. if (cedge.where.version == cfg->GetVersion() && cedge.where.point == point && cedge.where.id->Function() == m_id->Function() && cedge.where.id->Loop() == m_id->Loop()) { cedge.callee->IncRef(); BlockId *callee = BlockId::Make(B_Function, cedge.callee); ComputeModsetCall(mcfg, edge, callee, cedge.rfld_chain); callee->DecRef(); } } } } // sort the modset exps to ensure a consistent representation. if (m_modset_list) SortVector<PointValue,compare_PointValue>(m_modset_list); if (m_assign_list) SortVector<GuardAssign,compare_GuardAssign>(m_assign_list); if (indirect) CalleeCache.Release(m_id->BaseVar()); }
void BlockSummary::GetAssumedBits(BlockMemory *mcfg, PPoint end_point, Vector<AssumeInfo> *assume_list) { BlockId *id = mcfg->GetId(); BlockCFG *cfg = mcfg->GetCFG(); BlockSummary *sum = GetBlockSummary(id); const Vector<Bit*> *assumes = sum->GetAssumes(); size_t assume_count = VectorSize<Bit*>(assumes); // pull in assumptions from the summary for mcfg. in some cases these // assumptions won't be useful, e.g. describing the state at exit // for functions. for now we're just adding all of them though. TODO: fix. for (size_t ind = 0; ind < assume_count; ind++) { Bit *bit = assumes->At(ind); bit->IncRef(assume_list); AssumeInfo info; info.bit = bit; assume_list->PushBack(info); } sum->DecRef(); Vector<BlockCFG*> *annot_list = BodyAnnotCache.Lookup(id->Function()); // add assumes at function entry for any preconditions. if (id->Kind() == B_Function) { for (size_t ind = 0; annot_list && ind < annot_list->Size(); ind++) { BlockCFG *annot_cfg = annot_list->At(ind); if (annot_cfg->GetAnnotationKind() != AK_Precondition && annot_cfg->GetAnnotationKind() != AK_PreconditionAssume) continue; Bit *bit = BlockMemory::GetAnnotationBit(annot_cfg); if (!bit) continue; annot_cfg->IncRef(assume_list); bit->IncRef(assume_list); AssumeInfo info; info.annot = annot_cfg; info.bit = bit; assume_list->PushBack(info); } } // add assumptions from points within the block. for (size_t pind = 0; pind < cfg->GetPointAnnotationCount(); pind++) { PointAnnotation pann = cfg->GetPointAnnotation(pind); if (end_point && pann.point >= end_point) continue; BlockCFG *annot_cfg = GetAnnotationCFG(pann.annot); if (!annot_cfg) continue; Assert(annot_cfg->GetAnnotationKind() != AK_AssertRuntime); if (Bit *bit = BlockMemory::GetAnnotationBit(annot_cfg)) { // get the annotation bit in terms of block entry. Bit *point_bit = NULL; mcfg->TranslateBit(TRK_Point, pann.point, bit, &point_bit); point_bit->MoveRef(&point_bit, assume_list); annot_cfg->IncRef(assume_list); AssumeInfo info; info.annot = annot_cfg; info.point = pann.point; info.bit = point_bit; assume_list->PushBack(info); } annot_cfg->DecRef(); } // add assumptions from annotation edges within the block, invariants // on values accessed by the block, and from the summaries of any callees. for (size_t eind = 0; eind < cfg->GetEdgeCount(); eind++) { PEdge *edge = cfg->GetEdge(eind); PPoint point = edge->GetSource(); if (end_point && point >= end_point) continue; InvariantAssumeVisitor visitor(mcfg, point, assume_list); edge->DoVisit(&visitor); if (PEdgeAnnotation *nedge = edge->IfAnnotation()) { // add an assumption for this annotation. BlockCFG *annot_cfg = GetAnnotationCFG(nedge->GetAnnotationId()); if (!annot_cfg) continue; Bit *bit = BlockMemory::GetAnnotationBit(annot_cfg); // don't incorporate AssertRuntimes, these are not assumed. if (bit && annot_cfg->GetAnnotationKind() != AK_AssertRuntime) { // get the annotation bit in terms of block entry. Bit *point_bit = NULL; mcfg->TranslateBit(TRK_Point, point, bit, &point_bit); point_bit->MoveRef(&point_bit, assume_list); annot_cfg->IncRef(assume_list); AssumeInfo info; info.annot = annot_cfg; info.point = point; info.bit = point_bit; assume_list->PushBack(info); } annot_cfg->DecRef(); } if (BlockId *callee = edge->GetDirectCallee()) { GetCallAssumedBits(mcfg, edge, callee, false, assume_list); callee->DecRef(); } else if (edge->IsCall()) { // add conditional assumes for the indirect targets of the call. // this is most useful for baked information and annotations, where // we sometimes need to attach information at indirect calls. CallEdgeSet *callees = CalleeCache.Lookup(id->BaseVar()); size_t old_count = assume_list->Size(); if (callees) { for (size_t cind = 0; cind < callees->GetEdgeCount(); cind++) { const CallEdge &cedge = callees->GetEdge(cind); if (cedge.where.id == id && cedge.where.point == point) { cedge.callee->IncRef(); BlockId *callee = BlockId::Make(B_Function, cedge.callee); GetCallAssumedBits(mcfg, edge, callee, true, assume_list); callee->DecRef(); } } } if (assume_list->Size() != old_count) { // we managed to do something at this indirect call site. // add another assumption restricting the possible callees to // only those identified by our callgraph. GuardExpVector receiver_list; mcfg->TranslateReceiver(point, &receiver_list); for (size_t rind = 0; rind < receiver_list.Size(); rind++) { const GuardExp &gs = receiver_list[rind]; gs.guard->IncRef(); // make a bit: !when || rcv == callee0 || rcv == callee1 || ... Bit *extra_bit = Bit::MakeNot(gs.guard); for (size_t cind = 0; cind < callees->GetEdgeCount(); cind++) { const CallEdge &cedge = callees->GetEdge(cind); if (cedge.where.id == id && cedge.where.point == point) { Variable *callee_var = cedge.callee; callee_var->IncRef(); Exp *callee_exp = Exp::MakeVar(callee_var); gs.exp->IncRef(); Bit *equal = Exp::MakeCompareBit(B_Equal, callee_exp, gs.exp); extra_bit = Bit::MakeOr(extra_bit, equal); } } extra_bit->MoveRef(NULL, assume_list); AssumeInfo info; info.bit = extra_bit; assume_list->PushBack(info); } } CalleeCache.Release(id->BaseVar()); } } BodyAnnotCache.Release(id->Function()); // add assumptions from heap invariants describing values mentioned // in added assumptions. we could keep doing this transitively but don't, // to ensure termination. size_t count = assume_list->Size(); for (size_t ind = 0; ind < count; ind++) { InvariantAssumeVisitor visitor(NULL, 0, assume_list); assume_list->At(ind).bit->DoVisit(&visitor); } CombineAssumeList(assume_list); }
void TopoSortCFG(BlockCFG *cfg) { // can't topo sort a CFG that might have loops. Assert(cfg->GetLoopHeadCount() == 0); // map from old CFG points to the new points in the topo order. we can only // add a new point once we've added all its predecessors. PPointListHash remapping; // points in the remappping, in the order to add them to the CFG. Vector<Location*> new_points; // map from new points back to original CFG points. Vector<PPoint> old_points; // worklist items are the points where the sources of incoming edges have // already been added to the remapping, the point itself has not. Vector<PPoint> worklist; PPoint entry_point = cfg->GetEntryPoint(); PPoint exit_point = cfg->GetExitPoint(); // seed the worklist. worklist.PushBack(entry_point); while (!worklist.Empty()) { // pick the point from the worklist with the minimum line number. // if there is code like: // if (x) // a; // else // b; // we could add either a or b to the remapping first, but we want to add // a first. the ordering of points is used for naming loops, and we want // this ordering to be deterministic and map back to the code predictably. size_t best_index = 0; size_t best_line = cfg->GetPointLocation(worklist[0])->Line(); for (size_t ind = 1; ind < worklist.Size(); ind++) { size_t new_line = cfg->GetPointLocation(worklist[ind])->Line(); if (new_line < best_line) { best_index = ind; best_line = new_line; } } PPoint point = worklist[best_index]; worklist[best_index] = worklist.Back(); worklist.PopBack(); Assert(!remapping.Lookup(point, false)); Location *loc = cfg->GetPointLocation(point); loc->IncRef(); new_points.PushBack(loc); old_points.PushBack(point); remapping.Insert(point, new_points.Size()); const Vector<PEdge*> &outgoing = cfg->GetOutgoingEdges(point); for (size_t oind = 0; oind < outgoing.Size(); oind++) { PEdge *edge = outgoing[oind]; PPoint target = edge->GetTarget(); // this can happen if there are multiple edges from the worklist point // to the target, e.g. 'if (x) {}'. not going to happen much. if (worklist.Contains(target)) continue; Assert(!remapping.Lookup(target, false)); // we can add the target to the worklist if it has no incoming edges // from points not in the remapping. bool missing_incoming = false; const Vector<PEdge*> &incoming = cfg->GetIncomingEdges(target); for (size_t iind = 0; iind < incoming.Size(); iind++) { PEdge *edge = incoming[iind]; PPoint source = edge->GetSource(); if (!remapping.Lookup(source, false)) { missing_incoming = true; break; } } if (!missing_incoming) worklist.PushBack(target); } } // this assert will fail if either the CFG contains cycles, or if there are // nodes unreachable from the start. neither of these cases should be // possible here. Assert(new_points.Size() == cfg->GetPointCount()); Assert(old_points.Size() == cfg->GetPointCount()); // remap all the edges. this is also done so that the edges will be // in topological order according to their source points. Vector<PEdge*> new_edges; for (size_t pind = 0; pind < old_points.Size(); pind++) { const Vector<PEdge*> &edges = cfg->GetOutgoingEdges(old_points[pind]); for (size_t eind = 0; eind < edges.Size(); eind++) { PEdge *edge = edges[eind]; PPoint new_source = remapping.LookupSingle(edge->GetSource()); PPoint new_target = remapping.LookupSingle(edge->GetTarget()); PEdge *new_edge = PEdge::ChangeEdge(edge, new_source, new_target); new_edges.PushBack(new_edge); } } // clear out the initial CFG. cfg->ClearBody(); // add the new points, edges, annotations. for (size_t pind = 0; pind < new_points.Size(); pind++) cfg->AddPoint(new_points[pind]); for (size_t eind = 0; eind < new_edges.Size(); eind++) cfg->AddEdge(new_edges[eind]); // set the new entry point. this had better be the first point in the order. PPoint new_entry_point = remapping.LookupSingle(entry_point); Assert(new_entry_point == 1); cfg->SetEntryPoint(new_entry_point); if (exit_point) { // set the new exit point. this had better be the last point in the order. PPoint new_exit_point = remapping.LookupSingle(exit_point); Assert(new_exit_point == new_points.Size()); cfg->SetExitPoint(new_exit_point); } }
void TrimUnreachable(BlockCFG *cfg, bool flatten_skips) { // can't flatten skips if there might be loops in the CFG. Assert(!flatten_skips || cfg->GetLoopHeadCount() == 0); // receives the locations of the new points and edges of the CFG. we will // fill these in, then replace wholesale the old points/edges on the CFG. Vector<Location*> new_points; Vector<PEdge*> new_edges; Vector<LoopHead> new_loop_heads; Vector<PPoint> worklist; // get the set of points reachable from CFG entry. // worklist items are points in entry_reachable whose outgoing edges // have not been examined. PPointHash entry_reachable; PPoint entry = cfg->GetEntryPoint(); entry_reachable.Insert(entry); worklist.PushBack(entry); while (!worklist.Empty()) { PPoint back = worklist.Back(); worklist.PopBack(); const Vector<PEdge*> &outgoing = cfg->GetOutgoingEdges(back); for (size_t oind = 0; oind < outgoing.Size(); oind++) { PEdge *edge = outgoing[oind]; PPoint next = edge->GetTarget(); if (!entry_reachable.Lookup(next)) { entry_reachable.Insert(next); worklist.PushBack(next); } } } // get the set of points which reach the CFG exit. // worklist items are points in exit_reaches whose incoming edges // have not been examined. PPointHash exit_reaches; PPoint exit = cfg->GetExitPoint(); exit_reaches.Insert(exit); worklist.PushBack(exit); while (!worklist.Empty()) { PPoint back = worklist.Back(); worklist.PopBack(); const Vector<PEdge*> &incoming = cfg->GetIncomingEdges(back); for (size_t iind = 0; iind < incoming.Size(); iind++) { PEdge *edge = incoming[iind]; PPoint prev = edge->GetSource(); if (!exit_reaches.Lookup(prev)) { exit_reaches.Insert(prev); worklist.PushBack(prev); } } } // make sure we include the entry regardless of whether the function // has a path from entry to exit. exit_reaches.Insert(entry); if (flatten_skips) exit_reaches.Insert(FollowSkipEdges(cfg, entry)); // map from old points to corresponding new points. only defined for // points that are in both entry_reachable and exit_reaches, // and that do not have outgoing skip edges (if flatten_skips is set). PPointListHash remapping; // map from some old p0 to another old p1 where p0 connects to p1 by // skip edges and p1 has no outgoing skips. empty if flatten_skips is // not set. only defined if remapping is defined for p1. PPointListHash skip_remapping; for (PPoint point = 1; point <= cfg->GetPointCount(); point++) { if (entry_reachable.Lookup(point) && exit_reaches.Lookup(point)) { // if this is just the source of some skip edges flatten them out. // the target of the skips will be defined by remapping since // there can be only one outgoing skip edge from a point and // thus all paths from point pass through target_point; if point // reaches the exit then so does target_point. if (flatten_skips) { PPoint target_point = FollowSkipEdges(cfg, point); if (target_point != point) { skip_remapping.Insert(point, target_point); // don't add anything to remapping for point continue; } } Location *loc = cfg->GetPointLocation(point); loc->IncRef(); new_points.PushBack(loc); PPoint new_point = new_points.Size(); remapping.Insert(point, new_point); } } for (size_t eind = 0; eind < cfg->GetEdgeCount(); eind++) { PEdge *edge = cfg->GetEdge(eind); PPoint source = edge->GetSource(); PPoint target = edge->GetTarget(); if (skip_remapping.Lookup(source, false)) continue; // flatten any skips after the target point Vector<PPoint> *skip_target_list = skip_remapping.Lookup(target, false); if (skip_target_list) { Assert(skip_target_list->Size() == 1); target = skip_target_list->At(0); } Vector<PPoint> *new_source_list = remapping.Lookup(source, false); Vector<PPoint> *new_target_list = remapping.Lookup(target, false); if (new_source_list && new_target_list) { Assert(new_source_list->Size() == 1); Assert(new_target_list->Size() == 1); PPoint new_source = new_source_list->At(0); PPoint new_target = new_target_list->At(0); PEdge *new_edge = PEdge::ChangeEdge(edge, new_source, new_target); new_edges.PushBack(new_edge); } } for (size_t lind = 0; lind < cfg->GetLoopHeadCount(); lind++) { const LoopHead &head = cfg->GetLoopHead(lind); // don't check skip_remapping because we don't allow skip flattening // when the CFG still has loops in it Vector<PPoint> *new_point_list = remapping.Lookup(head.point, false); if (new_point_list) { Assert(new_point_list->Size() == 1); LoopHead new_head(new_point_list->At(0), head.end_location); if (head.end_location) head.end_location->IncRef(); new_loop_heads.PushBack(new_head); } } // clear out the initial CFG. cfg->ClearBody(); // add the new points, edges, loop heads. for (size_t pind = 0; pind < new_points.Size(); pind++) cfg->AddPoint(new_points[pind]); for (size_t eind = 0; eind < new_edges.Size(); eind++) cfg->AddEdge(new_edges[eind]); for (size_t lind = 0; lind < new_loop_heads.Size(); lind++) cfg->AddLoopHead(new_loop_heads[lind].point, new_loop_heads[lind].end_location); // set the new entry and exit points of the CFG. // the entry may be connected to skip edges Vector<PPoint> *skip_entry_list = skip_remapping.Lookup(entry, false); if (skip_entry_list) { Assert(skip_entry_list->Size() == 1); entry = skip_entry_list->At(0); } PPoint new_entry = remapping.LookupSingle(entry); PPoint new_exit = 0; Vector<PPoint> *new_exit_list = remapping.Lookup(exit, false); if (new_exit_list) { Assert(new_exit_list->Size() == 1); new_exit = new_exit_list->At(0); } cfg->SetEntryPoint(new_entry); cfg->SetExitPoint(new_exit); }
void InferSummaries(const Vector<BlockSummary*> &summary_list) { static BaseTimer infer_timer("infer_summaries"); Timer _timer(&infer_timer); if (summary_list.Empty()) return; Variable *function = summary_list[0]->GetId()->BaseVar(); Vector<BlockCFG*> *annot_list = BodyAnnotCache.Lookup(function->GetName()); // all traces which might refer to the result of pointer arithmetic. Vector<Exp*> arithmetic_list; ArithmeticEscape escape(function, arithmetic_list); // initial pass over the CFGs to get traces used in pointer arithmetic. for (size_t ind = 0; ind < summary_list.Size(); ind++) { BlockSummary *sum = summary_list[ind]; BlockCFG *cfg = sum->GetMemory()->GetCFG(); for (size_t eind = 0; eind < cfg->GetEdgeCount(); eind++) { PEdge *edge = cfg->GetEdge(eind); if (PEdgeAssign *assign_edge = edge->IfAssign()) { Exp *left = assign_edge->GetLeftSide(); Exp *right = assign_edge->GetRightSide(); ProcessArithmeticAssign(&escape, cfg->GetId(), left, right); } } } for (size_t ind = 0; ind < summary_list.Size(); ind++) { BlockSummary *sum = summary_list[ind]; BlockMemory *mcfg = sum->GetMemory(); BlockCFG *cfg = mcfg->GetCFG(); // accumulate all the assertions at points in the CFG. Vector<AssertInfo> asserts; // add assertions at function exit for any postconditions. if (cfg->GetId()->Kind() == B_Function) { for (size_t aind = 0; annot_list && aind < annot_list->Size(); aind++) { BlockCFG *annot_cfg = annot_list->At(aind); if (annot_cfg->GetAnnotationKind() != AK_Postcondition) continue; if (Bit *bit = BlockMemory::GetAnnotationBit(annot_cfg)) { AssertInfo info; info.kind = ASK_Annotation; info.cls = ASC_Check; info.point = cfg->GetExitPoint(); info.bit = bit; asserts.PushBack(info); } } } // add assertions for any point annotations within the CFG. for (size_t pind = 0; pind < cfg->GetPointAnnotationCount(); pind++) { PointAnnotation pann = cfg->GetPointAnnotation(pind); BlockCFG *annot_cfg = GetAnnotationCFG(pann.annot); if (!annot_cfg) continue; if (annot_cfg->GetAnnotationKind() != AK_Assert) continue; if (Bit *bit = BlockMemory::GetAnnotationBit(annot_cfg)) { AssertInfo info; info.kind = ASK_Annotation; info.cls = ASC_Check; info.point = pann.point; info.bit = bit; asserts.PushBack(info); } } for (size_t eind = 0; eind < cfg->GetEdgeCount(); eind++) { PEdge *edge = cfg->GetEdge(eind); PPoint point = edge->GetSource(); if (PEdgeAnnotation *nedge = edge->IfAnnotation()) { // add an assertion for this annotation if it not an assume. BlockCFG *annot_cfg = GetAnnotationCFG(nedge->GetAnnotationId()); if (!annot_cfg) continue; if (annot_cfg->GetAnnotationKind() != AK_Assert && annot_cfg->GetAnnotationKind() != AK_AssertRuntime) { continue; } if (Bit *bit = BlockMemory::GetAnnotationBit(annot_cfg)) { AssertInfo info; info.kind = (annot_cfg->GetAnnotationKind() == AK_Assert) ? ASK_Annotation : ASK_AnnotationRuntime; info.cls = ASC_Check; info.point = point; info.bit = bit; asserts.PushBack(info); } } // add assertions for any invariants affected by a write. Exp *left = NULL; if (PEdgeAssign *nedge = edge->IfAssign()) left = nedge->GetLeftSide(); if (PEdgeCall *nedge = edge->IfCall()) left = nedge->GetReturnValue(); // for now our detection of affected invariants is pretty crude; // writes to fields can affect type invariants on the field's type // which use that field, and writes to global variables can affect // invariants on that global. TODO: pin this down once we draw a // precise line between which invariants can and can't be checked. if (left && left->IsFld()) { ExpFld *nleft = left->AsFld(); String *csu_name = nleft->GetField()->GetCSUType()->GetCSUName(); Vector<BlockCFG*> *comp_annot_list = CompAnnotCache.Lookup(csu_name); for (size_t aind = 0; comp_annot_list && aind < comp_annot_list->Size(); aind++) { BlockCFG *annot_cfg = comp_annot_list->At(aind); if (annot_cfg->GetAnnotationKind() != AK_Invariant) continue; Bit *bit = BlockMemory::GetAnnotationBit(annot_cfg); if (!bit) continue; Vector<Exp*> lval_list; LvalListVisitor visitor(&lval_list); bit->DoVisit(&visitor); bool uses_field = false; for (size_t ind = 0; ind < lval_list.Size(); ind++) { if (ExpFld *lval = lval_list[ind]->IfFld()) { if (lval->GetField() == nleft->GetField()) uses_field = true; } } if (uses_field) { // this is a type invariant which uses the field being written // as an lvalue. we need to assert this write preserves // the invariant. BlockId *id = annot_cfg->GetId(); Variable *this_var = Variable::Make(id, VK_This, NULL, 0, NULL); Exp *this_exp = Exp::MakeVar(this_var); Exp *this_drf = Exp::MakeDrf(this_exp); Bit *new_bit = BitReplaceExp(bit, this_drf, nleft->GetTarget()); AssertInfo info; info.kind = ASK_Invariant; info.cls = ASC_Check; info.point = point; info.bit = new_bit; asserts.PushBack(info); } } CompAnnotCache.Release(csu_name); } if (left && left->IsVar()) { Variable *var = left->AsVar()->GetVariable(); if (var->Kind() == VK_Glob) { Vector<BlockCFG*> *glob_annot_list = InitAnnotCache.Lookup(var->GetName()); for (size_t aind = 0; glob_annot_list && aind < glob_annot_list->Size(); aind++) { BlockCFG *annot_cfg = glob_annot_list->At(aind); Bit *bit = BlockMemory::GetAnnotationBit(annot_cfg); if (!bit) continue; AssertInfo info; info.kind = ASK_Invariant; info.cls = ASC_Check; info.point = point; info.bit = bit; asserts.PushBack(info); } InitAnnotCache.Release(var->GetName()); } } if (PEdgeCall *nedge = edge->IfCall()) { // add assertions for any callee preconditions. // pull preconditions from both direct and indirect calls. Vector<Variable*> callee_names; if (Variable *callee = nedge->GetDirectFunction()) { callee_names.PushBack(callee); } else { CallEdgeSet *callees = CalleeCache.Lookup(function); if (callees) { for (size_t cind = 0; cind < callees->GetEdgeCount(); cind++) { const CallEdge &edge = callees->GetEdge(cind); if (edge.where.id == cfg->GetId() && edge.where.point == point) callee_names.PushBack(edge.callee); } } // CalleeCache release is below. } for (size_t cind = 0; cind < callee_names.Size(); cind++) { String *callee = callee_names[cind]->GetName(); Vector<BlockCFG*> *call_annot_list = BodyAnnotCache.Lookup(callee); for (size_t aind = 0; call_annot_list && aind < call_annot_list->Size(); aind++) { BlockCFG *annot_cfg = call_annot_list->At(aind); if (annot_cfg->GetAnnotationKind() != AK_Precondition) continue; if (Bit *bit = BlockMemory::GetAnnotationBit(annot_cfg)) { ConvertCallsiteMapper mapper(cfg, point, false); Bit *caller_bit = bit->DoMap(&mapper); if (!caller_bit) continue; AssertInfo info; info.kind = ASK_Annotation; info.cls = ASC_Check; info.point = point; info.bit = caller_bit; asserts.PushBack(info); } } BodyAnnotCache.Release(callee); } if (!nedge->GetDirectFunction()) CalleeCache.Release(function); } BufferScanVisitor write_visitor(asserts, arithmetic_list, point, true); BufferScanVisitor read_visitor(asserts, arithmetic_list, point, false); IntegerScanVisitor integer_visitor(asserts, point); GCScanVisitor gcsafe_visitor(asserts, point); // only look at the written lvalues for the write visitor. if (PEdgeAssign *assign = edge->IfAssign()) write_visitor.Visit(assign->GetLeftSide()); if (PEdgeCall *call = edge->IfCall()) { if (Exp *returned = call->GetReturnValue()) write_visitor.Visit(returned); } edge->DoVisit(&read_visitor); // disable integer overflow visitor for now. // edge->DoVisit(&integer_visitor); edge->DoVisit(&gcsafe_visitor); } if (cfg->GetId()->Kind() == B_Function) { BlockModset *modset = GetBlockModset(cfg->GetId()); if (modset->CanGC()) { AssertInfo info; info.kind = ASK_CanGC; info.cls = ASC_Check; info.point = cfg->GetExitPoint(); String *name = cfg->GetId()->BaseVar()->GetName(); Variable *var = Variable::Make(NULL, VK_Glob, name, 0, name); Exp *varexp = Exp::MakeVar(var); Exp *gcsafe = Exp::MakeGCSafe(varexp, false); info.bit = Bit::MakeVar(gcsafe); asserts.PushBack(info); } } MarkRedundantAssertions(mcfg, asserts); // move the finished assertion list into the summary. for (size_t ind = 0; ind < asserts.Size(); ind++) { const AssertInfo &info = asserts[ind]; sum->AddAssert(info.kind, info.cls, info.point, info.bit); } } // infer delta and termination invariants for all summaries. for (size_t ind = 0; ind < summary_list.Size(); ind++) InferInvariants(summary_list[ind], arithmetic_list); BodyAnnotCache.Release(function->GetName()); }
// determine whether loophead is a reducible loop with backedges in cfg. // fill in dominate_table with the points dominated by loophead, // and add as backedges any edge going to loophead which is itself // dominated by loophead. return true if any backedges were found. bool GetLoopBackedges(BlockCFG *cfg, PPoint loophead) { // compute the nodes reachable from the entry point other than // through start. the dominated points are the dual of this set. // points reachable from the start according to the above criteria PPointHash reachable; // worklist items are points in reachable whose outgoing edges have // not been examined Vector<PPoint> worklist; if (!entry_reach_table->Lookup(loophead)) return false; PPoint entry = cfg->GetEntryPoint(); reachable.Insert(entry); worklist.PushBack(entry); while (!worklist.Empty()) { PPoint back = worklist.Back(); worklist.PopBack(); const Vector<PEdge*>& outgoing = cfg->GetOutgoingEdges(back); for (size_t oind = 0; oind < outgoing.Size(); oind++) { PEdge *edge = outgoing[oind]; PPoint next = edge->GetTarget(); if (next == loophead) continue; // already did this target if (reachable.Lookup(next)) continue; reachable.Insert(next); worklist.PushBack(next); } } // compute the set of dominated points. this is the difference // between the points reachable from the CFG entry, and the points // in the reach table we just computed. for (PPoint point = 1; point <= cfg->GetPointCount(); point++) { if (!reachable.Lookup(point) && entry_reach_table->Lookup(point)) dominate_table->Insert(PPointPair(loophead, point)); } // backedges on the loophead are incoming edges whose source is // dominated by the loophead bool found_backedge = false; const Vector<PEdge*> &incoming = cfg->GetIncomingEdges(loophead); for (size_t eind = 0; eind < incoming.Size(); eind++) { PEdge *edge = incoming[eind]; if (dominate_table->Lookup(PPointPair(loophead, edge->GetSource()))) { backedge_table->Insert(edge); found_backedge = true; } } return found_backedge; }
// clone a loop body, mapping each point in the body of loophead // to a new point in receive_cfg. receive_cfg will receive new points // for the cloned body, and new edges for any non-backedge whose source // and target are both in loophead's body. for other edges involving loophead, // old_entry/exit/backedge_indexes will be filled in with indexes into // the edges list of base_cfg. it may be that base_cfg == receive_cfg. void CloneLoopBody(BlockCFG *base_cfg, PPoint loophead, PPointListHash *remapping, BlockCFG *receive_cfg, Vector<size_t> *old_entry_indexes, Vector<size_t> *old_exit_indexes, Vector<size_t> *old_back_indexes) { Assert(remapping->IsEmpty()); Vector<PPoint> *body_list = body_list_table->Lookup(loophead, true); Assert(!body_list->Empty()); // allow for base_cfg == receive_cfg, so keep track of how many points and // edges were originally in base_cfg before modifying receive_cfg. size_t init_points_size = base_cfg->GetPointCount(); size_t init_edges_size = base_cfg->GetEdgeCount(); // copy all points in the loop body to the new CFG. for (size_t bind = 0; bind < body_list->Size(); bind++) { PPoint body_point = body_list->At(bind); Assert(remapping->Lookup(body_point, false) == NULL); Assert(0 < body_point && body_point <= init_points_size); Location *loc = base_cfg->GetPointLocation(body_point); loc->IncRef(); PPoint new_point = receive_cfg->AddPoint(loc); remapping->Insert(body_point, new_point); } // copy all edges between points in the body to the new CFG. for (size_t eind = 0; eind < init_edges_size; eind++) { PEdge *edge = base_cfg->GetEdge(eind); PPoint source = edge->GetSource(); PPoint target = edge->GetTarget(); if (!entry_reach_table->Lookup(source)) continue; PPoint new_source = 0; if (body_table->Lookup(PPointPair(loophead, source))) new_source = remapping->LookupSingle(source); PPoint new_target = 0; if (body_table->Lookup(PPointPair(loophead, target))) new_target = remapping->LookupSingle(target); if (!new_source && !new_target) { // edge is not involved with this loop. leave it alone } else if (!new_source && new_target) { // entry edge. leave it alone old_entry_indexes->PushBack(eind); } else if (new_source && !new_target) { // exit edge. leave it alone old_exit_indexes->PushBack(eind); } else { Assert(new_source && new_target); if (target == loophead) { // back edge. leave it alone Assert(backedge_table->Lookup(edge)); old_back_indexes->PushBack(eind); } else { // inner edge. clone the edge for the new source and target PEdge *new_edge = PEdge::ChangeEdge(edge, new_source, new_target); receive_cfg->AddEdge(new_edge); } } } }
Where* CheckerPropagate::TryPropagate(Bit *bit, Exp *lval) { BlockMemory *mcfg = m_frame->Memory(); TypeCSU *csu = NULL; Exp *csu_lval = NULL; if (UseHeapExp(lval, &csu, &csu_lval)) { // do the heap propagation unless we are trying to push heap data // up into the caller. if (!m_prefer_precondition || !UseCallerExp(lval, m_frame->Kind() == B_Function) || (m_frame->Kind() == B_Loop && !mcfg->IsExpPreserved(lval))) { Where *res = WhereInvariant::Make(csu, csu_lval, bit); if (res) return res; // fall through, we might still be able to treat this as a precondition. } } if (UseCallerExp(lval, m_frame->Kind() == B_Function)) return WherePrecondition::Make(m_frame->Memory(), bit); if (PPoint point = UseCalleeExp(lval)) { // fail propagation if this is from a later callee than the point // this propagation occurs at. this can come up when generating // sufficient conditions. if (point > m_point || (point == m_point && !m_allow_point)) return NULL; // cutoff propagation if the buffer came from a primitive memory // allocator. if we find a sufficient condition that does not // mention the allocator we could continue propagation. PEdge *edge = mcfg->GetCFG()->GetSingleOutgoingEdge(point); PEdgeCall *edge_call = edge->IfCall(); Variable *callee = edge_call ? edge_call->GetDirectFunction() : NULL; Exp *callee_base; Exp *callee_size; if (callee && GetAllocationFunction(callee, &callee_base, &callee_size)) { callee_base->DecRef(); callee_size->DecRef(); if (lval->IsBound()) lval = lval->GetLvalTarget(); // ignore this if it refers to fields or other structures // in the result of the allocation. this data is either // uninitialized or zeroed, either way we don't care. if (ExpClobber *nlval = lval->IfClobber()) { Exp *callee_lval = nlval->GetCallee(); Exp *callee_target = NULL; if (nlval->GetValueKind() == NULL) { callee_target = callee_lval; } else { // skip the first dereference. for terminators we still depend on // the initial contents of the allocated buffer. if (ExpExit *ncallee = callee_lval->IfExit()) callee_target = ncallee->GetTarget(); } if (callee_target) { while (callee_target->IsFld()) callee_target = callee_target->GetLvalTarget(); if (callee_target->IsExit()) return new WhereNone(RK_None); } } // watch for accessing indexes of a buffer returned via the allocator, // which currently aren't mapped back into the callee correctly. // TODO: fix hack. if (lval->IsDrf() && lval->GetLvalTarget()->IsIndex()) return new WhereNone(RK_None); return new WhereNone(RK_Finished); } if (callee && IsCutoffFunction(callee)) return new WhereNone(RK_Finished); return WherePostcondition::Make(m_frame, point, bit); } return NULL; }