/* 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; }
/******************************************************** * TransformFormula() * * Get rid of DIV/MODs, ARRAY read/writes, FOR constructs ********************************************************/ ASTNode ArrayTransformer::TransformFormula(const ASTNode& simpleForm) { assert(TransformMap != NULL); const Kind k = simpleForm.GetKind(); if (!(is_Form_kind(k) && BOOLEAN_TYPE == simpleForm.GetType())) { // FIXME: "You have inputted a NON-formula"? FatalError("TransformFormula:" "You have input a NON-formula", simpleForm); } ASTNodeMap::const_iterator iter; if ((iter = TransformMap->find(simpleForm)) != TransformMap->end()) return iter->second; ASTNode result; switch (k) { case TRUE: case FALSE: { result = simpleForm; break; } case NOT: { ASTVec c; c.push_back(TransformFormula(simpleForm[0])); result = nf->CreateNode(NOT, c); break; } case BOOLEXTRACT: { ASTVec c; c.push_back(TransformTerm(simpleForm[0])); c.push_back(simpleForm[1]); result = nf->CreateNode(BOOLEXTRACT, c); break; } case BVLT: case BVLE: case BVGT: case BVGE: case BVSLT: case BVSLE: case BVSGT: case BVSGE: { ASTVec c; c.push_back(TransformTerm(simpleForm[0])); c.push_back(TransformTerm(simpleForm[1])); result = nf->CreateNode(k, c); break; } case EQ: { ASTNode term1 = TransformTerm(simpleForm[0]); ASTNode term2 = TransformTerm(simpleForm[1]); if (bm->UserFlags.optimize_flag) result = simp->CreateSimplifiedEQ(term1, term2); else result = nf->CreateNode(EQ, term1, term2); break; } case AND: // These could shortcut. Not sure if the extra effort is // justified. case OR: case NAND: case NOR: case IFF: case XOR: case ITE: case IMPLIES: { ASTVec vec; vec.reserve(simpleForm.Degree()); for (ASTVec::const_iterator it = simpleForm.begin(), itend = simpleForm.end(); it != itend; it++) { vec.push_back(TransformFormula(*it)); } result = nf->CreateNode(k, vec); break; } case PARAMBOOL: { // If the parameteric boolean variable is of the form // VAR(const), then convert it into a Boolean variable of the // form "VAR(const)". // // Else if the paramteric boolean variable is of the form // VAR(expression), then simply return it if (BVCONST == simpleForm[1].GetKind()) { result = bm->NewParameterized_BooleanVar(simpleForm[0], simpleForm[1]); } else { result = simpleForm; } break; } default: { if (k == SYMBOL && BOOLEAN_TYPE == simpleForm.GetType()) result = simpleForm; else { FatalError("TransformFormula: Illegal kind: ", ASTUndefined, k); std::cerr << "The input is: " << simpleForm << std::endl; std::cerr << "The valuewidth of input is : " << simpleForm.GetValueWidth() << std::endl; } break; } } assert(!result.IsNull()); if (simpleForm.Degree() > 0) (*TransformMap)[simpleForm] = 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); 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