verilator/src/V3Expand.cpp
2020-01-06 18:05:53 -05:00

985 lines
47 KiB
C++

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Add temporaries, such as for expand nodes
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2004-2020 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
//
// 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 "V3Global.h"
#include "V3Expand.h"
#include "V3Ast.h"
#include <algorithm>
#include <cstdarg>
//######################################################################
// Expand state, as a visitor of each AstNode
class ExpandVisitor : public AstNVisitor {
private:
// NODE STATE
// AstNode::user1() -> bool. Processed
AstUser1InUse m_inuser1;
// STATE
AstNode* m_stmtp; // Current statement
// METHODS
VL_DEBUG_FUNC; // Declare debug()
int longOrQuadWidth(AstNode* nodep) {
return (nodep->width() + (VL_EDATASIZE - 1)) & ~(VL_EDATASIZE - 1);
}
V3Number notWideMask(AstNode* nodep) {
return V3Number(nodep, VL_EDATASIZE, ~VL_MASK_E(nodep->widthMin()));
}
V3Number wordMask(AstNode* nodep) {
if (nodep->isWide()) {
return V3Number(nodep, VL_EDATASIZE, VL_MASK_E(nodep->widthMin()));
} else {
V3Number mask (nodep, longOrQuadWidth(nodep));
mask.setMask(nodep->widthMin());
return mask;
}
}
void insertBefore(AstNode* placep, AstNode* newp) {
newp->user1(1); // Already processed, don't need to re-iterate
AstNRelinker linker;
placep->unlinkFrBack(&linker);
newp->addNext(placep);
linker.relink(newp);
}
void replaceWithDelete(AstNode* nodep, AstNode* newp) {
newp->user1(1); // Already processed, don't need to re-iterate
nodep->replaceWith(newp);
nodep->deleteTree(); VL_DANGLING(nodep);
}
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 (VN_IS(nodep, VarRef)) VN_CAST(nodep, VarRef)->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 && word<nodep->widthWords()) {
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->dtypeSetBitUnsized(VL_QUADSIZE, quadfromp->widthMin(),
AstNumeric::UNSIGNED);
return new AstCCast(nodep->fileline(),
quadfromp,
VL_EDATASIZE);
} else if (nodep->isQuad() && word==1) {
AstNode* quadfromp = nodep->cloneTree(true);
quadfromp->dtypeSetBitUnsized(VL_QUADSIZE, quadfromp->widthMin(),
AstNumeric::UNSIGNED);
return new AstCCast(nodep->fileline(),
new AstShiftR(nodep->fileline(),
quadfromp,
new AstConst(nodep->fileline(), VL_EDATASIZE),
VL_EDATASIZE),
VL_EDATASIZE);
} 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_EDATASIZE;
AstNode* llowp = newAstWordSelClone(lhsp, othword);
if (int loffset = VL_BITBIT_E(shift)) {
AstNode* lhip = newAstWordSelClone(lhsp, othword-1);
int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword
newp = new AstOr
(fl,
new AstAnd(fl,
new AstConst(fl, AstConst::SizedEData(), VL_MASK_E(loffset)),
new AstShiftR(fl,
lhip,
new AstConst(fl, nbitsonright),
VL_EDATASIZE)),
new AstAnd(fl,
new AstConst(fl, AstConst::SizedEData(), ~VL_MASK_E(loffset)),
new AstShiftL(fl,
llowp,
new AstConst(fl, loffset),
VL_EDATASIZE)));
} 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 (VN_IS(lsbp, Const)) {
return new AstConst(lsbp->fileline(),
wordAdder + VL_BITWORD_E(VN_CAST(lsbp, Const)->toUInt()));
} else {
AstNode* shiftp = new AstShiftR(lsbp->fileline(),
lsbp->cloneTree(true),
new AstConst(lsbp->fileline(), VL_EDATASIZE_LOG2),
VL_EDATASIZE);
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 (VN_IS(nodep, CondBound) && VN_IS(VN_CAST(nodep, CondBound)->lhsp(), Lte)) {
// nodep = VN_CAST(nodep, CondBound)->rhsp();
//}
return nodep;
}
AstNode* newSelBitBit(AstNode* lsbp) {
// Return equation to get the VL_BITBIT of a constant or non-constant
if (VN_IS(lsbp, Const)) {
return new AstConst(lsbp->fileline(),
VL_BITBIT_E(VN_CAST(lsbp, Const)->toUInt()));
} else {
return new AstAnd(lsbp->fileline(),
new AstConst(lsbp->fileline(), VL_EDATASIZE - 1),
dropCondBound(lsbp)->cloneTree(true));
}
}
//====================
bool expandWide(AstNodeAssign* nodep, AstConst* rhsp) {
UINFO(8," Wordize ASSIGN(CONST) "<<nodep<<endl);
// -> {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; w<nodep->widthWords(); w++) {
addWordAssign(nodep, w, new AstConst(nodep->fileline(),
AstConst::SizedEData(),
rhsp->num().edataWord(w)));
}
return true;
}
//-------- Uniops
bool expandWide(AstNodeAssign* nodep, AstVarRef* rhsp) {
UINFO(8," Wordize ASSIGN(VARREF) "<<nodep<<endl);
for (int w=0; w<nodep->widthWords(); w++) {
addWordAssign(nodep, w, newAstWordSelClone(rhsp, w));
}
return true;
}
bool expandWide(AstNodeAssign* nodep, AstArraySel* rhsp) {
UINFO(8," Wordize ASSIGN(ARRAYSEL) "<<nodep<<endl);
UASSERT_OBJ(!VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType), nodep,
"ArraySel with unpacked arrays should have been removed in V3Slice");
for (int w=0; w<nodep->widthWords(); w++) {
addWordAssign(nodep, w, newAstWordSelClone(rhsp, w));
}
return true;
}
bool expandWide(AstNodeAssign* nodep, AstNot* rhsp) {
UINFO(8," Wordize ASSIGN(NOT) "<<nodep<<endl);
// -> {for each_word{ ASSIGN(WORDSEL(wide,#),NOT(WORDSEL(lhs,#))) }}
for (int w=0; w<nodep->widthWords(); 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) "<<nodep<<endl);
for (int w=0; w<nodep->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) "<<nodep<<endl);
for (int w=0; w<nodep->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) "<<nodep<<endl);
for (int w=0; w<nodep->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) "<<nodep<<endl);
for (int w=0; w<nodep->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) "<<nodep<<endl);
for (int w=0; w<nodep->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) {
if (nodep->user1SetOnce()) return; // Process once
iterateChildren(nodep);
if (nodep->isWide()) {
// See under ASSIGN(EXTEND)
} else {
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
AstNode* newp = lhsp;
if (nodep->isQuad()) {
if (lhsp->isQuad()) {
lhsp->dtypeFrom(nodep); // Just mark it, else nop
} else if (lhsp->isWide()) {
nodep->v3fatalSrc("extending larger thing into smaller?");
} else {
UINFO(8," EXTEND(q<-l) "<<nodep<<endl);
newp = new AstCCast(nodep->fileline(), lhsp, nodep);
}
} else { // Long
UASSERT_OBJ(!(lhsp->isQuad() || lhsp->isWide()), nodep,
"extending larger thing into smaller?");
lhsp->dtypeFrom(nodep); // Just mark it, else nop
}
replaceWithDelete(nodep, newp); VL_DANGLING(nodep);
}
}
bool expandWide(AstNodeAssign* nodep, AstExtend* rhsp) {
UINFO(8," Wordize ASSIGN(EXTEND) "<<nodep<<endl);
int w = 0;
for (w=0; w<rhsp->lhsp()->widthWords(); w++) {
addWordAssign(nodep, w, newAstWordSelClone(rhsp->lhsp(), w));
}
for (; w<nodep->widthWords(); w++) {
addWordAssign(nodep, w, new AstConst(rhsp->fileline(), AstConst::SizedEData(), 0));
}
return true;
}
virtual void visit(AstSel* nodep) {
if (nodep->user1SetOnce()) return; // Process once
iterateChildren(nodep);
// Remember, Sel's may have non-integer rhs, so need to optimize for that!
UASSERT_OBJ(nodep->widthMin() == nodep->widthConst(), nodep, "Width mismatch");
if (VN_IS(nodep->backp(), NodeAssign)
&& nodep==VN_CAST(nodep->backp(), NodeAssign)->lhsp()) {
// Sel is an LHS assignment select
} else if (nodep->isWide()) {
// See under ASSIGN(WIDE)
}
else if (nodep->fromp()->isWide()) {
UINFO(8," SEL(wide) "<<nodep<<endl);
// Selection amounts
// Check for constant shifts & save some constification work later.
// Grab lowest bit(s)
AstNode* lowwordp = new AstWordSel(nodep->fromp()->fileline(),
nodep->fromp()->cloneTree(true),
newSelBitWord(nodep->lsbp(), 0));
if (nodep->isQuad() && !lowwordp->isQuad()) {
lowwordp = new AstCCast(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, 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 AstCCast(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_EDATASIZE),
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_EDATASIZE),
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_EDATASIZE) {
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 AstCCast(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->dtypeFrom(nodep);
replaceWithDelete(nodep, newp); VL_DANGLING(nodep);
}
else { // Long/Quad from Long/Quad
UINFO(8," SEL->SHIFT "<<nodep<<endl);
AstNode* fromp = nodep->fromp()->unlinkFrBack();
AstNode* lsbp = nodep->lsbp()->unlinkFrBack();
if (nodep->isQuad() && !fromp->isQuad()) {
fromp = new AstCCast(nodep->fileline(), fromp, nodep);
}
AstNode* newp = new AstShiftR(nodep->fileline(),
fromp,
dropCondBound(lsbp),
fromp->width()); // {large}>>32 requires 64-bit shift operation; then cast
newp->dtypeFrom(fromp);
if (!nodep->isQuad() && fromp->isQuad()) {
newp = new AstCCast(newp->fileline(), newp, nodep);
}
newp->dtypeFrom(nodep);
replaceWithDelete(nodep, newp); VL_DANGLING(nodep);
}
}
bool expandWide(AstNodeAssign* nodep, AstSel* rhsp) {
UASSERT_OBJ(nodep->widthMin() == rhsp->widthConst(), nodep, "Width mismatch");
if (VN_IS(rhsp->lsbp(), Const) && VL_BITBIT_E(rhsp->lsbConst()) == 0) {
int lsb = rhsp->lsbConst();
UINFO(8," Wordize ASSIGN(SEL,align) "<<nodep<<endl);
for (int w=0; w<nodep->widthWords(); w++) {
addWordAssign(nodep, w, newAstWordSelClone(rhsp->fromp(), w + VL_BITWORD_E(lsb)));
}
return true;
} else {
UINFO(8," Wordize ASSIGN(EXTRACT,misalign) "<<nodep<<endl);
for (int w=0; w<nodep->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_EDATASIZE);
// Upper bits
V3Number zero (nodep, VL_EDATASIZE, 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_EDATASIZE),
newSelBitBit(rhsp->lsbp()));
AstNode* midmayp = new AstShiftL(rhsp->fileline(),
midwordp,
midshiftp,
VL_EDATASIZE);
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 (VN_IS(lhsp->lsbp(), Const)) {
// 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, destp->widthMin());
for (int bit=lsb; bit<(msb+1); bit++) maskset.setBit(bit, 1);
V3Number maskold (nodep, destp->widthMin()); maskold.opNot(maskset);
if (destwide) {
UINFO(8," ASSIGNSEL(const,wide) "<<nodep<<endl);
for (int w=0; w<destp->widthWords(); w++) {
if (w >= VL_BITWORD_E(lsb) && w <= VL_BITWORD_E(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(),
AstConst::SizedEData(),
maskold.edataWord(w)),
oldvalp);
}
addWordAssign(nodep, w,
destp,
new AstOr(lhsp->fileline(),
oldvalp,
newWordGrabShift(lhsp->fileline(), w,
rhsp, lsb)));
}
}
rhsp->deleteTree(); VL_DANGLING(rhsp);
destp->deleteTree(); VL_DANGLING(destp);
} else {
UINFO(8," ASSIGNSEL(const,narrow) "<<nodep<<endl);
if (destp->isQuad() && !rhsp->isQuad()) {
rhsp = new AstCCast(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) "<<nodep<<endl);
AstNode* rhsp = nodep->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_EDATASIZE)),
oldvalp);
}
// Restrict the shift amount to 0-31, see bug804.
AstNode* shiftp = new AstAnd(nodep->fileline(), lhsp->lsbp()->cloneTree(true),
new AstConst(nodep->fileline(), VL_EDATASIZE - 1));
AstNode* newp = new AstOr(lhsp->fileline(),
oldvalp,
new AstShiftL(lhsp->fileline(),
rhsp,
shiftp,
VL_EDATASIZE));
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 -- "<<nodep<<endl);
// For wide destp, we can either form a equation for every destination word,
// with the appropriate long equation of if it's being written or not.
// Or, we can use a LHS variable arraysel with
// non-constant index to set the vector.
// Doing the variable arraysel is better for globals and large arrays,
// doing every word is better for temporaries and if we're setting most words
// since it may result in better substitution optimizations later.
// This results in so much code, we're better off leaving a function call.
// Reconsider if we get subexpression elimination.
return false;
} else {
UINFO(8," ASSIGNSEL(varlsb,narrow) "<<nodep<<endl);
// nodep->dumpTree(cout, "- old: ");
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
AstNode* destp = lhsp->fromp()->unlinkFrBack();
AstNode* oldvalp = destp->cloneTree(true);
fixCloneLvalue(oldvalp);
V3Number maskwidth (nodep, destp->widthMin());
for (int bit=0; bit < lhsp->widthConst(); bit++) maskwidth.setBit(bit, 1);
if (destp->isQuad() && !rhsp->isQuad()) {
rhsp = new AstCCast(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) {
if (nodep->user1SetOnce()) return; // Process once
iterateChildren(nodep);
if (nodep->isWide()) {
// See under ASSIGN(WIDE)
} else {
UINFO(8," CONCAT "<<nodep<<endl);
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
int rhsshift = rhsp->widthMin();
if (nodep->isQuad() && !lhsp->isQuad()) {
lhsp = new AstCCast(nodep->fileline(), lhsp, nodep);
}
if (nodep->isQuad() && !rhsp->isQuad()) {
rhsp = new AstCCast(nodep->fileline(), rhsp, nodep);
}
AstNode* newp = new AstOr(nodep->fileline(),
new AstShiftL(nodep->fileline(),
lhsp,
new AstConst(nodep->fileline(), rhsshift),
nodep->width()),
rhsp);
newp->dtypeFrom(nodep); // Unsigned
replaceWithDelete(nodep, newp); VL_DANGLING(nodep);
}
}
bool expandWide(AstNodeAssign* nodep, AstConcat* rhsp) {
UINFO(8," Wordize ASSIGN(CONCAT) "<<nodep<<endl);
// Lhs or Rhs may be word, long, or quad.
// newAstWordSelClone nicely abstracts the difference.
int rhsshift = rhsp->rhsp()->widthMin();
// Sometimes doing the words backwards is preferable.
// 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; w<rhsp->widthWords(); 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) {
if (nodep->user1SetOnce()) return; // Process once
iterateChildren(nodep);
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) "<<nodep<<endl);
newp = new AstNegate(nodep->fileline(), lhsp);
} else {
UINFO(8," REPLICATE "<<nodep<<endl);
const AstConst* constp = VN_CAST(nodep->rhsp(), Const);
UASSERT_OBJ(constp, nodep,
"Replication value isn't a constant. Checked earlier!");
uint32_t times = constp->toUInt();
if (nodep->isQuad() && !lhsp->isQuad()) {
lhsp = new AstCCast(nodep->fileline(), lhsp, nodep);
}
newp = lhsp->cloneTree(true);
for (unsigned repnum=1; repnum<times; repnum++) {
int rhsshift = repnum*lhswidth;
newp = new AstOr(nodep->fileline(),
new AstShiftL(nodep->fileline(),
lhsp->cloneTree(true),
new AstConst(nodep->fileline(), rhsshift),
nodep->width()),
newp);
newp->dtypeFrom(nodep); // Unsigned
}
lhsp->deleteTree(); // Never used
}
newp->dtypeFrom(nodep); // Unsigned
replaceWithDelete(nodep, newp); VL_DANGLING(nodep);
}
}
bool expandWide(AstNodeAssign* nodep, AstReplicate* rhsp) {
UINFO(8," Wordize ASSIGN(REPLICATE) "<<nodep<<endl);
AstNode* lhsp = rhsp->lhsp();
int lhswidth = lhsp->widthMin();
const AstConst* constp = VN_CAST(rhsp->rhsp(), Const);
UASSERT_OBJ(constp, rhsp, "Replication value isn't a constant. Checked earlier!");
uint32_t times = constp->toUInt();
for (int w=0; w<rhsp->widthWords(); w++) {
AstNode* newp;
if (lhswidth==1) {
newp = new AstNegate(nodep->fileline(), lhsp->cloneTree(true));
newp->dtypeSetLogicSized(VL_EDATASIZE,
AstNumeric::UNSIGNED); // Replicate always unsigned
} else {
newp = newAstWordSelClone(lhsp, w);
for (unsigned repnum=1; repnum<times; repnum++) {
newp = new AstOr(nodep->fileline(),
newWordGrabShift(rhsp->fileline(), w, lhsp,
lhswidth*repnum),
newp);
}
}
addWordAssign(nodep, w, newp);
}
return true;
}
virtual void visit(AstChangeXor* nodep) {
if (nodep->user1SetOnce()) return; // Process once
iterateChildren(nodep);
UINFO(8," Wordize ChangeXor "<<nodep<<endl);
// -> (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}}
AstNode* newp = NULL;
for (int w=0; w<nodep->lhsp()->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); VL_DANGLING(nodep);
}
void visitEqNeq(AstNodeBiop* nodep) {
if (nodep->user1SetOnce()) return; // Process once
iterateChildren(nodep);
if (nodep->lhsp()->isWide()) {
UINFO(8," Wordize EQ/NEQ "<<nodep<<endl);
// -> (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}}
AstNode* newp = NULL;
for (int w=0; w<nodep->lhsp()->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 (VN_IS(nodep, Neq)) {
newp = new AstNeq(nodep->fileline(),
new AstConst(nodep->fileline(), AstConst::SizedEData(), 0),
newp);
} else {
newp = new AstEq(nodep->fileline(),
new AstConst(nodep->fileline(), AstConst::SizedEData(), 0),
newp);
}
replaceWithDelete(nodep, newp); VL_DANGLING(nodep);
}
}
virtual void visit(AstEq* nodep) { visitEqNeq(nodep); }
virtual void visit(AstNeq* nodep) { visitEqNeq(nodep); }
virtual void visit(AstRedOr* nodep) {
if (nodep->user1SetOnce()) return; // Process once
iterateChildren(nodep);
if (nodep->lhsp()->isWide()) {
UINFO(8," Wordize REDOR "<<nodep<<endl);
// -> (0!={or{for each_word{WORDSEL(lhs,#)}}}
AstNode* newp = NULL;
for (int w=0; w<nodep->lhsp()->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(), AstConst::SizedEData(), 0),
newp);
replaceWithDelete(nodep, newp); VL_DANGLING(nodep);
} else {
UINFO(8," REDOR->EQ "<<nodep<<endl);
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
AstNode* newp = new AstNeq(nodep->fileline(),
new AstConst(nodep->fileline(), AstConst::WidthedValue(),
longOrQuadWidth(nodep), 0),
lhsp);
replaceWithDelete(nodep, newp); VL_DANGLING(nodep);
}
}
virtual void visit(AstRedAnd* nodep) {
if (nodep->user1SetOnce()) return; // Process once
iterateChildren(nodep);
if (nodep->lhsp()->isWide()) {
UINFO(8," Wordize REDAND "<<nodep<<endl);
// -> (0!={and{for each_word{WORDSEL(lhs,#)}}}
AstNode* newp = NULL;
for (int w=0; w<nodep->lhsp()->widthWords(); w++) {
AstNode* eqp = newAstWordSelClone(nodep->lhsp(), w);
if (w==nodep->lhsp()->widthWords()-1) {
// Rather than 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())),
// Bug in cppcheck
// cppcheck-suppress memleak
eqp);
}
newp = (newp==NULL) ? eqp : (new AstAnd(nodep->fileline(), newp, eqp));
}
newp = new AstEq(nodep->fileline(),
new AstConst(nodep->fileline(), AstConst::SizedEData(),
~VL_MASK_E(0)), newp);
replaceWithDelete(nodep, newp); VL_DANGLING(nodep);
} else {
UINFO(8," REDAND->EQ "<<nodep<<endl);
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
AstNode* newp = new AstEq(nodep->fileline(),
new AstConst(nodep->fileline(), wordMask(lhsp)),
lhsp);
replaceWithDelete(nodep, newp); VL_DANGLING(nodep);
}
}
virtual void visit(AstRedXor* nodep) {
if (nodep->user1SetOnce()) return; // Process once
iterateChildren(nodep);
if (nodep->lhsp()->isWide()) {
UINFO(8," Wordize REDXOR "<<nodep<<endl);
// -> (0!={redxor{for each_word{XOR(WORDSEL(lhs,#))}}}
AstNode* newp = NULL;
for (int w=0; w<nodep->lhsp()->widthWords(); w++) {
AstNode* eqp = newAstWordSelClone(nodep->lhsp(), w);
newp = (newp==NULL) ? eqp : (new AstXor(nodep->fileline(), newp, eqp));
}
newp = new AstRedXor(nodep->fileline(), newp);
UINFO(8," Wordize REDXORnew "<<newp<<endl);
replaceWithDelete(nodep, newp); VL_DANGLING(nodep);
}
// 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) {
if (nodep->user1SetOnce()) return; // Process once
if (!nodep->isStatement()) {
iterateChildren(nodep);
return;
}
m_stmtp = nodep;
iterateChildren(nodep);
m_stmtp = NULL;
}
virtual void visit(AstNodeAssign* nodep) {
if (nodep->user1SetOnce()) return; // Process once
m_stmtp = nodep;
iterateChildren(nodep);
bool did = false;
if (nodep->isWide() && ((VN_IS(nodep->lhsp(), VarRef)
|| VN_IS(nodep->lhsp(), ArraySel)))
&& !AstVar::scVarRecurse(nodep->lhsp()) // Need special function for SC
&& !AstVar::scVarRecurse(nodep->rhsp())) {
if (AstConst* rhsp = VN_CAST(nodep->rhsp(), Const)) {
did = expandWide(nodep, rhsp);
} else if (AstVarRef* rhsp = VN_CAST(nodep->rhsp(), VarRef)) {
did = expandWide(nodep, rhsp);
} else if (AstSel* rhsp = VN_CAST(nodep->rhsp(), Sel)) {
did = expandWide(nodep, rhsp);
} else if (AstArraySel* rhsp = VN_CAST(nodep->rhsp(), ArraySel)) {
did = expandWide(nodep, rhsp);
} else if (AstConcat* rhsp = VN_CAST(nodep->rhsp(), Concat)) {
did = expandWide(nodep, rhsp);
} else if (AstReplicate* rhsp = VN_CAST(nodep->rhsp(), Replicate)) {
did = expandWide(nodep, rhsp);
} else if (AstAnd* rhsp = VN_CAST(nodep->rhsp(), And)) {
did = expandWide(nodep, rhsp);
} else if (AstOr* rhsp = VN_CAST(nodep->rhsp(), Or)) {
did = expandWide(nodep, rhsp);
} else if (AstNot* rhsp = VN_CAST(nodep->rhsp(), Not)) {
did = expandWide(nodep, rhsp);
} else if (AstXor* rhsp = VN_CAST(nodep->rhsp(), Xor)) {
did = expandWide(nodep, rhsp);
} else if (AstXnor* rhsp = VN_CAST(nodep->rhsp(), Xnor)) {
did = expandWide(nodep, rhsp);
} else if (AstNodeCond* rhsp = VN_CAST(nodep->rhsp(), NodeCond)) {
did = expandWide(nodep, rhsp);
}
} else if (AstSel* lhsp = VN_CAST(nodep->lhsp(), Sel)) {
did = expandLhs(nodep, lhsp);
}
// Cleanup common code
if (did) {
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
}
m_stmtp = NULL;
}
//--------------------
// Default: Just iterate
virtual void visit(AstVar*) {} // Don't hit varrefs under vars
virtual void visit(AstNode* nodep) {
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
explicit ExpandVisitor(AstNetlist* nodep) {
m_stmtp = NULL;
iterate(nodep);
}
virtual ~ExpandVisitor() {}
};
//----------------------------------------------------------------------
// Top loop
//######################################################################
// Expand class functions
void V3Expand::expandAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
{
ExpandVisitor visitor (nodep);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("expand", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}