// $Id$ //************************************************************************* // DESCRIPTION: Verilator: Add temporaries, such as for expand nodes // // Code available from: http://www.veripool.com/verilator // // AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli // //************************************************************************* // // Copyright 2004-2007 by Wilson Snyder. This program is free software; you can // redistribute it and/or modify it under the terms of either the GNU // General Public License or the Perl Artistic License. // // Verilator is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // //************************************************************************* // V3Expand's Transformations: // // Each module: // Expand verilated.h macros into internal micro optimizations (RTL) // this will enable later optimizations. // Wide operands become assignments to each word of the vector, (WORDSELs) // Note in this case that the widthMin is not correct for the MSW of // the vector. This must be accounted for if doing later constant // propagation across signals. // //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include #include #include #include #include "V3Global.h" #include "V3Expand.h" #include "V3Ast.h" //###################################################################### // Expand state, as a visitor of each AstNode class ExpandVisitor : public AstNVisitor { private: // NODE STATE // STATE AstNode* m_stmtp; // Current statement //int debug() { return 9; } // METHODS int longOrQuadWidth (AstNode* nodep) { // Return 32 or 64... return (nodep->width()+(VL_WORDSIZE-1)) & ~(VL_WORDSIZE-1); } V3Number notWideMask (AstNode* nodep) { return V3Number (nodep->fileline(), VL_WORDSIZE, ~VL_MASK_I(nodep->widthMin())); } V3Number wordMask (AstNode* nodep) { if (nodep->isWide()) { return V3Number (nodep->fileline(), VL_WORDSIZE, VL_MASK_I(nodep->widthMin())); } else { V3Number mask (nodep->fileline(), longOrQuadWidth(nodep)); mask.setMask(nodep->widthMin()); return mask; } } void insertBefore (AstNode* placep, AstNode* newp) { AstNRelinker linker; placep->unlinkFrBack(&linker); newp->addNext(placep); linker.relink(newp); } void replaceWithDelete (AstNode* nodep, AstNode* newp) { nodep->replaceWith(newp); nodep->deleteTree(); nodep=NULL; } AstNode* newWordAssign (AstNodeAssign* placep, int word, AstNode* lhsp, AstNode* rhsp) { AstAssign* newp = new AstAssign (placep->fileline(), new AstWordSel (placep->fileline(), lhsp->cloneTree(true), new AstConst (placep->fileline(), word)), rhsp); return newp; } void addWordAssign (AstNodeAssign* placep, int word, AstNode* lhsp, AstNode* rhsp) { insertBefore (placep, newWordAssign(placep, word, lhsp, rhsp)); } void addWordAssign (AstNodeAssign* placep, int word, AstNode* rhsp) { addWordAssign (placep, word, placep->lhsp(), rhsp); } void fixCloneLvalue (AstNode* nodep) { // In AstSel transforms, we call clone() on VarRefs that were lvalues, // but are now being used on the RHS of the assignment if (nodep->castVarRef()) nodep->castVarRef()->lvalue(false); // Iterate if (nodep->op1p()) fixCloneLvalue(nodep->op1p()); if (nodep->op2p()) fixCloneLvalue(nodep->op2p()); if (nodep->op3p()) fixCloneLvalue(nodep->op3p()); if (nodep->op4p()) fixCloneLvalue(nodep->op4p()); } AstNode* newAstWordSelClone (AstNode* nodep, int word) { // Get the specified word number from a wide array // Or, if it's a long/quad, do appropriate conversion to wide // Concat may pass negative word numbers, that means it wants a zero if (nodep->isWide() && word>=0 && wordwidthWords()) { return new AstWordSel (nodep->fileline(), nodep->cloneTree(true), new AstConst(nodep->fileline(), word)); } else if (nodep->isQuad() && word==0) { AstNode* quadfromp = nodep->cloneTree(true); quadfromp->width(VL_QUADSIZE,quadfromp->widthMin()); return new AstCast (nodep->fileline(), quadfromp, VL_WORDSIZE); } else if (nodep->isQuad() && word==1) { AstNode* quadfromp = nodep->cloneTree(true); quadfromp->width(VL_QUADSIZE,quadfromp->widthMin()); return new AstCast (nodep->fileline(), new AstShiftR (nodep->fileline(), quadfromp, new AstConst (nodep->fileline(), VL_WORDSIZE), VL_WORDSIZE), VL_WORDSIZE); } else if (!nodep->isWide() && !nodep->isQuad() && word==0) { return nodep->cloneTree(true); } else { // Out of bounds return new AstConst (nodep->fileline(), 0); } } AstNode* newWordGrabShift (FileLine* fl, int word, AstNode* lhsp, int shift) { // Extract the expression to grab the value for the specified word, if it's the shift // of shift bits from lhsp AstNode* newp; // Negative word numbers requested for lhs when it's "before" what we want. // We get a 0 then. int othword = word - shift/VL_WORDSIZE; AstNode* llowp = newAstWordSelClone (lhsp, othword); if (int loffset = VL_BITBIT_I(shift)) { AstNode* lhip = newAstWordSelClone (lhsp, othword-1); int nbitsonright = VL_WORDSIZE-loffset; // bits that end up in lword newp = new AstOr (fl, new AstAnd(fl, new AstConst (fl, VL_MASK_I(loffset)), new AstShiftR (fl, lhip, new AstConst(fl, nbitsonright), VL_WORDSIZE)), new AstAnd(fl, new AstConst (fl, ~VL_MASK_I(loffset)), new AstShiftL(fl, llowp, new AstConst(fl, loffset), VL_WORDSIZE))); } else { newp = llowp; } return newp; } AstNode* newSelBitWord(AstNode* lsbp, int wordAdder) { // Return equation to get the VL_BITWORD of a constant or non-constant if (lsbp->castConst()) { return new AstConst (lsbp->fileline(), wordAdder + VL_BITWORD_I(lsbp->castConst()->asInt())); } else { AstNode* shiftp = new AstShiftR (lsbp->fileline(), lsbp->cloneTree(true), new AstConst(lsbp->fileline(), VL_WORDSIZE_LOG2), VL_WORDSIZE); if (wordAdder != 0) { shiftp = new AstAdd (lsbp->fileline(), // This is indexing a arraysel, so a 32 bit constant is fine new AstConst (lsbp->fileline(), wordAdder), shiftp); } return shiftp; } } AstNode* dropCondBound(AstNode* nodep) { // Experimental only... // If there's a CONDBOUND safety to keep arrays in bounds, // we're going to AND it to a value that always fits inside a // word, so we don't need it. //if (nodep->castCondBound() && nodep->castCondBound()->lhsp()->castLte()) { // nodep = nodep->castCondBound()->rhsp(); //} return nodep; } AstNode* newSelBitBit(AstNode* lsbp) { // Return equation to get the VL_BITBIT of a constant or non-constant if (lsbp->castConst()) { return new AstConst (lsbp->fileline(), VL_BITBIT_I(lsbp->castConst()->asInt())); } else { return new AstAnd (lsbp->fileline(), new AstConst(lsbp->fileline(), VL_WORDSIZE-1), dropCondBound(lsbp)->cloneTree(true)); } } //==================== bool expandWide (AstNodeAssign* nodep, AstConst* rhsp) { UINFO(8," Wordize ASSIGN(CONST) "< {for each_word{ ASSIGN(WORDSEL(wide,#),WORDSEL(CONST,#))}} if (rhsp->num().isFourState()) { rhsp->v3error("Unsupported: 4-state numbers in this context"); } for (int w=0; wwidthWords(); w++) { V3Number num (nodep->fileline(), VL_WORDSIZE, rhsp->num().dataWord(w)); addWordAssign(nodep, w, new AstConst (nodep->fileline(), num)); } return true; } //-------- Uniops bool expandWide (AstNodeAssign* nodep, AstVarRef* rhsp) { UINFO(8," Wordize ASSIGN(VARREF) "<widthWords(); w++) { addWordAssign(nodep, w, newAstWordSelClone (rhsp, w)); } return true; } bool expandWide (AstNodeAssign* nodep, AstArraySel* rhsp) { UINFO(8," Wordize ASSIGN(ARRAYSEL) "<widthWords(); w++) { addWordAssign(nodep, w, newAstWordSelClone (rhsp, w)); } return true; } bool expandWide (AstNodeAssign* nodep, AstNot* rhsp) { UINFO(8," Wordize ASSIGN(NOT) "< {for each_word{ ASSIGN(WORDSEL(wide,#),NOT(WORDSEL(lhs,#))) }} for (int w=0; wwidthWords(); w++) { addWordAssign(nodep, w, new AstNot (rhsp->fileline(), newAstWordSelClone (rhsp->lhsp(), w))); } return true; } //-------- Biops bool expandWide (AstNodeAssign* nodep, AstAnd* rhsp) { UINFO(8," Wordize ASSIGN(AND) "<widthWords(); w++) { addWordAssign(nodep, w, new AstAnd (nodep->fileline(), newAstWordSelClone (rhsp->lhsp(), w), newAstWordSelClone (rhsp->rhsp(), w))); } return true; } bool expandWide (AstNodeAssign* nodep, AstOr* rhsp) { UINFO(8," Wordize ASSIGN(OR) "<widthWords(); w++) { addWordAssign(nodep, w, new AstOr (nodep->fileline(), newAstWordSelClone (rhsp->lhsp(), w), newAstWordSelClone (rhsp->rhsp(), w))); } return true; } bool expandWide (AstNodeAssign* nodep, AstXor* rhsp) { UINFO(8," Wordize ASSIGN(XOR) "<widthWords(); w++) { addWordAssign(nodep, w, new AstXor (nodep->fileline(), newAstWordSelClone (rhsp->lhsp(), w), newAstWordSelClone (rhsp->rhsp(), w))); } return true; } bool expandWide (AstNodeAssign* nodep, AstXnor* rhsp) { UINFO(8," Wordize ASSIGN(XNOR) "<widthWords(); w++) { addWordAssign(nodep, w, new AstXnor (nodep->fileline(), newAstWordSelClone (rhsp->lhsp(), w), newAstWordSelClone (rhsp->rhsp(), w))); } return true; } //-------- Triops bool expandWide (AstNodeAssign* nodep, AstNodeCond* rhsp) { UINFO(8," Wordize ASSIGN(COND) "<widthWords(); w++) { addWordAssign(nodep, w, new AstCond (nodep->fileline(), rhsp->condp()->cloneTree(true), newAstWordSelClone (rhsp->expr1p(), w), newAstWordSelClone (rhsp->expr2p(), w))); } return true; } // VISITORS virtual void visit(AstExtend* nodep, AstNUser*) { nodep->iterateChildren(*this); if (nodep->isWide()) { // See under ASSIGN(EXTEND) } else { AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); AstNode* newp = lhsp; if (nodep->isQuad()) { if (lhsp->isQuad()) { lhsp->widthSignedFrom(nodep); // Just mark it, else nop } else if (lhsp->isWide()) { nodep->v3fatalSrc("extending larger thing into smaller?"); } else { UINFO(8," EXTEND(q<-l) "<fileline(), lhsp, nodep); } } else { // Long if (lhsp->isQuad() || lhsp->isWide()) { nodep->v3fatalSrc("extending larger thing into smaller?"); } else { lhsp->widthSignedFrom(nodep); // Just mark it, else nop } } replaceWithDelete(nodep,newp); nodep=NULL; } } bool expandWide (AstNodeAssign* nodep, AstExtend* rhsp) { UINFO(8," Wordize ASSIGN(EXTEND) "<lhsp()->widthWords(); w++) { addWordAssign(nodep, w, newAstWordSelClone (rhsp->lhsp(), w)); } for (; wwidthWords(); w++) { addWordAssign(nodep, w, new AstConst (rhsp->fileline(), 0)); } return true; } virtual void visit(AstSel* nodep, AstNUser*) { nodep->iterateChildren(*this); // Remember, Sel's may have non-integer rhs, so need to optimize for that! if (nodep->widthMin()!=(int)nodep->widthConst()) nodep->v3fatalSrc("Width mismatch"); if (nodep->backp()->castNodeAssign() && nodep==nodep->backp()->castNodeAssign()->lhsp()) { // Sel is an LHS assignment select } else if (nodep->isWide()) { // See under ASSIGN(WIDE) } else if (nodep->fromp()->isWide()) { UINFO(8," SEL(wide) "<fromp()->fileline(), nodep->fromp()->cloneTree(true), newSelBitWord(nodep->lsbp(), 0)); if (nodep->isQuad() && !lowwordp->isQuad()) lowwordp = new AstCast(nodep->fileline(), lowwordp, nodep); AstNode* lowp = new AstShiftR (nodep->fileline(), lowwordp, newSelBitBit(nodep->lsbp()), nodep->width()); // If > 1 bit, we might be crossing the word boundary AstNode* midp=NULL; V3Number zero (nodep->fileline(), longOrQuadWidth(nodep)); if (nodep->widthConst() > 1) { AstNode* midwordp = // SEL(from,[1+wordnum]) new AstWordSel (nodep->fromp()->fileline(), nodep->fromp()->cloneTree(true), newSelBitWord(nodep->lsbp(), 1)); if (nodep->isQuad() && !midwordp->isQuad()) midwordp = new AstCast(nodep->fileline(), midwordp, nodep); // If we're selecting bit zero, then all 32 bits in word 1 get shifted << by 32 bits // else we need to form the lower word, so we << by 31 or less // nbitsfromlow <= (lsb==0) ? 64-bitbit(lsb) : 32-bitbit(lsb) AstNode* midshiftp = new AstSub (nodep->lsbp()->fileline(), new AstConst(nodep->lsbp()->fileline(), VL_WORDSIZE), newSelBitBit(nodep->lsbp())); if (nodep->isQuad()) { midshiftp = new AstCond (nodep->fileline(), new AstEq (nodep->fileline(), new AstConst(nodep->fileline(), 0), newSelBitBit(nodep->lsbp())), new AstConst(nodep->lsbp()->fileline(), VL_WORDSIZE), midshiftp); } AstNode* midmayp = new AstShiftL (nodep->fileline(), midwordp, midshiftp, nodep->width()); if (nodep->isQuad()) { midp = midmayp; // Always grab from two words } else { midp = new AstCond (nodep->fileline(), new AstEq (nodep->fileline(), new AstConst(nodep->fileline(), 0), newSelBitBit(nodep->lsbp())), new AstConst(nodep->fileline(), zero), midmayp); } } // If > 32 bits, we might be crossing the second word boundary AstNode* hip=NULL; if (nodep->widthConst() > VL_WORDSIZE) { AstNode* hiwordp = // SEL(from,[2+wordnum]) new AstWordSel (nodep->fromp()->fileline(), nodep->fromp()->cloneTree(true), newSelBitWord(nodep->lsbp(), 2)); if (nodep->isQuad() && !hiwordp->isQuad()) hiwordp = new AstCast(nodep->fileline(), hiwordp, nodep); AstNode* himayp = new AstShiftL (nodep->fileline(), hiwordp, // nbitsfromlow_and_mid <= 64-bitbit(lsb) new AstSub (nodep->lsbp()->fileline(), new AstConst(nodep->lsbp()->fileline(), 64), newSelBitBit(nodep->lsbp())), nodep->width()); // if (frombit==0) then ignore, else use it hip = new AstCond (nodep->fileline(), new AstEq (nodep->fileline(), new AstConst(nodep->fileline(), 0), newSelBitBit(nodep->lsbp())), new AstConst(nodep->fileline(), zero), himayp); } AstNode* newp = lowp; if (midp) newp = new AstOr (nodep->fileline(), midp, newp); if (hip) newp = new AstOr (nodep->fileline(), hip, newp); newp->widthFrom(nodep); replaceWithDelete(nodep,newp); nodep=NULL; } else { // Long/Quad from Long/Quad UINFO(8," SEL->SHIFT "<fromp()->unlinkFrBack(); AstNode* lsbp = nodep->lsbp()->unlinkFrBack(); if (nodep->isQuad() && !fromp->isQuad()) fromp = new AstCast(nodep->fileline(), fromp, nodep); AstNode* newp = new AstShiftR (nodep->fileline(), fromp, dropCondBound(lsbp), nodep->width()); newp->widthSignedFrom(nodep); if (!nodep->isQuad() && fromp->isQuad()) { newp = new AstCast (newp->fileline(), newp, nodep); } newp->widthSignedFrom(nodep); replaceWithDelete(nodep,newp); nodep=NULL; } } bool expandWide (AstNodeAssign* nodep, AstSel* rhsp) { if (nodep->widthMin()!=(int)rhsp->widthConst()) nodep->v3fatalSrc("Width mismatch"); if (rhsp->lsbp()->castConst() && VL_BITBIT_I(rhsp->lsbConst())==0) { int lsb = rhsp->lsbConst(); UINFO(8," Wordize ASSIGN(SEL,align) "<widthWords(); w++) { addWordAssign(nodep, w, newAstWordSelClone (rhsp->fromp(), w + VL_BITWORD_I(lsb))); } return true; } else { UINFO(8," Wordize ASSIGN(EXTRACT,misalign) "<widthWords(); w++) { // Grab lowest bits AstNode* lowwordp = new AstWordSel (rhsp->fileline(), rhsp->fromp()->cloneTree(true), newSelBitWord(rhsp->lsbp(), w)); AstNode* lowp = new AstShiftR (rhsp->fileline(), lowwordp, newSelBitBit(rhsp->lsbp()), VL_WORDSIZE); // Upper bits V3Number zero (nodep->fileline(), VL_WORDSIZE, 0); AstNode* midwordp = // SEL(from,[1+wordnum]) new AstWordSel (rhsp->fromp()->fileline(), rhsp->fromp()->cloneTree(true), newSelBitWord(rhsp->lsbp(), w+1)); AstNode* midshiftp = new AstSub (rhsp->lsbp()->fileline(), new AstConst(rhsp->lsbp()->fileline(), VL_WORDSIZE), newSelBitBit(rhsp->lsbp())); AstNode* midmayp = new AstShiftL (rhsp->fileline(), midwordp, midshiftp, VL_WORDSIZE); AstNode* midp = new AstCond (rhsp->fileline(), new AstEq (rhsp->fileline(), new AstConst(rhsp->fileline(), 0), newSelBitBit(rhsp->lsbp())), new AstConst(rhsp->fileline(), zero), midmayp); AstNode* newp = new AstOr (nodep->fileline(), midp, lowp); addWordAssign(nodep, w, newp); } return true; } } bool expandLhs(AstNodeAssign* nodep, AstSel* lhsp) { // Possibilities // destp: wide or narrow // rhsp: wide (destp must be wide), narrow, or 1 bit wide // rhsp: may be allones and can remove AND NOT gate // lsbp: constant or variable // Yuk. bool destwide = lhsp->fromp()->isWide(); bool ones = nodep->rhsp()->isAllOnesV(); if (lhsp->lsbp()->castConst()) { // The code should work without this constant test, but it won't // constify as nicely as we'd like. AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); AstNode* destp = lhsp->fromp()->unlinkFrBack(); int lsb = lhsp->lsbConst(); int msb = lhsp->msbConst(); V3Number maskset (nodep->fileline(), destp->widthMin()); for (int bit=lsb; bit<(msb+1); bit++) maskset.setBit(bit,1); V3Number maskold (nodep->fileline(), destp->widthMin()); maskold.opNot(maskset); if (destwide) { UINFO(8," ASSIGNSEL(const,wide) "<widthWords(); w++) { if (w>=VL_BITWORD_I(lsb) && w<=VL_BITWORD_I(msb)) { // else we would just be setting it to the same exact value AstNode* oldvalp = newAstWordSelClone (destp, w); fixCloneLvalue(oldvalp); if (!ones) oldvalp = new AstAnd (lhsp->fileline(), new AstConst (lhsp->fileline(), maskold.dataWord(w)), oldvalp); addWordAssign(nodep, w, destp, new AstOr (lhsp->fileline(), oldvalp, newWordGrabShift(lhsp->fileline(), w, rhsp, lsb))); } } } else { UINFO(8," ASSIGNSEL(const,narrow) "<isQuad() && !rhsp->isQuad()) rhsp = new AstCast(nodep->fileline(), rhsp, nodep); AstNode* oldvalp = destp->cloneTree(true); fixCloneLvalue(oldvalp); if (!ones) oldvalp = new AstAnd (lhsp->fileline(), new AstConst (lhsp->fileline(), maskold), oldvalp); AstNode* newp = new AstOr (lhsp->fileline(), oldvalp, new AstShiftL (lhsp->fileline(), rhsp, new AstConst (lhsp->fileline(), lsb), destp->width())); newp = new AstAssign (nodep->fileline(), destp, newp); insertBefore(nodep,newp); } return true; } else { // non-const RHS if (destwide && lhsp->widthConst()==1) { UINFO(8," ASSIGNSEL(varlsb,wide,1bit) "<rhsp()->unlinkFrBack(); AstNode* destp = lhsp->fromp()->unlinkFrBack(); AstNode* oldvalp = new AstWordSel (lhsp->fileline(), destp->cloneTree(true), newSelBitWord(lhsp->lsbp(), 0)); fixCloneLvalue(oldvalp); if (!ones) oldvalp = new AstAnd (lhsp->fileline(), new AstNot (lhsp->fileline(), new AstShiftL (lhsp->fileline(), new AstConst (nodep->fileline(),1), // newSelBitBit may exceed the MSB of this variable. // That's ok as we'd just AND with a larger value, // but oldval would clip the upper bits to sanity newSelBitBit(lhsp->lsbp()), VL_WORDSIZE)), oldvalp); AstNode* newp = new AstOr (lhsp->fileline(), oldvalp, new AstShiftL (lhsp->fileline(), rhsp, lhsp->lsbp()->cloneTree(true), VL_WORDSIZE)); newp = new AstAssign (nodep->fileline(), new AstWordSel (nodep->fileline(), destp, newSelBitWord(lhsp->lsbp(), 0)), newp); insertBefore(nodep,newp); return true; } else if (destwide) { UINFO(8," ASSIGNSEL(varlsb,wide) -- NoOp -- "<dumpTree(cout,"- old: "); AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); AstNode* destp = lhsp->fromp()->unlinkFrBack(); AstNode* oldvalp = destp->cloneTree(true); fixCloneLvalue(oldvalp); V3Number maskwidth (nodep->fileline(), destp->widthMin()); for (int bit=0; bit<(int)lhsp->widthConst(); bit++) maskwidth.setBit(bit,1); if (destp->isQuad() && !rhsp->isQuad()) rhsp = new AstCast(nodep->fileline(), rhsp, nodep); if (!ones) oldvalp = new AstAnd (lhsp->fileline(), new AstNot (lhsp->fileline(), new AstShiftL (lhsp->fileline(), new AstConst (nodep->fileline(), maskwidth), lhsp->lsbp()->cloneTree(true), destp->width())), oldvalp); AstNode* newp = new AstOr (lhsp->fileline(), oldvalp, new AstShiftL (lhsp->fileline(), rhsp, lhsp->lsbp()->cloneTree(true), destp->width())); newp = new AstAssign (nodep->fileline(), destp, newp); //newp->dumpTree(cout,"- new: "); insertBefore(nodep,newp); return true; } } } virtual void visit(AstConcat* nodep, AstNUser*) { nodep->iterateChildren(*this); if (nodep->isWide()) { // See under ASSIGN(WIDE) } else { UINFO(8," CONCAT "<lhsp()->unlinkFrBack(); AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); int rhsshift = rhsp->widthMin(); if (nodep->isQuad() && !lhsp->isQuad()) lhsp = new AstCast(nodep->fileline(), lhsp, nodep); if (nodep->isQuad() && !rhsp->isQuad()) rhsp = new AstCast(nodep->fileline(), rhsp, nodep); AstNode* newp = new AstOr (nodep->fileline(), new AstShiftL (nodep->fileline(), lhsp, new AstConst (nodep->fileline(), rhsshift), nodep->width()), rhsp); newp->widthFrom(nodep); // Unsigned replaceWithDelete(nodep,newp); nodep=NULL; } } bool expandWide (AstNodeAssign* nodep, AstConcat* rhsp) { UINFO(8," Wordize ASSIGN(CONCAT) "<rhsp()->widthMin(); // Sometimes doing the words backwards is preferrable. // When we have x={x,foo} backwards is better, when x={foo,x} forward is better // However V3Subst tends to rip this up, so not worth optimizing now. for (int w=0; wwidthWords(); w++) { addWordAssign(nodep, w, new AstOr (rhsp->fileline(), newWordGrabShift(rhsp->fileline(), w, rhsp->lhsp(), rhsshift), newAstWordSelClone (rhsp->rhsp(), w))); } return true; } virtual void visit(AstReplicate* nodep, AstNUser*) { nodep->iterateChildren(*this); if (nodep->isWide()) { // See under ASSIGN(WIDE) } else { AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); AstNode* newp; int lhswidth = lhsp->widthMin(); if (lhswidth==1) { UINFO(8," REPLICATE(w1) "<fileline(), lhsp); } else { UINFO(8," REPLICATE "<rhsp()->castConst(); if (!constp) nodep->v3fatalSrc("Replication value isn't a constant. Checked earlier!"); uint32_t times = constp->asInt(); if (nodep->isQuad() && !lhsp->isQuad()) lhsp = new AstCast(nodep->fileline(), lhsp, nodep); newp = lhsp->cloneTree(true); for (unsigned repnum=1; repnumfileline(), new AstShiftL (nodep->fileline(), lhsp->cloneTree(true), new AstConst (nodep->fileline(), rhsshift), nodep->width()), newp); newp->widthFrom(nodep); // Unsigned } lhsp->deleteTree(); // Never used } newp->widthFrom(nodep); // Unsigned replaceWithDelete(nodep,newp); nodep=NULL; } } bool expandWide (AstNodeAssign* nodep, AstReplicate* rhsp) { UINFO(8," Wordize ASSIGN(REPLICATE) "<lhsp(); int lhswidth = lhsp->widthMin(); AstConst* constp = rhsp->rhsp()->castConst(); if (!constp) rhsp->v3fatalSrc("Replication value isn't a constant. Checked earlier!"); uint32_t times = constp->asInt(); for (int w=0; wwidthWords(); w++) { AstNode* newp; if (lhswidth==1) { newp = new AstUnaryMin (nodep->fileline(), lhsp->cloneTree(true)); newp->width(VL_WORDSIZE,VL_WORDSIZE); } else { newp = newAstWordSelClone (lhsp, w); for (unsigned repnum=1; repnumfileline(), newWordGrabShift(rhsp->fileline(), w, lhsp, lhswidth*repnum), newp); } } addWordAssign(nodep, w, newp); } return true; } virtual void visit(AstChangeXor* nodep, AstNUser*) { nodep->iterateChildren(*this); UINFO(8," Wordize ChangeXor "< (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}} AstNode* newp = NULL; for (int w=0; wlhsp()->widthWords(); w++) { AstNode* eqp = new AstXor (nodep->fileline(), newAstWordSelClone (nodep->lhsp(), w), newAstWordSelClone (nodep->rhsp(), w)); newp = (newp==NULL) ? eqp : (new AstOr (nodep->fileline(), newp, eqp)); } replaceWithDelete(nodep,newp); nodep=NULL; } void visitEqNeq(AstNodeBiop* nodep) { nodep->iterateChildren(*this); if (nodep->lhsp()->isWide()) { UINFO(8," Wordize EQ/NEQ "< (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}} AstNode* newp = NULL; for (int w=0; wlhsp()->widthWords(); w++) { AstNode* eqp = new AstXor (nodep->fileline(), newAstWordSelClone (nodep->lhsp(), w), newAstWordSelClone (nodep->rhsp(), w)); newp = (newp==NULL) ? eqp : (new AstOr (nodep->fileline(), newp, eqp)); } if (nodep->castNeq()) { newp = new AstNeq (nodep->fileline(), new AstConst (nodep->fileline(), 0), newp); } else { newp = new AstEq (nodep->fileline(), new AstConst (nodep->fileline(), 0), newp); } replaceWithDelete(nodep,newp); nodep=NULL; } } virtual void visit(AstEq* nodep, AstNUser*) { visitEqNeq (nodep); } virtual void visit(AstNeq* nodep, AstNUser*) { visitEqNeq (nodep); } virtual void visit(AstRedOr* nodep, AstNUser*) { nodep->iterateChildren(*this); if (nodep->lhsp()->isWide()) { UINFO(8," Wordize REDOR "< (0!={or{for each_word{WORDSEL(lhs,#)}}} AstNode* newp = NULL; for (int w=0; wlhsp()->widthWords(); w++) { AstNode* eqp = newAstWordSelClone (nodep->lhsp(), w); newp = (newp==NULL) ? eqp : (new AstOr (nodep->fileline(), newp, eqp)); } newp = new AstNeq (nodep->fileline(), new AstConst (nodep->fileline(), 0), newp); replaceWithDelete(nodep,newp); nodep=NULL; } else { UINFO(8," REDOR->EQ "<lhsp()->unlinkFrBack(); V3Number zero (nodep->fileline(), longOrQuadWidth(nodep)); AstNode* newp = new AstNeq (nodep->fileline(), new AstConst (nodep->fileline(), zero), lhsp); replaceWithDelete(nodep,newp); nodep=NULL; } } virtual void visit(AstRedAnd* nodep, AstNUser*) { nodep->iterateChildren(*this); if (nodep->lhsp()->isWide()) { UINFO(8," Wordize REDAND "< (0!={and{for each_word{WORDSEL(lhs,#)}}} AstNode* newp = NULL; for (int w=0; wlhsp()->widthWords(); w++) { AstNode* eqp = newAstWordSelClone (nodep->lhsp(), w); if (w==nodep->lhsp()->widthWords()-1) { // Rather then doing a (slowish) ==##, we OR in the bits that aren't part of the mask eqp = new AstOr (nodep->fileline(), new AstConst (nodep->fileline(), notWideMask(nodep->lhsp())), eqp); } newp = (newp==NULL) ? eqp : (new AstAnd (nodep->fileline(), newp, eqp)); } newp = new AstEq (nodep->fileline(), new AstConst (nodep->fileline(), ~0), newp); replaceWithDelete(nodep, newp); nodep=NULL; } else { UINFO(8," REDAND->EQ "<lhsp()->unlinkFrBack(); AstNode* newp = new AstEq (nodep->fileline(), new AstConst (nodep->fileline(), wordMask(lhsp)), lhsp); replaceWithDelete(nodep,newp); nodep=NULL; } } virtual void visit(AstRedXor* nodep, AstNUser*) { nodep->iterateChildren(*this); if (nodep->lhsp()->isWide()) { UINFO(8," Wordize REDXOR "< (0!={redxor{for each_word{XOR(WORDSEL(lhs,#))}}} AstNode* newp = NULL; for (int w=0; wlhsp()->widthWords(); w++) { AstNode* eqp = newAstWordSelClone (nodep->lhsp(), w); newp = (newp==NULL) ? eqp : (new AstXor (nodep->fileline(), newp, eqp)); } newp = new AstRedXor (nodep->fileline(), newp); replaceWithDelete(nodep, newp); nodep=NULL; } // We don't reduce non-wide XORs, as its more efficient to use a temp register, // which the inlined function does nicely. } virtual void visit(AstNodeStmt* nodep, AstNUser*) { m_stmtp = nodep; nodep->iterateChildren(*this); m_stmtp = NULL; } virtual void visit(AstNodeAssign* nodep, AstNUser*) { m_stmtp = nodep; nodep->iterateChildren(*this); bool did = false; if (nodep->isWide() && ((nodep->lhsp()->castVarRef() && !nodep->lhsp()->castVarRef()->varp()->isSc()) || nodep->lhsp()->castArraySel())) { if (AstConst* rhsp = nodep->rhsp()->castConst()) { did = expandWide(nodep,rhsp); } else if (AstVarRef* rhsp = nodep->rhsp()->castVarRef()) { if (rhsp->varp()->isSc()) { // Still need special access function } else { did = expandWide(nodep,rhsp); } } else if (AstSel* rhsp = nodep->rhsp()->castSel()) { did = expandWide(nodep,rhsp); } else if (AstArraySel* rhsp = nodep->rhsp()->castArraySel()) { did = expandWide(nodep,rhsp); } else if (AstConcat* rhsp = nodep->rhsp()->castConcat()) { did = expandWide(nodep,rhsp); } else if (AstReplicate* rhsp = nodep->rhsp()->castReplicate()) { did = expandWide(nodep,rhsp); } else if (AstAnd* rhsp = nodep->rhsp()->castAnd()) { did = expandWide(nodep,rhsp); } else if (AstOr* rhsp = nodep->rhsp()->castOr()) { did = expandWide(nodep,rhsp); } else if (AstNot* rhsp = nodep->rhsp()->castNot()) { did = expandWide(nodep,rhsp); } else if (AstXor* rhsp = nodep->rhsp()->castXor()) { did = expandWide(nodep,rhsp); } else if (AstXnor* rhsp = nodep->rhsp()->castXnor()) { did = expandWide(nodep,rhsp); } else if (AstNodeCond* rhsp = nodep->rhsp()->castNodeCond()) { did = expandWide(nodep,rhsp); } } else if (AstSel* lhsp = nodep->lhsp()->castSel()) { did = expandLhs(nodep,lhsp); } // Cleanup common code if (did) { nodep->unlinkFrBack()->deleteTree(); nodep=NULL; } m_stmtp = NULL; } //-------------------- // Default: Just iterate virtual void visit(AstVar*, AstNUser*) {} // Don't hit varrefs under vars virtual void visit(AstNode* nodep, AstNUser*) { nodep->iterateChildren(*this); } public: // CONSTUCTORS ExpandVisitor() { m_stmtp=NULL; } virtual ~ExpandVisitor() {} void main(AstNode* nodep) { AstNode::userClearTree(); // userp() used on entire tree nodep->accept(*this); } }; //---------------------------------------------------------------------- // Top loop //###################################################################### // Expand class functions void V3Expand::expandAll(AstNetlist* nodep) { UINFO(2,__FUNCTION__<<": "<