//Acceps a query, calls the SAT solver and generates Valid/InValid. //if returned 0 then input is INVALID if returned 1 then input is //VALID if returned 2 then UNDECIDED SOLVER_RETURN_TYPE STP::TopLevelSTPAux(SATSolver& NewSolver, const ASTNode& modified_input, const ASTNode& original_input) { ASTNode inputToSAT = modified_input; ASTNode orig_input = original_input; bm->ASTNodeStats("input asserts and query: ", inputToSAT); ASTNode simplified_solved_InputToSAT = inputToSAT; const bool arrayops = containsArrayOps(original_input); DifficultyScore difficulty; if (bm->UserFlags.stats_flag) cerr << "Difficulty Initially:" << difficulty.score(original_input) << endl; // A heap object so I can easily control its lifetime. BVSolver* bvSolver = new BVSolver(bm, simp); simplified_solved_InputToSAT = sizeReducing(inputToSAT, bvSolver); unsigned initial_difficulty_score = difficulty.score(simplified_solved_InputToSAT); // Fixed point it if it's not too difficult. // Currently we discards all the state each time sizeReducing is called, // so it's expensive to call. if (!arrayops && initial_difficulty_score < 1000000) { simplified_solved_InputToSAT = callSizeReducing(simplified_solved_InputToSAT, bvSolver, initial_difficulty_score); initial_difficulty_score = difficulty.score(simplified_solved_InputToSAT); } if (bm->UserFlags.stats_flag) cout << "Difficulty After Size reducing:" << initial_difficulty_score << endl; // Copy the solver map incase we need to revert. ASTNodeMap initialSolverMap; ASTNode toRevertTo; if (!arrayops) // we don't revert for Array problems yet, so don't copy it. { initialSolverMap.insert(simp->Return_SolverMap()->begin(), simp->Return_SolverMap()->end()); toRevertTo = simplified_solved_InputToSAT; } //round of substitution, solving, and simplification. ensures that //DAG is minimized as much as possibly, and ideally should //garuntee that all liketerms in BVPLUSes have been combined. bm->SimplifyWrites_InPlace_Flag = false; bm->Begin_RemoveWrites = false; bm->start_abstracting = false; bm->TermsAlreadySeenMap_Clear(); do { inputToSAT = simplified_solved_InputToSAT; if (bm->UserFlags.optimize_flag) { simplified_solved_InputToSAT = simp->CreateSubstitutionMap(simplified_solved_InputToSAT, arrayTransformer); // Imagine: // The simplifier simplifies (0 + T) to T // Then bvsolve introduces (0 + T) // Then CreateSubstitutionMap decides T maps to a constant, but leaving another (0+T). // When we go to simplify (0 + T) will still be in the simplify cache, so will be mapped to T. // But it shouldn't be T, it should be a constant. // Applying the substitution map fixes this case. // if (simp->hasUnappliedSubstitutions()) { simplified_solved_InputToSAT = simp->applySubstitutionMap(simplified_solved_InputToSAT); simp->haveAppliedSubstitutionMap(); } bm->ASTNodeStats("after pure substitution: ", simplified_solved_InputToSAT); simplified_solved_InputToSAT = simp->SimplifyFormula_TopLevel(simplified_solved_InputToSAT, false); bm->ASTNodeStats("after simplification: ", simplified_solved_InputToSAT); } if (bm->UserFlags.wordlevel_solve_flag && bm->UserFlags.optimize_flag) { simplified_solved_InputToSAT = bvSolver->TopLevelBVSolve(simplified_solved_InputToSAT); bm->ASTNodeStats("after solving: ", simplified_solved_InputToSAT); } } while (inputToSAT != simplified_solved_InputToSAT); if (bm->UserFlags.bitConstantProp_flag) { bm->GetRunTimes()->start(RunTimes::ConstantBitPropagation); SimplifyingNodeFactory nf(*(bm->hashingNodeFactory), *bm); simplifier::constantBitP::ConstantBitPropagation cb(simp, &nf, simplified_solved_InputToSAT); simplified_solved_InputToSAT = cb.topLevelBothWays(simplified_solved_InputToSAT); bm->GetRunTimes()->stop(RunTimes::ConstantBitPropagation); if (cb.isUnsatisfiable()) simplified_solved_InputToSAT = bm->ASTFalse; bm->ASTNodeStats("After Constant Bit Propagation begins: ", simplified_solved_InputToSAT); } if (bm->UserFlags.isSet("use-intervals", "1")) { EstablishIntervals intervals(*bm); simplified_solved_InputToSAT = intervals.topLevel_unsignedIntervals(simplified_solved_InputToSAT); bm->ASTNodeStats("After Establishing Intervals: ", simplified_solved_InputToSAT); } // Find pure literals. if (bm->UserFlags.isSet("pure-literals", "1")) { FindPureLiterals fpl; bool changed = fpl.topLevel(simplified_solved_InputToSAT, simp, bm); if (changed) { simplified_solved_InputToSAT = simp->applySubstitutionMap(simplified_solved_InputToSAT); simp->haveAppliedSubstitutionMap(); bm->ASTNodeStats("After Pure Literals: ", simplified_solved_InputToSAT); } } // Simplify using Ite context if (bm->UserFlags.optimize_flag && bm->UserFlags.isSet("ite-context", "0")) { UseITEContext iteC(bm); simplified_solved_InputToSAT = iteC.topLevel(simplified_solved_InputToSAT); bm->ASTNodeStats("After ITE Context: ", simplified_solved_InputToSAT); } if (bm->UserFlags.isSet("aig-core-simplify", "0")) { AIGSimplifyPropositionalCore aigRR(bm); simplified_solved_InputToSAT = aigRR.topLevel(simplified_solved_InputToSAT); bm->ASTNodeStats("After AIG Core: ", simplified_solved_InputToSAT); } bm->ASTNodeStats("Before SimplifyWrites_Inplace begins: ", simplified_solved_InputToSAT); bm->SimplifyWrites_InPlace_Flag = true; bm->Begin_RemoveWrites = false; bm->start_abstracting = false; bm->TermsAlreadySeenMap_Clear(); do { inputToSAT = simplified_solved_InputToSAT; if (bm->UserFlags.optimize_flag) { simplified_solved_InputToSAT = simp->CreateSubstitutionMap(simplified_solved_InputToSAT, arrayTransformer); if (simp->hasUnappliedSubstitutions()) { simplified_solved_InputToSAT = simp->applySubstitutionMap(simplified_solved_InputToSAT); simp->haveAppliedSubstitutionMap(); } bm->ASTNodeStats("after pure substitution: ", simplified_solved_InputToSAT); simplified_solved_InputToSAT = simp->SimplifyFormula_TopLevel(simplified_solved_InputToSAT, false); bm->ASTNodeStats("after simplification: ", simplified_solved_InputToSAT); if (bm->UserFlags.isSet("always-true", "0")) { SimplifyingNodeFactory nf(*(bm->hashingNodeFactory), *bm); AlwaysTrue always (simp,bm,&nf); simplified_solved_InputToSAT = always.topLevel(simplified_solved_InputToSAT); bm->ASTNodeStats("After removing always true: ", simplified_solved_InputToSAT); } } // The word level solver uses the simplifier to apply the rewrites it makes, // without optimisations enabled. It will enter infinite loops on some input. // Instead it could use the apply function of the substitution map, but it // doesn't yet... if (bm->UserFlags.wordlevel_solve_flag && bm->UserFlags.optimize_flag) { simplified_solved_InputToSAT = bvSolver->TopLevelBVSolve(simplified_solved_InputToSAT); bm->ASTNodeStats("after solving: ", simplified_solved_InputToSAT); } } while (inputToSAT != simplified_solved_InputToSAT); bm->ASTNodeStats("After SimplifyWrites_Inplace: ", simplified_solved_InputToSAT); if (bm->UserFlags.isSet("enable-unconstrained", "1")) { // Remove unconstrained. RemoveUnconstrained r(*bm); simplified_solved_InputToSAT = r.topLevel(simplified_solved_InputToSAT, simp); bm->ASTNodeStats("After Unconstrained Remove begins: ", simplified_solved_InputToSAT); } bm->TermsAlreadySeenMap_Clear(); bm->start_abstracting = false; bm->SimplifyWrites_InPlace_Flag = false; bm->Begin_RemoveWrites = false; long final_difficulty_score = difficulty.score(simplified_solved_InputToSAT); if (bm->UserFlags.stats_flag) { cerr << "Initial Difficulty Score:" << initial_difficulty_score << endl; cerr << "Final Difficulty Score:" << final_difficulty_score << endl; } bool optimize_enabled = bm->UserFlags.optimize_flag; if (final_difficulty_score > 1.1 * initial_difficulty_score && !arrayops && bm->UserFlags.isSet( "difficulty-reversion", "1")) { // If the simplified problem is harder, than the // initial problem we revert back to the initial // problem. if (bm->UserFlags.stats_flag) cerr << "simplification made the problem harder, reverting." << endl; simplified_solved_InputToSAT = toRevertTo; // I do this to clear the substitution/solver map. // Not sure what would happen if it contained simplifications // that haven't been applied. simp->ClearAllTables(); simp->Return_SolverMap()->insert(initialSolverMap.begin(), initialSolverMap.end()); initialSolverMap.clear(); // The arrayTransformer calls simplify. We don't want // it to put back in all the bad simplifications. bm->UserFlags.optimize_flag = false; } simplified_solved_InputToSAT = arrayTransformer->TransformFormula_TopLevel(simplified_solved_InputToSAT); bm->ASTNodeStats("after transformation: ", simplified_solved_InputToSAT); bm->TermsAlreadySeenMap_Clear(); bm->UserFlags.optimize_flag = optimize_enabled; SOLVER_RETURN_TYPE res; if (bm->UserFlags.arrayread_refinement_flag) { bm->counterexample_checking_during_refinement = true; } // We are about to solve. Clear out all the memory associated with caches // that we won't need again. simp->ClearCaches(); simp->haveAppliedSubstitutionMap(); bm->ClearAllTables(); // Deleting it clears out all the buckets associated with hashmaps etc. too. delete bvSolver; bvSolver = NULL; if (bm->UserFlags.stats_flag) simp->printCacheStatus(); const bool maybeRefinement = arrayops && bm->UserFlags.arrayread_refinement_flag; simplifier::constantBitP::ConstantBitPropagation* cb = NULL; std::auto_ptr<simplifier::constantBitP::ConstantBitPropagation> cleaner; if (bm->UserFlags.bitConstantProp_flag) { bm->ASTNodeStats("Before Constant Bit Propagation begins: ", simplified_solved_InputToSAT); bm->GetRunTimes()->start(RunTimes::ConstantBitPropagation); cb = new simplifier::constantBitP::ConstantBitPropagation(simp, bm->defaultNodeFactory, simplified_solved_InputToSAT); cleaner.reset(cb); bm->GetRunTimes()->stop(RunTimes::ConstantBitPropagation); if (cb->isUnsatisfiable()) simplified_solved_InputToSAT = bm->ASTFalse; } ToSATAIG toSATAIG(bm, cb); toSATAIG.setArrayTransformer(arrayTransformer); ToSATBase* satBase = bm->UserFlags.isSet("traditional-cnf", "0") ? tosat : ((ToSAT*) &toSATAIG) ; // If it doesn't contain array operations, use ABC's CNF generation. res = Ctr_Example->CallSAT_ResultCheck(NewSolver, simplified_solved_InputToSAT, orig_input, satBase, maybeRefinement); if (SOLVER_UNDECIDED != res) { // If the aig converter knows that it is never going to be called again, // it deletes the constant bit stuff before calling the SAT solver. if (toSATAIG.cbIsDestructed()) cleaner.release(); CountersAndStats("print_func_stats", bm); return res; } assert(arrayops); // should only go to abstraction refinement if there are array ops. assert(bm->UserFlags.arrayread_refinement_flag); // Refinement must be enabled too. // Unfortunately how I implemented the incremental CNF generator in ABC means that // cryptominisat and simplifying minisat may simplify away variables that we later need. res = Ctr_Example->SATBased_ArrayReadRefinement(NewSolver, simplified_solved_InputToSAT, orig_input, satBase); if (SOLVER_UNDECIDED != res) { if (toSATAIG.cbIsDestructed()) cleaner.release(); CountersAndStats("print_func_stats", bm); return res; } res = Ctr_Example->SATBased_ArrayWriteRefinement(NewSolver, orig_input, satBase); if (SOLVER_UNDECIDED != res) { if (toSATAIG.cbIsDestructed()) cleaner.release(); CountersAndStats("print_func_stats", bm); return res; } res = Ctr_Example->SATBased_ArrayReadRefinement(NewSolver, simplified_solved_InputToSAT, orig_input, satBase); if (SOLVER_UNDECIDED != res) { if (toSATAIG.cbIsDestructed()) cleaner.release(); CountersAndStats("print_func_stats", bm); return res; } // if (!bm->UserFlags.num_absrefine_flag) { FatalError("TopLevelSTPAux: reached the end without proper conclusion:" "either a divide by zero in the input or a bug in STP"); //bogus return to make the compiler shut up return SOLVER_ERROR; } // else { // return res; } } //End of TopLevelSTPAux
ASTNode SubstitutionMap::replace(const ASTNode& n, ASTNodeMap& fromTo, ASTNodeMap& cache, NodeFactory * nf, bool stopAtArrays, bool preventInfinite) { const Kind k = n.GetKind(); if (k == BVCONST || k == TRUE || k == FALSE) return n; ASTNodeMap::const_iterator it; if ((it = cache.find(n)) != cache.end()) return it->second; if ((it = fromTo.find(n)) != fromTo.end()) { const ASTNode& r = it->second; assert(r.GetIndexWidth() == n.GetIndexWidth()); if (preventInfinite) cache.insert(make_pair(n, r)); ASTNode replaced = replace(r, fromTo, cache, nf, stopAtArrays, preventInfinite); if (replaced != r) { fromTo.erase(n); fromTo[n] = replaced; } if (preventInfinite) cache.erase(n); cache.insert(make_pair(n, replaced)); return replaced; } // These can't be created like regular nodes are if (k == SYMBOL) return n; const unsigned int indexWidth = n.GetIndexWidth(); if (stopAtArrays && indexWidth > 0) // is an array. { return n; } const ASTVec& children = n.GetChildren(); assert(children.size() > 0); // Should have no leaves left here. ASTVec new_children; new_children.reserve(children.size()); for (ASTVec::const_iterator it = children.begin(); it != children.end(); it++) { new_children.push_back(replace(*it, fromTo, cache, nf, stopAtArrays, preventInfinite)); } assert(new_children.size() == children.size()); // This code short-cuts if the children are the same. Nodes with the same children, // won't have necessarily given the same node if the simplifyingNodeFactory is enabled // now, but wasn't enabled when the node was created. Shortcutting saves lots of time. if (new_children == children) { cache.insert(make_pair(n, n)); return n; } ASTNode result; const unsigned int valueWidth = n.GetValueWidth(); if (valueWidth == 0) // n.GetType() == BOOLEAN_TYPE { result = nf->CreateNode(k, new_children); } else { // If the index and value width aren't saved, they are reset sometimes (??) result = nf->CreateArrayTerm(k, indexWidth, valueWidth, new_children); } // We may have created something that should be mapped. For instance, // if n is READ(A, x), and the fromTo is: {x==0, READ(A,0) == 1}, then // by here the result will be READ(A,0). Which needs to be mapped again.. // I hope that this makes it idempotent. if (fromTo.find(result) != fromTo.end()) { // map n->result, if running replace() on result gives us 'n', it will not infinite loop. // This is only currently required for the bitblast equivalence stuff. if (preventInfinite) cache.insert(make_pair(n, result)); result = replace(result, fromTo, cache, nf, stopAtArrays, preventInfinite); } assert(result.GetValueWidth() == valueWidth); assert(result.GetIndexWidth() == indexWidth); // If there is already an "n" element in the cache, the maps semantics are to ignore the next insertion. if (preventInfinite) cache.erase(n); cache.insert(make_pair(n, result)); return result; }
// NB: This expects that the constructor was called with teh same node. Sorry. ASTNode ConstantBitPropagation::topLevelBothWays(const ASTNode& top) { assert(top.GetSTPMgr()->UserFlags.bitConstantProp_flag); assert (BOOLEAN_TYPE == top.GetType()); propagate(); status = NO_CHANGE; //Determine what must always be true. ASTNodeMap fromTo = getAllFixed(); if (debug_cBitProp_messages) { cerr << "Number removed by bottom UP:" << fromTo.size() << endl; } setNodeToTrue(top); if (debug_cBitProp_messages) { cerr << "starting propagation" << endl; printNodeWithFixings(); cerr << "Initial Tree:" << endl; cerr << top; } propagate(); if (debug_cBitProp_messages) { cerr << "status:" << status <<endl; cerr << "ended propagation" << endl; printNodeWithFixings(); } // propagate may have stopped with a conflict. if (CONFLICT == status) return top.GetSTPMgr()->CreateNode(FALSE); ASTVec toConjoin; // go through the fixedBits. If a node is entirely fixed. // "and" it onto the top. Creates redundancy. Check that the // node doesn't already depend on "top" directly. for (NodeToFixedBitsMap::NodeToFixedBitsMapType::iterator it = fixedMap->map->begin(); it != fixedMap->map->end(); it++) // iterates through all the pairs of node->fixedBits. { const FixedBits& bits = *it->second; if (!bits.isTotallyFixed()) continue; const ASTNode& node = (it->first); // Don't constrain nodes we already know all about. if (node.isConstant()) continue; // other nodes will contain the same information (the extract doesn't change the fixings). if (BVEXTRACT == node.GetKind() || BVCONCAT == node.GetKind()) continue; // toAssign: conjoin it with the top level. // toReplace: replace all references to it (except the one conjoined to the top) with this. ASTNode propositionToAssert; ASTNode constantToReplaceWith; // skip the assigning and replacing. bool doAssign = true; { // If it is already contained in the fromTo map, then it's one of the values // that have fully been determined (previously). Not conjoined. if (fromTo.find(node) != fromTo.end()) continue; ASTNode constNode = bitsToNode(node,bits); if (node.GetType() == BOOLEAN_TYPE) { if (SYMBOL == node.GetKind()) { bool r = simplifier->UpdateSubstitutionMap(node, constNode); assert(r); doAssign = false; } else if (bits.getValue(0)) { propositionToAssert = node; constantToReplaceWith = constNode; } else { propositionToAssert = nf->CreateNode(NOT, node); constantToReplaceWith = constNode; } } else if (node.GetType() == BITVECTOR_TYPE) { assert(((unsigned)bits.getWidth()) == node.GetValueWidth()); if (SYMBOL == node.GetKind()) { bool r = simplifier->UpdateSubstitutionMap(node, constNode); assert(r); doAssign = false; } else { propositionToAssert = nf->CreateNode(EQ, node, constNode); constantToReplaceWith = constNode; } } else FatalError("sadf234s"); } if (doAssign && top != propositionToAssert && !dependents->nodeDependsOn(top, propositionToAssert)) { assert(!constantToReplaceWith.IsNull()); assert(constantToReplaceWith.isConstant()); assert(propositionToAssert.GetType() == BOOLEAN_TYPE); assert(node.GetValueWidth() == constantToReplaceWith.GetValueWidth()); fromTo.insert(make_pair(node, constantToReplaceWith)); toConjoin.push_back(propositionToAssert); } } // Write the constants into the main graph. ASTNodeMap cache; ASTNode result = SubstitutionMap::replace(top, fromTo, cache,nf); if (0 != toConjoin.size()) { // It doesn't happen very often. But the "toConjoin" might contain a variable // that was added to the substitution map (because the value was determined just now // during propagation. ASTNode conjunct = (1 == toConjoin.size())? toConjoin[0]: nf->CreateNode(AND,toConjoin); conjunct = simplifier->applySubstitutionMap(conjunct); result = nf->CreateNode(AND, result, conjunct); // conjoin the new conditions. } if (debug_print_graph_after) { ofstream file; file.open("afterCbitp.gdl"); PrintingHackfixedMap = fixedMap; printer::GDL_Print(file,top,&toString); file.close(); } assert(BVTypeCheck(result)); assert(status != CONFLICT); // conflict should have been seen earlier. return result; }