Ejemplo n.º 1
0
BlockCFG* GetBlockCFG(BlockId *id)
{
  BlockCFG *cfg = NULL;

  switch (id->Kind()) {

  case B_Initializer:
    cfg = InitializerCache.Lookup(id->Function());
    if (cfg)
      cfg->IncRef();
    InitializerCache.Release(id->Function());
    break;

  case B_Function:
  case B_Loop:
    cfg = BlockCFGCache.Lookup(id);
    if (cfg)
      cfg->IncRef();
    BlockCFGCache.Release(id);
    break;

  default:
    Assert(false);
  }

  return cfg;
}
Ejemplo n.º 2
0
  void LookupInsert(Cache_BlockCFG *cache, BlockId *id)
  {
    Assert(id->Kind() == B_Function || id->Kind() == B_Loop);
    String *function = id->Function();
    const char *function_name = function->Value();

    if (!DoLookupTransaction(BODY_DATABASE, function_name, &scratch_buf)) {
      id->IncRef(cache);
      cache->Insert(id, NULL);
      return;
    }

    Buffer read_buf(scratch_buf.base, scratch_buf.pos - scratch_buf.base);
    Vector<BlockCFG*> cfg_list;
    BlockCFG::ReadList(&read_buf, &cfg_list);

    scratch_buf.Reset();

    for (size_t ind = 0; ind < cfg_list.Size(); ind++) {
      BlockCFG *cfg = cfg_list[ind];
      BlockId *id = cfg->GetId();

      id->IncRef(cache);
      cfg->MoveRef(NULL, cache);
      cache->Insert(id, cfg);
    }
  }
Ejemplo n.º 3
0
void BlockCFGCacheAddListWithRefs(const Vector<BlockCFG*> &cfgs)
{
  for (size_t ind = 0; ind < cfgs.Size(); ind++) {
    BlockCFG *cfg = cfgs[ind];
    BlockId *id = cfg->GetId();

    id->IncRef(&BlockCFGCache);
    cfg->IncRef(&BlockCFGCache);
    BlockCFGCache.Insert(id, cfg);
  }
}
Ejemplo n.º 4
0
  void LookupInsert(Cache_Initializer *cache, String *var)
  {
    if (!DoLookupTransaction(INIT_DATABASE, var->Value(), &scratch_buf)) {
      var->IncRef(cache);
      cache->Insert(var, NULL);
      return;
    }

    Buffer read_buf(scratch_buf.base, scratch_buf.pos - scratch_buf.base);
    BlockCFG *cfg = BlockCFG::Read(&read_buf);

    scratch_buf.Reset();

    var->IncRef(cache);
    cfg->MoveRef(NULL, cache);
    cache->Insert(var, cfg);
  }
Ejemplo n.º 5
0
void BlockSummary::ComputeAssertNames()
{
  BlockCFG *cfg = GetBlockCFG(m_id);
  Assert(cfg);

  // the assertion names need the filename of the containing function.
  Location *loc = cfg->GetPointLocation(cfg->GetEntryPoint());

  // keep track of the last kind/index to be generated for an assertion.
  // the index space should be different for each assertion kind so that
  // different assertions do not interfere with one another. we can get this
  // with a simple counter because the assertions are sorted by kind.
  AssertKind last_kind = ASK_None;
  size_t last_index = 0;

  for (size_t ind = 0; m_assert_list && ind < m_assert_list->Size(); ind++) {
    AssertInfo &info = m_assert_list->At(ind);
    Assert(!info.name_buf);

    // only compute indexes for assertions which need to be checked.
    if (info.cls != ASC_Check) continue;

    // reset the index if this is a new kind of assertion.
    if (info.kind != last_kind) {
      last_kind = info.kind;
      last_index = 0;
    }
    last_index++;

    info.name_buf = new Buffer(256);
    BufferOutStream out(info.name_buf);

    out << AssertKindString(last_kind) << "$"
        << loc->FileName()->Value() << "$"
        << m_id->Function()->Value() << "$"
        << (m_id->Kind() == B_Loop ? m_id->Loop()->Value() : "func") << "$"
        << last_index << '\0';
  }

  cfg->DecRef();
}
Ejemplo n.º 6
0
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;
}
Ejemplo n.º 7
0
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();
}
Ejemplo n.º 8
0
// consumes a reference on callee.
bool CheckSingleCallee(CheckerState *state, CheckerFrame *frame, PPoint point,
                       WherePostcondition *postcondition,
                       BlockId *callee, bool direct)
{
  Assert(postcondition);
  Assert(postcondition->GetPoint() == point);

  BlockCFG *cfg = frame->Memory()->GetCFG();
  bool is_call = cfg->PointEdgeIsCall(point);

  CheckerFrame *callee_frame;
  if (is_call)
    callee_frame = frame->GetCalleeFrame(point);
  else
    callee_frame = frame->GetLoopTailFrame(point);

  // we should be going strictly backwards, and thus should never be
  // able to visit the exit point of a callee more than once.
  Assert(!callee_frame);

  callee_frame = state->MakeFrame(callee);
  callee->DecRef();

  if (callee_frame == NULL) {
    // there are two ways we can get here:
    // 1. we saw the definition but timed out generating the memory.
    //    emit a warning.
    // 2. we never saw a definition. emit a warning in the indirect call case,
    //    make a report in the direct call case. we should really make a report
    //    in all cases, but the indirect callgraph needs some more work.

    BlockCFG *cfg = GetBlockCFG(callee);
    if (!cfg) {
      const char *kind = direct ? "direct" : "indirect";
      logout << "WARNING: Missing " << kind
             << " callee: '" << callee << "'" << endl;

      if (!direct)
        return false;

      // make an empty propagation for before the call.
      CheckerPropagate propagate(frame, point, false);
      state->m_stack.PushBack(&propagate);

      state->SetReport(RK_NoCallee);
      return true;
    }
    cfg->DecRef();

    logout << "WARNING: Missing memory: '" << callee << "'" << endl;
    return false;
  }

  PPoint exit_point = callee_frame->Memory()->GetCFG()->GetExitPoint();
  callee_frame->AssertPointGuard(exit_point, true);

  if (is_call) {
    frame->ConnectCallee(callee_frame, point, true);
  }
  else {
    // we are getting the last iteration of the loop so there is no direct
    // callee to connect yet.
    frame->ConnectLoopTail(callee_frame, point);
  }

  // add the exit equalities for any callee_expand in frame and caller_expand
  // in callee_frame.
  frame->ExpandPendingExps();
  callee_frame->ExpandPendingExps();

  // get the safe bits for the callee frame.
  Bit *callee_base_bit;
  GuardBitVector callee_safe_list;
  postcondition->GetCalleeBits(callee_frame,
                               &callee_base_bit, &callee_safe_list);

  if (CheckFrameList(state, callee_frame, exit_point, false, false,
                     callee_base_bit, callee_safe_list)) {
    callee_base_bit->DecRef();
    return true;
  }

  callee_base_bit->DecRef();

  if (is_call)
    frame->DisconnectCallee(callee_frame, point);
  else
    frame->DisconnectLoopTail(callee_frame, point);

  state->DeleteFrame(callee_frame);
  return false;
}
Ejemplo n.º 9
0
// check a single caller to the specified frame at site 'where'.
// return value is whether an error path was found.
bool CheckSingleCaller(CheckerState *state, CheckerFrame *frame,
                       WherePrecondition *precondition, BlockPPoint where)
{
  CheckerFrame *caller_frame = frame->GetCallerFrame();
  CheckerFrame *loop_parent_frame = frame->GetLoopParentFrame();

  bool has_caller = (caller_frame != NULL);
  bool has_loop_parent = (loop_parent_frame != NULL);

  // whether we should keep the caller and callee frames disconnected
  // (unless they already are connected). this is done during loop propagation
  // if there is either a sufficient condition on the propagation or
  // no propagation at all (connecting frames for heap propagation),
  // ensuring we do not have to unroll the loop ad infinitum.
  bool keep_disconnected = false;

  if (frame->Kind() == B_Loop) {
    if (!precondition || precondition->IsIgnoreUnroll())
      keep_disconnected = true;
  }

  // special handling for loop unroll cases.
  if (frame->Kind() == B_Loop && frame->Memory()->GetId() == where.id) {

    // filter out unrolled loop iterations if the caller is disconnected.
    if (keep_disconnected) {
      if (checker_verbose.IsSpecified())
        logout << "CHECK: " << frame << ": Skipping loop unroll" << endl;
      return false;
    }
  }

  // whether we had to create the caller frame.
  bool created_frame = false;

  // whether we need to connect this frame to the caller frame.
  bool connect_frame = false;

  if (has_caller) {
    // we should already have ensured this is a compatible caller.
    Assert(frame->GetCaller() == where);
    Assert(caller_frame);
  }
  else if (where == frame->GetLoopParent()) {
    // connecting to the loop parent of this frame.
    caller_frame = loop_parent_frame;
    Assert(caller_frame);

    connect_frame = true;
  }
  else {
    // no caller to connect to, we will have to make one.
    caller_frame = state->MakeFrame(where.id);

    if (caller_frame == NULL) {
      logout << "WARNING: Missing memory: '" << where.id << "'" << endl;
      return false;
    }

    BlockCFG *cfg = caller_frame->CFG();

    // check for call sites at loop isomorphic points. we will also see the
    // corresponding call site within the loop body, and handling that site
    // will also cover this one.
    if (cfg->IsLoopIsomorphic(where.point)) {
      if (checker_verbose.IsSpecified())
        logout << "CHECK: " << frame << ": Caller is loop isomorphic" << endl;
      state->DeleteFrame(caller_frame);
      return false;
    }

    // for incremental analysis, make sure the call edge uses the current
    // version of the caller CFG. if there is a version mismatch then this
    // call site is for an older version of the CFG; if the current version
    // of the CFG still calls the function then there will be a separate
    // call edge for the updated call site.
    if (cfg->GetVersion() != where.version) {
      if (checker_verbose.IsSpecified())
        logout << "CHECK: " << frame << ": Caller is an older version" << endl;
      state->DeleteFrame(caller_frame);
      return false;
    }

    caller_frame->AssertPointGuard(where.point, true);

    // check for a recursive function/loop call. if there is an existing
    // propagate for this function then just assert its safe/sufficient
    // bit within this caller_frame. TODO: this is unsound as we are not
    // accounting for the *other* parameters to the function which might
    // differ along the recursive calls.
    if (precondition && frame->Memory() == caller_frame->Memory()) {
      Bit *entry_bit = precondition->GetBit();
      caller_frame->AddAssert(entry_bit);
    }

    connect_frame = true;
    created_frame = true;
  }

  // if we are skipping from a loop iteration to the parent, make sure there
  // is a loop child relationship between the two.
  if (keep_disconnected) {
    Assert(frame->Kind() == B_Loop);
    Assert(caller_frame->Memory() != frame->Memory());

    // connect the caller frame as the loop parent if there isn't already one.
    if (loop_parent_frame) {
      Assert(loop_parent_frame == caller_frame);
    }
    else {
      caller_frame->ConnectLoopChild(frame, where.point);
    }
  }

  if (keep_disconnected)
    connect_frame = false;

  if (connect_frame) {
    caller_frame->ConnectCallee(frame, where.point, true);
    frame->PushCallerLoopParent(caller_frame);
  }

  // add any equalities we can between the frame and its caller.
  frame->ExpandPendingExps();

  if (precondition) {
    // get the safe bits for the caller frame.
    Bit *caller_base_bit;
    GuardBitVector caller_safe_list;
    precondition->GetCallerBits(caller_frame, where.point,
                                &caller_base_bit, &caller_safe_list);

    if (CheckFrameList(state, caller_frame, where.point, false, false,
                       caller_base_bit, caller_safe_list)) {
      caller_base_bit->DecRef();
      return true;
    }

    caller_base_bit->DecRef();
  }
  else {
    // continue the checker without any active propagation.
    if (CheckFrameSingle(state, caller_frame, where.point))
      return true;
  }

  if (!has_caller && frame->GetCaller().id != NULL)
    caller_frame->DisconnectCallee(frame, where.point);

  if (!has_loop_parent && frame->GetLoopParent().id != NULL)
    caller_frame->DisconnectLoopChild(frame, where.point);

  if (created_frame)
    state->DeleteFrame(caller_frame);

  return false;
}
Ejemplo n.º 10
0
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);
}
Ejemplo n.º 11
0
void SplitLoops(BlockCFG *base_cfg, Vector<BlockCFG*> *result_cfg_list)
{
    // get the CFG which will eventually become the loop-free outer function CFG.
    BlockCFG *func_cfg;
    if (base_cfg->GetId()->Kind() == B_FunctionWhole) {
        // make an ID for the outer function body.
        Variable *function_info = base_cfg->GetId()->BaseVar();
        function_info->IncRef();
        BlockId *outer_id = BlockId::Make(B_Function, function_info);

        // make the function CFG by cloning the base CFG with the new ID.
        func_cfg = BlockCFG::Make(outer_id);
        CopyCFGLocationsVariables(base_cfg, func_cfg);
        CopyCFGPointsEdges(base_cfg, func_cfg);
    }
    else if (base_cfg->GetId()->Kind() == B_Function) {
        // this call came from a recursive invocation of SplitLoops after we
        // removed an irreducible loop from the function.
        func_cfg = base_cfg;
    }
    else {
        // just destructively update the original CFG.
        base_cfg->IncRef();
        func_cfg = base_cfg;
    }

    // add a new entry point with a skip edge to the original entry point.
    // loop splitting breaks if the entry point is marked as a loop head.

    PPoint entry = func_cfg->GetEntryPoint();
    Location *loc = func_cfg->GetPointLocation(entry);
    loc->IncRef();
    PPoint new_entry = func_cfg->AddPoint(loc);

    PEdge *skip_edge = PEdge::MakeSkip(new_entry, entry);
    func_cfg->AddEdge(skip_edge);
    func_cfg->SetEntryPoint(new_entry);

    // setup the tables we need to do loop splitting.
    SetupTables();

    // compute the points reachable from the entry point.
    GetEntryReachable(func_cfg);

    // the real loops in the program with back edges.
    Vector<PPoint> loops;

    for (size_t lind = 0; lind < func_cfg->GetLoopHeadCount(); lind++) {
        PPoint head = func_cfg->GetLoopHead(lind).point;
        if (GetLoopBackedges(func_cfg, head))
            loops.PushBack(head);
    }

    // compute reachability and check for irreducible loops.
    for (size_t lind = 0; lind < func_cfg->GetLoopHeadCount(); lind++) {
        const LoopHead &head = func_cfg->GetLoopHead(lind);

        if (GetLoopReachable(func_cfg, head.point)) {
            // loop is irreducible.

            // get the loop's irreducible edges.
            Vector<PEdge*> irreducible_edges;
            GetLoopBody(func_cfg, head.point, &irreducible_edges);
            Assert(!irreducible_edges.Empty());

            // clone the loop's body and remove the irreducible edges.
            ReduceLoop(func_cfg, head.point, irreducible_edges);

            // try again on the modified CFG.
            CleanupTables();
            SplitLoops(func_cfg, result_cfg_list);
            return;
        }
    }

    // there are no irreducible loops at this point so this should
    // never have any entries added.
    Vector<PEdge*> irreducible_edges;

    // compute loop bodies.
    for (size_t lind = 0; lind < loops.Size(); lind++) {
        PPoint head = loops[lind];
        GetLoopBody(func_cfg, head, &irreducible_edges);
        Assert(irreducible_edges.Empty());
    }

    // construct a tree of all the loops. loop A contains loop B
    // if A != B and the head of B is in the body of A.
    PPointListHash loop_tree;

    // split off all the loops in the CFG. make sure we split inner loops
    // before outer, so that the Loop edges on inner loops will appear in
    // the split body for outer loops.
    while (!loops.Empty()) {

        // find a candidate loop to split. this is one whose loop children
        // have already been split off and are no longer in the loops list.
        PPoint loophead = 0;
        for (size_t lind = 0; lind < loops.Size(); lind++) {

            bool is_viable = true;
            for (size_t xlind = 0; xlind < loops.Size(); xlind++) {
                if (xlind == lind)
                    continue;
                Assert(loops[lind] != loops[xlind]);

                if (body_table->Lookup(PPointPair(loops[lind], loops[xlind]))) {
                    is_viable = false;
                    break;
                }
            }

            if (is_viable) {
                loophead = loops[lind];
                loops[lind] = loops.Back();
                loops.PopBack();
                break;
            }
        }

        Assert(loophead);
        BlockCFG *loop_cfg = SplitSingleLoop(loophead, loops, func_cfg);
        result_cfg_list->PushBack(loop_cfg);
    }

    // clear out the loopheads, we don't want them around anymore.
    func_cfg->ClearLoopHeads();

    // trim unreachable points in the function CFG (i.e. bodies of loops that
    // now redirect to point zero), collapse skips and topo sort.
    TrimUnreachable(func_cfg, true);
    TopoSortCFG(func_cfg);

    result_cfg_list->PushBack(func_cfg);
    CleanupTables();

    // fill in any loop parents for the inner loop CFGs, and make sure the
    // result CFGs are ordered correctly, with inner loops before outer loops
    // and the outer function.
    for (size_t cind = 0; cind < result_cfg_list->Size(); cind++) {
        BlockCFG *cfg = result_cfg_list->At(cind);

        for (size_t eind = 0; eind < cfg->GetEdgeCount(); eind++) {
            if (PEdgeLoop *edge = cfg->GetEdge(eind)->IfLoop()) {
                BlockId *target_id = edge->GetLoopId();

                bool found_target = false;
                for (size_t xcind = 0; xcind < cind; xcind++) {
                    BlockCFG *xcfg = result_cfg_list->At(xcind);
                    if (xcfg->GetId() == target_id) {
                        found_target = true;

                        cfg->GetId()->IncRef();
                        BlockPPoint where(cfg->GetId(), edge->GetSource());
                        xcfg->AddLoopParent(where);

                        // mark the isomorphic points in the parent CFG.
                        GetLoopIsomorphicPoints(cfg, edge, xcfg);

                        break;
                    }
                }

                Assert(found_target);
            }
        }
    }

    // assign the final names to the various loop CFGs.
    FillLoopNames(func_cfg, "loop", *result_cfg_list);
}
Ejemplo n.º 12
0
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());
}
Ejemplo n.º 13
0
// mark the trivial/redundant assertions in the specified list.
void MarkRedundantAssertions(BlockMemory *mcfg, Vector<AssertInfo> &asserts)
{
  BlockCFG *cfg = mcfg->GetCFG();

  // assertions are marked redundant in two passes:
  // 1. for each path reaching the assertion, the validity of the assertion is
  //    implied by one or more prior or future assertions.
  //    this pass also picks up assertions which trivially hold, where the
  //    assertion is valid due to the conditions along the paths themselves.
  // 2. there is an isomorphic assertion within an inner loop. it is
  //    sufficient to check just the inner assertion.

  // implication works differently for invariants vs. other assertions,
  // since the invariant condition will be asserted at block exit.

  // for regular assertions, an bit is redundant if (guard ==> bit)
  // is implied by the (oguard ==> obit) for other assertions:

  // VALID((oguard ==> obit) ==> (guard ==> bit))
  // !SAT(!((oguard ==> obit) ==> (guard ==> bit)))
  // !SAT(!(!(oguard ==> obit) || (guard ==> bit)))
  // !SAT((oguard ==> obit) && !(guard ==> bit))
  // !SAT((oguard ==> obit) && !(!guard || bit))
  // !SAT((oguard ==> obit) && guard && !bit)

  // for invariants, a bit is redundant if guard implies the oguard
  // for other invariants with the same asserted bit:

  // VALID(guard ==> oguard)
  // !SAT(!(guard ==> oguard))
  // !SAT(!(!guard || oguard))
  // !SAT(guard && !oguard)

  Solver *solver = new Solver("redundant");

  for (size_t ind = 0; ind < asserts.Size(); ind++) {
    AssertInfo &info = asserts[ind];
    solver->PushContext();

    Assert(info.cls == ASC_Check);

    // assert guard.
    Bit *guard = mcfg->GetGuard(info.point);
    solver->AddAssert(0, guard);

    if (info.kind != ASK_Invariant) {
      // assert !bit.
      Bit *not_bit = Bit::MakeNot(info.bit);

      Bit *result_not_bit;
      mcfg->TranslateBit(TRK_Point, info.point, not_bit, &result_not_bit);
      solver->AddAssert(0, result_not_bit);
    }

    if (!solver->IsSatisfiable()) {
      // the assert is tautological or is proved by the guard, thus trivial.
      info.cls = ASC_Trivial;
      solver->PopContext();
      continue;
    }

    // assert the remaining assertions in the summary hold.
    for (size_t aind = 0; aind < asserts.Size(); aind++) {
      const AssertInfo &oinfo = asserts[aind];

      // skip this assertion itself.
      if (info.point == oinfo.point && info.bit == oinfo.bit)
        continue;

      // skip assertions already marked as trivial or redundant.
      if (oinfo.cls != ASC_Check)
        continue;

      // skip assertions for a different kind than the original.
      // this avoids interference between the different kinds of assertions,
      // though it is unlikely to affect whether we actually mark an
      // assert as redundant.
      if (oinfo.kind != info.kind)
        continue;

      Bit *oguard = mcfg->GetGuard(oinfo.point);

      if (info.kind == ASK_Invariant) {
        // only compare with other invariants for the same bit.
        if (oinfo.bit != info.bit)
          continue;

        // assert !oguard
        Bit *not_oguard = Bit::MakeNot(oguard);
        solver->AddAssert(0, not_oguard);
      }
      else {
        // assert (oguard ==> obit).
        Bit *result_obit;
        mcfg->TranslateBit(TRK_Point, oinfo.point, oinfo.bit, &result_obit);

        Bit *imply_bit = Bit::MakeImply(oguard, result_obit);
        solver->AddAssert(0, imply_bit);
      }
    }

    if (!solver->IsSatisfiable()) {
      // the assert is implied by the remaining assertions, thus redundant.
      info.cls = ASC_Redundant;
    }

    solver->PopContext();
  }

  solver->Clear();
  delete solver;

  for (size_t ind = 0; ind < cfg->GetEdgeCount(); ind++) {
    PEdgeLoop *loop_edge = cfg->GetEdge(ind)->IfLoop();
    if (!loop_edge)
      continue;

    for (size_t aind = 0; aind < asserts.Size(); aind++) {
      AssertInfo &info = asserts[aind];

      if (info.cls != ASC_Check)
        continue;

      if (cfg->IsLoopIsomorphic(info.point)) {
        // this assertion's point is isomorphic to a point within the
        // loop body, so there will be an equivalent loop assertion.
        info.cls = ASC_Redundant;
      }
    }
  }
}
Ejemplo n.º 14
0
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());
}