bool SubstitutionMap::UpdateSubstitutionMap(const ASTNode& e0, const ASTNode& e1) { int i = TermOrder(e0, e1); if (0 == i) return false; assert(e0 != e1); assert(e0.GetValueWidth() == e1.GetValueWidth()); assert(e0.GetIndexWidth() == e1.GetIndexWidth()); if (e0.GetKind() == SYMBOL) { if (CheckSubstitutionMap(e0)) { // e0 and e1 might both be variables, e0 is already substituted for, // but maybe not e1. if (e1.GetKind() == SYMBOL) i = -1; else return false; // already in the map. } if (loops(e0, e1)) return false; // loops. } if (e1.GetKind() == SYMBOL) { if (CheckSubstitutionMap(e1)) return false; // already in the map. if (loops(e1, e0)) return false; // loops } //e0 is of the form READ(Arr,const), and e1 is const, or //e0 is of the form var, and e1 is a function. if (1 == i && !CheckSubstitutionMap(e0)) { buildDepends(e0, e1); (*SolverMap)[e0] = e1; return true; } //e1 is of the form READ(Arr,const), and e0 is const, or //e1 is of the form var, and e0 is const if (-1 == i && !CheckSubstitutionMap(e1)) { buildDepends(e1, e0); (*SolverMap)[e1] = e0; return true; } return false; }
void LetizeNode(const ASTNode& n, ASTNodeSet& PLPrintNodeSet, bool smtlib1) { const Kind kind = n.GetKind(); if (kind == SYMBOL || kind == BVCONST || kind == FALSE || kind == TRUE) return; const ASTVec& c = n.GetChildren(); for (ASTVec::const_iterator it = c.begin(), itend = c.end(); it != itend; it++) { const ASTNode& ccc = *it; const Kind k = ccc.GetKind(); if (k == SYMBOL || k == BVCONST || k == FALSE || k == TRUE) continue; if (PLPrintNodeSet.find(ccc) == PLPrintNodeSet.end()) { // If branch: if *it is not in NodeSet then, // // 1. add it to NodeSet // // 2. Letize its childNodes PLPrintNodeSet.insert(ccc); LetizeNode(ccc, PLPrintNodeSet, smtlib1); } else { // 0. Else branch: Node has been seen before // // 1. Check if the node has a corresponding letvar in the // 1. NodeLetVarMap. // // 2. if no, then create a new var and add it to the // 2. NodeLetVarMap if ((!smtlib1 || ccc.GetType() == BITVECTOR_TYPE) && NodeLetVarMap.find(ccc) == NodeLetVarMap.end()) { // Create a new symbol. Get some name. if it conflicts with a // declared name, too bad. int sz = NodeLetVarMap.size(); std::ostringstream oss; oss << "?let_k_" << sz; ASTNode CurrentSymbol = n.GetSTPMgr()->CreateSymbol( oss.str().c_str(), n.GetIndexWidth(), n.GetValueWidth()); /* If for some reason the variable being created here is * already declared by the user then the printed output will * not be a legal input to the system. too bad. I refuse to * check for this. [Vijay is the author of this comment.] */ NodeLetVarMap[ccc] = CurrentSymbol; std::pair<ASTNode, ASTNode> node_letvar_pair(CurrentSymbol, ccc); NodeLetVarVec.push_back(node_letvar_pair); } } } } // end of LetizeNode()
// Check that the transformations have occurred. void ArrayTransformer::assertTransformPostConditions(const ASTNode& term, ASTNodeSet& visited) { // I haven't measure whether this is the quickest way to do it? std::pair<ASTNodeSet::iterator, bool> p = visited.insert(term); if (!p.second) return; const Kind k = term.GetKind(); // Check the array reads / writes have been removed assert(READ != k); assert(WRITE != k); // There should be no nodes left of type array. assert(0 == term.GetIndexWidth()); const ASTVec& c = term.GetChildren(); ASTVec::const_iterator it = c.begin(); const ASTVec::const_iterator itend = c.end(); for (; it != itend; it++) { assertTransformPostConditions(*it, visited); } }
// True if any descendants are arrays. bool containsArrayOps(const ASTNode& n) { NodeIterator ni(n, n.GetSTPMgr()->ASTUndefined, *n.GetSTPMgr()); ASTNode current; while ((current = ni.next()) != ni.end()) if (current.GetIndexWidth() > 0) return true; return false; }
bool VariablesInExpression::VarSeenInTerm(const ASTNode& var, const ASTNode& term) { // This only returns true if we are searching for variables that aren't arrays. assert(var.GetKind() == SYMBOL && var.GetIndexWidth() == 0); if (term.isConstant()) return false; getSymbol(term); SymbolPtrSet visited; ASTNodeSet *symbols = new ASTNodeSet(); vector<Symbols*> av; VarSeenInTerm(symbol_graph[term.GetNodeNum()], visited, *symbols, av); bool result = (symbols->count(var) != 0); //cerr << "visited:" << visited.size() << endl; //cerr << "av:" << av.size() << endl; //cerr << "Term is const" << term.isConstant() << endl; if (visited.size() > 250) // No use caching it, unless we've done some work. { sort(av.begin(), av.end()); //cout << "===" << endl; for (size_t i = 0; i < av.size(); i++) { if (i!=0 && av[i] == av[i-1]) continue; const ASTNodeSet& sym = *TermsAlreadySeenMap.find(av[i])->second; //cout << "set: " << i << " " << sym.size() << endl; symbols->insert(sym.begin(), sym.end()); } TermsAlreadySeenMap.insert(make_pair(symbol_graph[term.GetNodeNum()], symbols)); //cout << "finish" << symbols->size() << endl; //cout << "===" << endl; result = (symbols->count(var) != 0); } else { const int size = av.size(); for (int i = 0; i < size; i++) { if (result) break; const ASTNodeSet& sym = *TermsAlreadySeenMap.find(av[i])->second; result |= (sym.find(var) != sym.end()); } delete symbols; } return result; }
void CNFMgr::convertTermForCNF(const ASTNode& varphi, ClauseList* defs) { CNFInfo* x = info[varphi]; //######################################## // step 1, done if we've already visited //######################################## if (x->termforcnf != NULL) { return; } //######################################## // step 2, ITE's always get renamed //######################################## if (varphi.isITE()) { x->termforcnf = doRenameITE(varphi, defs); reduceMemoryFootprintPos(varphi[0]); reduceMemoryFootprintNeg(varphi[0]); } else if (varphi.isAtom()) { x->termforcnf = ASTNodeToASTNodePtr(varphi); } else { ASTVec psis; ASTVec::const_iterator it = varphi.GetChildren().begin(); for (; it != varphi.GetChildren().end(); it++) { convertTermForCNF(*it, defs); psis.push_back(*(info[*it]->termforcnf)); } ASTNode psi = bm->CreateNode(varphi.GetKind(), psis); psi.SetValueWidth(varphi.GetValueWidth()); psi.SetIndexWidth(varphi.GetIndexWidth()); x->termforcnf = ASTNodeToASTNodePtr(psi); } } //End of convertTermForCNF()
bool containsArrayOps(const ASTNode& n, hash_set<int> & visited) { if (n.GetIndexWidth() > 0) return true; if (n.Degree() ==0) return false; if (visited.find(n.GetNodeNum()) != visited.end()) return false; visited.insert(n.GetNodeNum()); for (int i =0; i < n.Degree();i++) if (containsArrayOps(n[i],visited)) return true; return false; }
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; }
/* This function transforms Array Reads, Read over Writes, Read over * ITEs into flattened form. * * Transform1: Suppose there are two array reads in the input * Read(A,i) and Read(A,j) over the same array. Then Read(A,i) is * replaced with a symbolic constant, say v1, and Read(A,j) is * replaced with the following ITE: * * ITE(i=j,v1,v2) * */ ASTNode ArrayTransformer::TransformArrayRead(const ASTNode& term) { assert(TransformMap != NULL); const unsigned int width = term.GetValueWidth(); if (READ != term.GetKind()) return term; ASTNodeMap::const_iterator iter; if ((iter = TransformMap->find(term)) != TransformMap->end()) return iter->second; //'term' is of the form READ(arrName, readIndex) const ASTNode& arrName = term[0]; const ASTNode& readIndex = TransformTerm(term[1]); ASTNode result; switch (arrName.GetKind()) { case SYMBOL: { /* input is of the form: READ(A, readIndex) * * output is of the from: A1, if this is the first READ over A * * ITE(previous_readIndex=readIndex,A1,A2) * * ..... */ { ArrType::const_iterator it; if ((it = arrayToIndexToRead.find(arrName)) != arrayToIndexToRead.end()) { std::map<ASTNode, ArrayRead>::const_iterator it2; if ((it2 = it->second.find(readIndex)) != it->second.end()) { result = it2->second.ite; break; } } } // Make up a new abstract variable. Build symbolic name // corresponding to array read. The symbolic name has 2 // components: stringname, and a count ASTNode CurrentSymbol = bm->CreateFreshVariable(term.GetIndexWidth(), term.GetValueWidth(), "array_" + std::string(arrName.GetName())); result = CurrentSymbol; if (!bm->UserFlags.ackermannisation) { // result is a variable here; it is an ite in the // else-branch } else if (bm->UserFlags.isSet("old_ack", "0")) { /* oops. * This version of ack. doesn't do what I thought it did. The STP 0.1 * version of Ack. produces simpler * expressions. I've put that in the next block. Trevor's thesis * measures AckITE using this implementation, * rather than the next one like it should have!!!! */ // Full Array transform if we're not doing read refinement. // list of array-read indices corresponding to arrName, seen while // traversing the AST tree. we need this list to construct the ITEs const arrTypeMap& new_read_Indices = arrayToIndexToRead[arrName]; arrTypeMap::const_iterator it2 = new_read_Indices.begin(); arrTypeMap::const_iterator it2end = new_read_Indices.end(); for (; it2 != it2end; it2++) { ASTNode cond = simp->CreateSimplifiedEQ(readIndex, it2->first); if (ASTFalse == cond) continue; if (ASTTrue == cond) { result = it2->second.ite; break; } result = simp->CreateSimplifiedTermITE(cond, it2->second.ite, result); } } else { // Full Array transform if we're not doing read refinement. // list of array-read indices corresponding to arrName, seen while // traversing the AST tree. we need this list to construct the ITEs vector<std::pair<ASTNode, ASTNode>> p = ack_pair[arrName]; vector<std::pair<ASTNode, ASTNode>>::const_reverse_iterator it2 = p.rbegin(); vector<std::pair<ASTNode, ASTNode>>::const_reverse_iterator it2end = p.rend(); for (; it2 != it2end; it2++) { ASTNode cond = simp->CreateSimplifiedEQ(readIndex, it2->first); if (ASTFalse == cond) continue; if (ASTTrue == cond) { result = it2->second; break; } result = simp->CreateSimplifiedTermITE(cond, it2->second, result); } ack_pair[arrName].push_back(make_pair(readIndex, CurrentSymbol)); } assert(arrName.GetType() == ARRAY_TYPE); arrayToIndexToRead[arrName].insert( make_pair(readIndex, ArrayRead(result, CurrentSymbol))); break; } case WRITE: { /* The input to this case is: READ((WRITE A i val) j) * * The output of this case is: ITE( (= i j) val (READ A j)) */ /* 1. arrName or term[0] is infact a WRITE(A,i,val) expression * * 2. term[1] is the read-index j * * 3. arrName[0] is the new arrName i.e. A. A can be either a SYMBOL or a nested WRITE. no other possibility * * 4. arrName[1] is the WRITE index i.e. i * * 5. arrName[2] is the WRITE value i.e. val (val can inturn * be an array read) */ ASTNode writeIndex = TransformTerm(arrName[1]); ASTNode writeVal = TransformTerm(arrName[2]); if (ARRAY_TYPE != arrName[0].GetType()) FatalError("TransformArray: " "An array write is being attempted on a non-array:", term); // if ((SYMBOL == arrName[0].GetKind() //|| WRITE == arrName[0].GetKind())) { ASTNode cond = simp->CreateSimplifiedEQ(writeIndex, readIndex); assert(BVTypeCheck(cond)); // If the condition is true, it saves iteratively transforming through // all the (possibly nested) arrays. if (ASTTrue == cond) { result = writeVal; } else { ASTNode readTerm = nf->CreateTerm(READ, width, arrName[0], readIndex); assert(BVTypeCheck(readTerm)); // The simplifying node factory may have produced // something that's not a READ. ASTNode readPushedIn = TransformTerm(readTerm); assert(BVTypeCheck(readPushedIn)); result = simp->CreateSimplifiedTermITE(cond, writeVal, readPushedIn); } } // Trevor: I've removed this code because I don't see the advantage in working // inside out. i.e. transforming read(write(ite(p,A,B),i,j),k), into // read(ite(p,write(A,i,j),write(B,i,j),k). That is bringing up the ite. // Without this code it will become: ite(i=k, j, read(ite(p,A,B),k)) #if 0 else if (ITE == arrName[0].GetKind()) { // pull out the ite from the write // pushes the write // through. ASTNode writeTrue = nf->CreateNode(WRITE, (arrName[0][1]), writeIndex, writeVal); writeTrue.SetIndexWidth(writeIndex.GetValueWidth()); writeTrue.SetValueWidth(writeVal.GetValueWidth()); assert(ARRAY_TYPE == writeTrue.GetType()); ASTNode writeFalse = nf->CreateNode(WRITE, (arrName[0][2]), writeIndex, writeVal); writeFalse.SetIndexWidth(writeIndex.GetValueWidth()); writeFalse.SetValueWidth(writeVal.GetValueWidth()); assert(ARRAY_TYPE == writeFalse.GetType()); result = (writeTrue == writeFalse) ? writeTrue : simp->CreateSimplifiedTermITE(TransformFormula(arrName[0][0]), writeTrue, writeFalse); result.SetIndexWidth(writeIndex.GetValueWidth()); result.SetValueWidth(writeVal.GetValueWidth()); assert(ARRAY_TYPE == result.GetType()); result = nf->CreateTerm(READ, writeVal.GetValueWidth(), result, readIndex); BVTypeCheck(result); result = TransformArrayRead(result); } else FatalError("TransformArray: Write over bad type."); #endif break; } case ITE: { /* READ((ITE cond thn els) j) * * is transformed into * * (ITE cond (READ thn j) (READ els j)) */ // pull out the ite from the read // pushes the read through. //(ITE cond thn els) ASTNode cond = arrName[0]; cond = TransformFormula(cond); const ASTNode& thn = arrName[1]; const ASTNode& els = arrName[2]; //(READ thn j) ASTNode thnRead = nf->CreateTerm(READ, width, thn, readIndex); assert(BVTypeCheck(thnRead)); //(READ els j) ASTNode elsRead = nf->CreateTerm(READ, width, els, readIndex); assert(BVTypeCheck(elsRead)); /* We try to call TransformTerm only if necessary, because it * introduces a new symbol for each read. The amount of work we * need to do later is based on the square of the number of symbols. */ if (ASTTrue == cond) { result = TransformTerm(thnRead); } else if (ASTFalse == cond) { result = TransformTerm(elsRead); } else { thnRead = TransformTerm(thnRead); elsRead = TransformTerm(elsRead); //(ITE cond (READ thn j) (READ els j)) result = simp->CreateSimplifiedTermITE(cond, thnRead, elsRead); } break; } default: FatalError("TransformArray: " "The READ is NOT over SYMBOL/WRITE/ITE", term); break; }
ASTNode ArrayTransformer::TransformTerm(const ASTNode& term) { assert(TransformMap != NULL); const Kind k = term.GetKind(); if (!is_Term_kind(k)) FatalError("TransformTerm: Illegal kind: You have input a nonterm:", term, k); ASTNodeMap::const_iterator iter; if ((iter = TransformMap->find(term)) != TransformMap->end()) return iter->second; ASTNode result; switch (k) { case SYMBOL: case BVCONST: { result = term; break; } case WRITE: FatalError("TransformTerm: this kind is not supported", term); break; case READ: result = TransformArrayRead(term); break; case ITE: { ASTNode cond = term[0]; ASTNode thn = term[1]; ASTNode els = term[2]; cond = TransformFormula(cond); if (ASTTrue == cond) result = TransformTerm(thn); else if (ASTFalse == cond) result = TransformTerm(els); else { thn = TransformTerm(thn); els = TransformTerm(els); if (bm->UserFlags.optimize_flag) result = simp->CreateSimplifiedTermITE(cond, thn, els); else result = nf->CreateTerm(ITE, thn.GetValueWidth(), cond, thn, els); } assert(result.GetIndexWidth() == term.GetIndexWidth()); break; } default: { const ASTVec& c = term.GetChildren(); ASTVec::const_iterator it = c.begin(); ASTVec::const_iterator itend = c.end(); const unsigned width = term.GetValueWidth(); const unsigned indexwidth = term.GetIndexWidth(); ASTVec o; o.reserve(c.size()); for (; it != itend; it++) { o.push_back(TransformTerm(*it)); } if (c != o) { result = nf->CreateArrayTerm(k, indexwidth, width, o); } else result = term; } break; } if (term.Degree() > 0) (*TransformMap)[term] = result; if (term.GetValueWidth() != result.GetValueWidth()) FatalError("TransformTerm: " "result and input terms are of different length", result); if (term.GetIndexWidth() != result.GetIndexWidth()) { std::cerr << "TransformTerm: input term is : " << term << std::endl; FatalError("TransformTerm: " "result & input terms have different index length", result); } return result; }
ASTNode ArrayTransformer::TransformTerm(const ASTNode& term) { assert(TransformMap != NULL); const Kind k = term.GetKind(); if (!is_Term_kind(k)) FatalError("TransformTerm: Illegal kind: You have input a nonterm:", term, k); ASTNodeMap::const_iterator iter; if ((iter = TransformMap->find(term)) != TransformMap->end()) return iter->second; ASTNode result; switch (k) { case SYMBOL: case BVCONST: { result = term; break; } case WRITE: FatalError("TransformTerm: this kind is not supported", term); break; case READ: result = TransformArrayRead(term); break; case ITE: { ASTNode cond = term[0]; ASTNode thn = term[1]; ASTNode els = term[2]; cond = TransformFormula(cond); if (ASTTrue == cond) result = TransformTerm(thn); else if (ASTFalse == cond) result = TransformTerm(els); else { thn = TransformTerm(thn); els = TransformTerm(els); result = simp->CreateSimplifiedTermITE(cond, thn, els); } assert(result.GetIndexWidth() ==term.GetIndexWidth()); break; } default: { const ASTVec& c = term.GetChildren(); ASTVec::const_iterator it = c.begin(); ASTVec::const_iterator itend = c.end(); const unsigned width = term.GetValueWidth(); const unsigned indexwidth = term.GetIndexWidth(); ASTVec o; o.reserve(c.size()); for (; it != itend; it++) { o.push_back(TransformTerm(*it)); } if (c!=o) { result = nf->CreateArrayTerm(k,indexwidth, width, o); } else result = term; const Kind k = result.GetKind(); if (BVDIV == k || BVMOD == k || SBVDIV == k || SBVREM == k || SBVMOD == k) { // I had this as a reference, but that was wrong. Because // "result" gets over-written in the next block, result[1], may // get a reference count of zero, so be garbage collected. const ASTNode bottom = result[1]; if (SBVDIV == result.GetKind() || SBVREM == result.GetKind() || SBVMOD == result.GetKind()) { result = TranslateSignedDivModRem(result); } if (bm->UserFlags.division_by_zero_returns_one_flag) { // This is a difficult rule to introduce in other // places because it's recursive. i.e. result is // embedded unchanged inside the result. unsigned inputValueWidth = result.GetValueWidth(); ASTNode zero = bm->CreateZeroConst(inputValueWidth); ASTNode one = bm->CreateOneConst(inputValueWidth); result = nf->CreateTerm(ITE, inputValueWidth, nf->CreateNode(EQ, zero, bottom), one, result); //return result; if (bm->UserFlags.optimize_flag) return simp->SimplifyTerm_TopLevel(result); else return result; } } } break; } if (term.Degree() > 0) (*TransformMap)[term] = result; if (term.GetValueWidth() != result.GetValueWidth()) FatalError("TransformTerm: "\ "result and input terms are of different length", result); if (term.GetIndexWidth() != result.GetIndexWidth()) { cerr << "TransformTerm: input term is : " << term << endl; FatalError("TransformTerm: "\ "result & input terms have different index length", result); } return result; } //End of TransformTerm