// 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; }
// 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; } } } }