verilator/src/V3Const.cpp

2683 lines
138 KiB
C++

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Constant folding
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2021 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.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
// CONST TRANSFORMATIONS:
// Call on one node for PARAM values, or netlist for overall constant folding:
// Bottom up traversal:
// Attempt to convert operands to constants
// If operands are constant, replace this node with constant.
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3String.h"
#include "V3Const.h"
#include "V3Ast.h"
#include "V3Width.h"
#include "V3Simulate.h"
#include <algorithm>
#include <map>
//######################################################################
// Utilities
class ConstVarMarkVisitor final : public AstNVisitor {
// NODE STATE
// AstVar::user4p -> bool, Var marked, 0=not set yet
private:
// VISITORS
virtual void visit(AstVarRef* nodep) override {
if (nodep->varp()) nodep->varp()->user4(1);
}
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit ConstVarMarkVisitor(AstNode* nodep) {
AstNode::user4ClearTree(); // Check marked InUse before we're called
iterate(nodep);
}
virtual ~ConstVarMarkVisitor() override = default;
};
class ConstVarFindVisitor final : public AstNVisitor {
// NODE STATE
// AstVar::user4p -> bool, input from ConstVarMarkVisitor
// MEMBERS
bool m_found = false;
private:
// VISITORS
virtual void visit(AstVarRef* nodep) override {
if (nodep->varp() && nodep->varp()->user4()) m_found = true;
}
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit ConstVarFindVisitor(AstNode* nodep) { iterateAndNextNull(nodep); }
virtual ~ConstVarFindVisitor() override = default;
// METHODS
bool found() const { return m_found; }
};
//######################################################################
// Const state, as a visitor of each AstNode
class ConstVisitor final : public AstNVisitor {
private:
// NODE STATE
// ** only when m_warn/m_doExpensive is set. If state is needed other times,
// ** must track down everywhere V3Const is called and make sure no overlaps.
// AstVar::user4p -> Used by ConstVarMarkVisitor/ConstVarFindVisitor
// AstJumpLabel::user4 -> bool. Set when AstJumpGo uses this label
// AstEnum::user4 -> bool. Recursing.
// STATE
bool m_params = false; // If true, propagate parameterized and true numbers only
bool m_required = false; // If true, must become a constant
bool m_wremove = true; // Inside scope, no assignw removal
bool m_warn = false; // Output warnings
bool m_doExpensive = false; // Enable computationally expensive optimizations
bool m_doCpp = false; // Enable late-stage C++ optimizations
bool m_doNConst = false; // Enable non-constant-child simplifications
bool m_doShort = true; // Remove expressions that short circuit
bool m_doV = false; // Verilog, not C++ conversion
bool m_doGenerate = false; // Postpone width checking inside generate
bool m_hasJumpDelay = false; // JumpGo or Delay under this while
AstNodeModule* m_modp = nullptr; // Current module
AstArraySel* m_selp = nullptr; // Current select
AstNode* m_scopep = nullptr; // Current scope
AstAttrOf* m_attrp = nullptr; // Current attribute
// METHODS
VL_DEBUG_FUNC; // Declare debug()
bool operandConst(AstNode* nodep) { return VN_IS(nodep, Const); }
bool operandAsvConst(const AstNode* nodep) {
// BIASV(CONST, BIASV(CONST,...)) -> BIASV( BIASV_CONSTED(a,b), ...)
const AstNodeBiComAsv* bnodep = VN_CAST_CONST(nodep, NodeBiComAsv);
if (!bnodep) return false;
if (!VN_IS(bnodep->lhsp(), Const)) return false;
const AstNodeBiComAsv* rnodep = VN_CAST_CONST(bnodep->rhsp(), NodeBiComAsv);
if (!rnodep) return false;
if (rnodep->type() != bnodep->type()) return false;
if (rnodep->width() != bnodep->width()) return false;
if (rnodep->lhsp()->width() != bnodep->lhsp()->width()) return false;
if (!VN_IS(rnodep->lhsp(), Const)) return false;
return true;
}
bool operandAsvSame(const AstNode* nodep) {
// BIASV(SAMEa, BIASV(SAMEb,...)) -> BIASV( BIASV(SAMEa,SAMEb), ...)
const AstNodeBiComAsv* bnodep = VN_CAST_CONST(nodep, NodeBiComAsv);
if (!bnodep) return false;
const AstNodeBiComAsv* rnodep = VN_CAST_CONST(bnodep->rhsp(), NodeBiComAsv);
if (!rnodep) return false;
if (rnodep->type() != bnodep->type()) return false;
if (rnodep->width() != bnodep->width()) return false;
return operandsSame(bnodep->lhsp(), rnodep->lhsp());
}
bool operandAsvLUp(const AstNode* nodep) {
// BIASV(BIASV(CONSTll,lr),r) -> BIASV(CONSTll,BIASV(lr,r)) ?
//
// Example of how this is useful:
// BIASV(BIASV(CONSTa,b...),BIASV(CONSTc,d...)) // hits operandAsvUp
// BIASV(CONSTa,BIASV(b...,BIASV(CONSTc,d...))) // hits operandAsvUp
// BIASV(CONSTa,BIASV(CONSTc,BIASV(c...,d...))) // hits operandAsvConst
// BIASV(BIASV(CONSTa,CONSTc),BIASV(c...,d...))) // hits normal constant propagation
// BIASV(CONST_a_c,BIASV(c...,d...)))
//
// Idea for the future: All BiComAsvs could be lists, sorted by if they're constant
const AstNodeBiComAsv* bnodep = VN_CAST_CONST(nodep, NodeBiComAsv);
if (!bnodep) return false;
const AstNodeBiComAsv* lnodep = VN_CAST_CONST(bnodep->lhsp(), NodeBiComAsv);
if (!lnodep) return false;
if (lnodep->type() != bnodep->type()) return false;
if (lnodep->width() != bnodep->width()) return false;
return VN_IS(lnodep->lhsp(), Const);
}
bool operandAsvRUp(const AstNode* nodep) {
// BIASV(l,BIASV(CONSTrl,rr)) -> BIASV(CONSTrl,BIASV(l,rr)) ?
const AstNodeBiComAsv* bnodep = VN_CAST_CONST(nodep, NodeBiComAsv);
if (!bnodep) return false;
const AstNodeBiComAsv* rnodep = VN_CAST_CONST(bnodep->rhsp(), NodeBiComAsv);
if (!rnodep) return false;
if (rnodep->type() != bnodep->type()) return false;
if (rnodep->width() != bnodep->width()) return false;
return VN_IS(rnodep->lhsp(), Const);
}
static bool operandSubAdd(const AstNode* nodep) {
// SUB( ADD(CONSTx,y), CONSTz) -> ADD(SUB(CONSTx,CONSTz), y)
const AstNodeBiop* np = VN_CAST_CONST(nodep, NodeBiop);
const AstNodeBiop* lp = VN_CAST_CONST(np->lhsp(), NodeBiop);
return (lp && VN_IS(lp->lhsp(), Const) && VN_IS(np->rhsp(), Const)
&& lp->width() == np->width());
}
static bool operandAndOrSame(const AstNode* nodep) {
// OR( AND(VAL,x), AND(VAL,y)) -> AND(VAL,OR(x,y))
// OR( AND(x,VAL), AND(y,VAL)) -> AND(OR(x,y),VAL)
const AstNodeBiop* np = VN_CAST_CONST(nodep, NodeBiop);
const AstNodeBiop* lp = VN_CAST_CONST(np->lhsp(), NodeBiop);
const AstNodeBiop* rp = VN_CAST_CONST(np->rhsp(), NodeBiop);
return (lp && rp && lp->width() == rp->width() && lp->type() == rp->type()
&& (operandsSame(lp->lhsp(), rp->lhsp()) || operandsSame(lp->rhsp(), rp->rhsp())));
}
static bool matchOrAndNot(AstNodeBiop* nodep) {
// AstOr{$a, AstAnd{AstNot{$b}, $c}} if $a.width1, $a==$b => AstOr{$a,$c}
// Someday we'll sort the biops completely and this can be simplified
// This often results from our simplified clock generation:
// if (rst) ... else if (enable)... -> OR(rst,AND(!rst,enable))
AstNode* ap;
AstNodeBiop* andp;
if (VN_IS(nodep->lhsp(), And)) {
andp = VN_CAST(nodep->lhsp(), And);
ap = nodep->rhsp();
} else if (VN_IS(nodep->rhsp(), And)) {
andp = VN_CAST(nodep->rhsp(), And);
ap = nodep->lhsp();
} else
return false;
AstNodeUniop* notp;
AstNode* cp;
if (VN_IS(andp->lhsp(), Not)) {
notp = VN_CAST(andp->lhsp(), Not);
cp = andp->rhsp();
} else if (VN_IS(andp->rhsp(), Not)) {
notp = VN_CAST(andp->rhsp(), Not);
cp = andp->lhsp();
} else
return false;
AstNode* bp = notp->lhsp();
if (!operandsSame(ap, bp)) return false;
// Do it
cp->unlinkFrBack();
VL_DO_DANGLING(andp->unlinkFrBack()->deleteTree(), andp);
VL_DANGLING(notp);
// Replace whichever branch is now dangling
if (nodep->rhsp()) {
nodep->lhsp(cp);
} else {
nodep->rhsp(cp);
}
return true;
}
bool matchAndCond(AstAnd* nodep) {
// Push down a AND into conditional, when one side of conditional is constant
// (otherwise we'd be trading one operation for two operations)
// V3Clean often makes this pattern, as it postpones the AND until
// as high as possible, which is usally the right choice, except for this.
AstNodeCond* condp = VN_CAST(nodep->rhsp(), NodeCond);
if (!condp) return false;
if (!VN_IS(condp->expr1p(), Const) && !VN_IS(condp->expr2p(), Const)) return false;
AstConst* maskp = VN_CAST(nodep->lhsp(), Const);
if (!maskp) return false;
UINFO(4, "AND(CONSTm, CONDcond(c, i, e))->CONDcond(c, AND(m,i), AND(m, e)) " << nodep
<< endl);
AstNodeCond* newp = static_cast<AstNodeCond*>(
condp->cloneType(condp->condp()->unlinkFrBack(),
new AstAnd(nodep->fileline(), maskp->cloneTree(false),
condp->expr1p()->unlinkFrBack()),
new AstAnd(nodep->fileline(), maskp->cloneTree(false),
condp->expr2p()->unlinkFrBack())));
newp->dtypeFrom(nodep);
newp->expr1p()->dtypeFrom(nodep); // As And might have been to change widths
newp->expr2p()->dtypeFrom(nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return true;
}
static bool operandShiftSame(const AstNode* nodep) {
const AstNodeBiop* np = VN_CAST_CONST(nodep, NodeBiop);
{
const AstShiftL* lp = VN_CAST_CONST(np->lhsp(), ShiftL);
const AstShiftL* rp = VN_CAST_CONST(np->rhsp(), ShiftL);
if (lp && rp) {
return (lp->width() == rp->width() && lp->lhsp()->width() == rp->lhsp()->width()
&& operandsSame(lp->rhsp(), rp->rhsp()));
}
}
{
const AstShiftR* lp = VN_CAST_CONST(np->lhsp(), ShiftR);
const AstShiftR* rp = VN_CAST_CONST(np->rhsp(), ShiftR);
if (lp && rp) {
return (lp->width() == rp->width() && lp->lhsp()->width() == rp->lhsp()->width()
&& operandsSame(lp->rhsp(), rp->rhsp()));
}
}
return false;
}
bool operandHugeShiftL(const AstNodeBiop* nodep) {
return (VN_IS(nodep->rhsp(), Const)
&& !VN_CAST_CONST(nodep->rhsp(), Const)->num().isFourState()
&& (VN_CAST_CONST(nodep->rhsp(), Const)->toUInt()
>= static_cast<uint32_t>(nodep->width()))
&& isTPure(nodep->lhsp()));
}
bool operandHugeShiftR(const AstNodeBiop* nodep) {
return (VN_IS(nodep->rhsp(), Const)
&& !VN_CAST_CONST(nodep->rhsp(), Const)->num().isFourState()
&& (VN_CAST_CONST(nodep->rhsp(), Const)->toUInt()
>= static_cast<uint32_t>(nodep->lhsp()->width()))
&& isTPure(nodep->lhsp()));
}
bool operandIsTwo(const AstNode* nodep) {
return (VN_IS(nodep, Const) && !VN_CAST_CONST(nodep, Const)->num().isFourState()
&& nodep->width() <= VL_QUADSIZE && VN_CAST_CONST(nodep, Const)->toUQuad() == 2);
}
bool operandIsTwostate(const AstNode* nodep) {
return (VN_IS(nodep, Const) && !VN_CAST_CONST(nodep, Const)->num().isFourState());
}
bool operandIsPowTwo(const AstNode* nodep) {
if (!operandIsTwostate(nodep)) return false;
return (1 == VN_CAST_CONST(nodep, Const)->num().countOnes());
}
bool operandShiftOp(const AstNodeBiop* nodep) {
if (!VN_IS(nodep->rhsp(), Const)) return false;
const AstNodeBiop* lhsp = VN_CAST(nodep->lhsp(), NodeBiop);
if (!lhsp || !(VN_IS(lhsp, And) || VN_IS(lhsp, Or) || VN_IS(lhsp, Xor))) return false;
if (nodep->width() != lhsp->width()) return false;
if (nodep->width() != lhsp->lhsp()->width()) return false;
if (nodep->width() != lhsp->rhsp()->width()) return false;
return true;
}
bool operandShiftShift(const AstNodeBiop* nodep) {
// We could add a AND though.
const AstNodeBiop* lhsp = VN_CAST(nodep->lhsp(), NodeBiop);
if (!lhsp || !(VN_IS(lhsp, ShiftL) || VN_IS(lhsp, ShiftR))) return false;
// We can only get rid of a<<b>>c or a<<b<<c, with constant b & c
// because bits may be masked in that process, or (b+c) may exceed the word width.
if (!(VN_IS(nodep->rhsp(), Const) && VN_IS(lhsp->rhsp(), Const))) return false;
if (VN_CAST_CONST(nodep->rhsp(), Const)->num().isFourState()
|| VN_CAST_CONST(lhsp->rhsp(), Const)->num().isFourState())
return false;
if (nodep->width() != lhsp->width()) return false;
if (nodep->width() != lhsp->lhsp()->width()) return false;
return true;
}
bool operandWordOOB(const AstWordSel* nodep) {
// V3Expand may make a arraysel that exceeds the bounds of the array
// It was an expression, then got constified. In reality, the WordSel
// must be wrapped in a Cond, that will be false.
return (VN_IS(nodep->rhsp(), Const) && VN_IS(nodep->fromp(), NodeVarRef)
&& VN_CAST_CONST(nodep->fromp(), NodeVarRef)->access().isReadOnly()
&& (static_cast<int>(VN_CAST_CONST(nodep->rhsp(), Const)->toUInt())
>= VN_CAST(nodep->fromp(), NodeVarRef)->varp()->widthWords()));
}
bool operandSelFull(const AstSel* nodep) {
return (VN_IS(nodep->lsbp(), Const) && VN_IS(nodep->widthp(), Const)
&& nodep->lsbConst() == 0
&& static_cast<int>(nodep->widthConst()) == nodep->fromp()->width());
}
bool operandSelExtend(AstSel* nodep) {
// A pattern created by []'s after offsets have been removed
// SEL(EXTEND(any,width,...),(width-1),0) -> ...
// Since select's return unsigned, this is always an extend
AstExtend* extendp = VN_CAST(nodep->fromp(), Extend);
if (!(m_doV && extendp && VN_IS(nodep->lsbp(), Const) && VN_IS(nodep->widthp(), Const)
&& nodep->lsbConst() == 0
&& static_cast<int>(nodep->widthConst()) == extendp->lhsp()->width()))
return false;
VL_DO_DANGLING(replaceWChild(nodep, extendp->lhsp()), nodep);
return true;
}
bool operandSelBiLower(AstSel* nodep) {
// SEL(ADD(a,b),(width-1),0) -> ADD(SEL(a),SEL(b))
// Add or any operation which doesn't care if we discard top bits
AstNodeBiop* bip = VN_CAST(nodep->fromp(), NodeBiop);
if (!(m_doV && bip && VN_IS(nodep->lsbp(), Const) && VN_IS(nodep->widthp(), Const)
&& nodep->lsbConst() == 0))
return false;
if (debug() >= 9) nodep->dumpTree(cout, "SEL(BI)-in:");
AstNode* bilhsp = bip->lhsp()->unlinkFrBack();
AstNode* birhsp = bip->rhsp()->unlinkFrBack();
bip->lhsp(new AstSel(nodep->fileline(), bilhsp, 0, nodep->widthConst()));
bip->rhsp(new AstSel(nodep->fileline(), birhsp, 0, nodep->widthConst()));
if (debug() >= 9) bip->dumpTree(cout, "SEL(BI)-ou:");
VL_DO_DANGLING(replaceWChild(nodep, bip), nodep);
return true;
}
bool operandSelShiftLower(AstSel* nodep) {
// AND({a}, SHIFTR({b}, {c})) is often shorthand in C for Verilog {b}[{c} :+ {a}]
// becomes thought other optimizations
// SEL(SHIFTR({a},{b}),{lsb},{width}) -> SEL({a},{lsb+b},{width})
AstShiftR* shiftp = VN_CAST(nodep->fromp(), ShiftR);
if (!(m_doV && shiftp && VN_IS(shiftp->rhsp(), Const) && VN_IS(nodep->lsbp(), Const)
&& VN_IS(nodep->widthp(), Const))) {
return false;
}
AstNode* ap = shiftp->lhsp();
AstConst* bp = VN_CAST(shiftp->rhsp(), Const);
AstConst* lp = VN_CAST(nodep->lsbp(), Const);
if (bp->isWide() || bp->num().isFourState() || bp->num().isNegative() || lp->isWide()
|| lp->num().isFourState() || lp->num().isNegative()) {
return false;
}
int newLsb = lp->toSInt() + bp->toSInt();
if (newLsb + nodep->widthConst() > ap->width()) return false;
//
UINFO(9, "SEL(SHIFTR(a,b),l,w) -> SEL(a,l+b,w)\n");
if (debug() >= 9) nodep->dumpTree(cout, "SEL(SH)-in:");
AstSel* newp
= new AstSel(nodep->fileline(), ap->unlinkFrBack(), newLsb, nodep->widthConst());
newp->dtypeFrom(nodep);
if (debug() >= 9) newp->dumpTree(cout, "SEL(SH)-ou:");
VL_DO_DANGLING(nodep->replaceWith(newp), nodep);
return true;
}
bool operandBiExtendConstShrink(AstNodeBiop* nodep) {
// Loop unrolling favors standalone compares
// EQ(const{width32}, EXTEND(xx{width3})) -> EQ(const{3}, xx{3})
// The constant must have zero bits (+ 1 if signed) or compare
// would be incorrect. See also operandBiExtendConst
AstExtend* extendp = VN_CAST(nodep->rhsp(), Extend);
if (!extendp) return false;
AstNode* smallerp = extendp->lhsp();
int subsize = smallerp->width();
AstConst* constp = VN_CAST(nodep->lhsp(), Const);
if (!constp) return false;
if (!constp->num().isBitsZero(constp->width() - 1, subsize)) return false;
//
if (debug() >= 9) nodep->dumpTree(cout, "BI(EXTEND)-in:");
smallerp->unlinkFrBack();
VL_DO_DANGLING(extendp->unlinkFrBack()->deleteTree(), extendp); // aka nodep->lhsp.
nodep->rhsp(smallerp);
constp->unlinkFrBack();
V3Number num(constp, subsize);
num.opAssign(constp->num());
nodep->lhsp(new AstConst(constp->fileline(), num));
VL_DO_DANGLING(constp->deleteTree(), constp);
if (debug() >= 9) nodep->dumpTree(cout, "BI(EXTEND)-ou:");
return true;
}
bool operandBiExtendConstOver(const AstNodeBiop* nodep) {
// EQ(const{width32}, EXTEND(xx{width3})) -> constant
// When the constant has non-zero bits above the extend it's a constant.
// Avoids compiler warning
const AstExtend* extendp = VN_CAST_CONST(nodep->rhsp(), Extend);
if (!extendp) return false;
AstNode* smallerp = extendp->lhsp();
int subsize = smallerp->width();
const AstConst* constp = VN_CAST_CONST(nodep->lhsp(), Const);
if (!constp) return false;
if (constp->num().isBitsZero(constp->width() - 1, subsize)) return false;
return true;
}
AstNode* afterComment(AstNode* nodep) {
// Ignore comments, such as to determine if a AstIf is empty.
// nodep may be null, if so return null.
while (nodep && VN_IS(nodep, Comment)) { nodep = nodep->nextp(); }
return nodep;
}
bool isTPure(AstNode* nodep) {
// Pure checks - if this node and all nodes under it are free of
// side effects can do this optimization
// Eventually we'll recurse through tree when unknown, memoizing results so far,
// but for now can disable en-mass until V3Purify takes effect.
return m_doShort || VN_IS(nodep, VarRef) || VN_IS(nodep, Const);
}
// Extraction checks
bool warnSelect(AstSel* nodep) {
if (m_doGenerate) {
// Never checked yet
V3Width::widthParamsEdit(nodep);
iterateChildren(nodep); // May need "constifying"
}
// Find range of dtype we are selecting from
// Similar code in V3Unknown::AstSel
bool doit = true;
if (m_warn && VN_IS(nodep->lsbp(), Const) && VN_IS(nodep->widthp(), Const) && doit) {
int maxDeclBit = nodep->declRange().hiMaxSelect() * nodep->declElWidth()
+ (nodep->declElWidth() - 1);
if (VN_CAST(nodep->lsbp(), Const)->num().isFourState()
|| VN_CAST(nodep->widthp(), Const)->num().isFourState()) {
nodep->v3error("Selection index is constantly unknown or tristated: "
"lsb="
<< nodep->lsbp()->name() << " width=" << nodep->widthp()->name());
// Replacing nodep will make a mess above, so we replace the offender
replaceZero(nodep->lsbp());
} else if (nodep->declRange().ranged()
&& (nodep->msbConst() > maxDeclBit || nodep->lsbConst() > maxDeclBit)) {
// See also warning in V3Width
// Must adjust by element width as declRange() is in number of elements
nodep->v3warn(SELRANGE,
"Selection index out of range: "
<< (nodep->msbConst() / nodep->declElWidth()) << ":"
<< (nodep->lsbConst() / nodep->declElWidth()) << " outside "
<< nodep->declRange().hiMaxSelect() << ":0"
<< (nodep->declRange().lo() >= 0
? ""
: (" (adjusted +" + cvtToStr(-nodep->declRange().lo())
+ " to account for negative lsb)")));
UINFO(1, " Related Raw index is " << nodep->msbConst() << ":"
<< nodep->lsbConst() << endl);
// Don't replace with zero, we'll do it later
}
}
return false; // Not a transform, so NOP
}
static bool operandsSame(AstNode* node1p, AstNode* node2p) {
// For now we just detect constants & simple vars, though it could be more generic
if (VN_IS(node1p, Const) && VN_IS(node2p, Const)) {
return node1p->sameGateTree(node2p);
} else if (VN_IS(node1p, VarRef) && VN_IS(node2p, VarRef)) {
// Avoid comparing widthMin's, which results in lost optimization attempts
// If cleanup sameGateTree to be smarter, this can be restored.
// return node1p->sameGateTree(node2p);
return node1p->same(node2p);
} else {
return false;
}
}
bool ifSameAssign(const AstNodeIf* nodep) {
const AstNodeAssign* ifp = VN_CAST_CONST(nodep->ifsp(), NodeAssign);
const AstNodeAssign* elsep = VN_CAST_CONST(nodep->elsesp(), NodeAssign);
if (!ifp || ifp->nextp()) return false; // Must be SINGLE statement
if (!elsep || elsep->nextp()) return false;
if (ifp->type() != elsep->type()) return false; // Can't mix an assigndly and an assign
if (!ifp->lhsp()->sameGateTree(elsep->lhsp())) return false;
if (!ifp->rhsp()->gateTree()) return false;
if (!elsep->rhsp()->gateTree()) return false;
return true;
}
bool operandIfIf(const AstNodeIf* nodep) {
if (nodep->elsesp()) return false;
const AstNodeIf* lowerIfp = VN_CAST_CONST(nodep->ifsp(), NodeIf);
if (!lowerIfp || lowerIfp->nextp()) return false;
if (nodep->type() != lowerIfp->type()) return false;
if (afterComment(lowerIfp->elsesp())) return false;
return true;
}
bool ifConcatMergeableBiop(const AstNode* nodep) {
return (VN_IS(nodep, And) || VN_IS(nodep, Or) || VN_IS(nodep, Xor));
}
bool ifAdjacentSel(const AstSel* lhsp, const AstSel* rhsp) {
if (!v3Global.opt.oAssemble()) return false; // opt disabled
if (!lhsp || !rhsp) return false;
const AstNode* lfromp = lhsp->fromp();
const AstNode* rfromp = rhsp->fromp();
if (!lfromp || !rfromp || !lfromp->sameGateTree(rfromp)) return false;
const AstConst* lstart = VN_CAST_CONST(lhsp->lsbp(), Const);
const AstConst* rstart = VN_CAST_CONST(rhsp->lsbp(), Const);
const AstConst* lwidth = VN_CAST_CONST(lhsp->widthp(), Const);
const AstConst* rwidth = VN_CAST_CONST(rhsp->widthp(), Const);
if (!lstart || !rstart || !lwidth || !rwidth) return false; // too complicated
int rend = (rstart->toSInt() + rwidth->toSInt());
return (rend == lstart->toSInt());
}
bool ifMergeAdjacent(AstNode* lhsp, AstNode* rhsp) {
// called by concatmergeable to determine if {lhsp, rhsp} make sense
if (!v3Global.opt.oAssemble()) return false; // opt disabled
// two same varref
if (operandsSame(lhsp, rhsp)) return true;
AstSel* lselp = VN_CAST(lhsp, Sel);
AstSel* rselp = VN_CAST(rhsp, Sel);
// a[i:0] a
if (lselp && !rselp && rhsp->sameGateTree(lselp->fromp()))
rselp = new AstSel(rhsp->fileline(), rhsp->cloneTree(false), 0, rhsp->width());
// a[i:j] {a[j-1:k], b}
if (lselp && !rselp && VN_IS(rhsp, Concat))
return ifMergeAdjacent(lhsp, VN_CAST(rhsp, Concat)->lhsp());
// a a[msb:j]
if (rselp && !lselp && lhsp->sameGateTree(rselp->fromp()))
lselp = new AstSel(lhsp->fileline(), lhsp->cloneTree(false), 0, lhsp->width());
// {b, a[j:k]} a[k-1:i]
if (rselp && !lselp && VN_IS(lhsp, Concat))
return ifMergeAdjacent(VN_CAST(lhsp, Concat)->rhsp(), rhsp);
if (!lselp || !rselp) return false;
// a[a:b] a[b-1:c] are adjacent
AstNode* lfromp = lselp->fromp();
AstNode* rfromp = rselp->fromp();
if (!lfromp || !rfromp || !lfromp->sameGateTree(rfromp)) return false;
AstConst* lstart = VN_CAST(lselp->lsbp(), Const);
AstConst* rstart = VN_CAST(rselp->lsbp(), Const);
AstConst* lwidth = VN_CAST(lselp->widthp(), Const);
AstConst* rwidth = VN_CAST(rselp->widthp(), Const);
if (!lstart || !rstart || !lwidth || !rwidth) return false; // too complicated
int rend = (rstart->toSInt() + rwidth->toSInt());
// a[i:j] a[j-1:k]
if (rend == lstart->toSInt()) return true;
// a[i:0] a[msb:j]
if (rend == rfromp->width() && lstart->toSInt() == 0) return true;
return false;
}
bool concatMergeable(const AstNode* lhsp, const AstNode* rhsp) {
// determine if {a OP b, c OP d} => {a, c} OP {b, d} is advantageous
if (!v3Global.opt.oAssemble()) return false; // opt disabled
if (lhsp->type() != rhsp->type()) return false;
if (!ifConcatMergeableBiop(lhsp)) return false;
const AstNodeBiop* lp = VN_CAST_CONST(lhsp, NodeBiop);
const AstNodeBiop* rp = VN_CAST_CONST(rhsp, NodeBiop);
if (!lp || !rp) return false;
// {a[]&b[], a[]&b[]}
bool lad = ifMergeAdjacent(lp->lhsp(), rp->lhsp());
bool rad = ifMergeAdjacent(lp->rhsp(), rp->rhsp());
if (lad && rad) return true;
// {a[] & b[]&c[], a[] & b[]&c[]}
if (lad && concatMergeable(lp->rhsp(), rp->rhsp())) return true;
// {a[]&b[] & c[], a[]&b[] & c[]}
if (rad && concatMergeable(lp->lhsp(), rp->lhsp())) return true;
// {(a[]&b[])&(c[]&d[]), (a[]&b[])&(c[]&d[])}
if (concatMergeable(lp->lhsp(), rp->lhsp()) && concatMergeable(lp->rhsp(), rp->rhsp())) {
return true;
}
return false;
}
bool operandsSameSize(AstNode* lhsp, AstNode* rhsp) { return lhsp->width() == rhsp->width(); }
//----------------------------------------
// Constant Replacement functions.
// These all take a node, delete its tree, and replaces it with a constant
void replaceNum(AstNode* oldp, const V3Number& num) {
// Replace oldp node with a constant set to specified value
UASSERT(oldp, "Null old");
UASSERT_OBJ(!(VN_IS(oldp, Const) && !VN_CAST(oldp, Const)->num().isFourState()), oldp,
"Already constant??");
AstNode* newp = new AstConst(oldp->fileline(), num);
newp->dtypeFrom(oldp);
if (debug() > 5) oldp->dumpTree(cout, " const_old: ");
if (debug() > 5) newp->dumpTree(cout, " _new: ");
oldp->replaceWith(newp);
VL_DO_DANGLING(oldp->deleteTree(), oldp);
}
void replaceNum(AstNode* nodep, uint32_t val) {
V3Number num(nodep, nodep->width(), val);
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
}
void replaceNumSigned(AstNodeBiop* nodep, uint32_t val) {
// We allow both sides to be constant, as one may have come from
// parameter propagation, etc.
if (m_warn && !(VN_IS(nodep->lhsp(), Const) && VN_IS(nodep->rhsp(), Const))) {
nodep->v3warn(UNSIGNED, "Comparison is constant due to unsigned arithmetic");
}
VL_DO_DANGLING(replaceNum(nodep, val), nodep);
}
void replaceNumLimited(AstNodeBiop* nodep, uint32_t val) {
// Avoids gcc warning about same
if (m_warn) nodep->v3warn(CMPCONST, "Comparison is constant due to limited range");
VL_DO_DANGLING(replaceNum(nodep, val), nodep);
}
void replaceZero(AstNode* nodep) { VL_DO_DANGLING(replaceNum(nodep, 0), nodep); }
void replaceZeroChkPure(AstNode* nodep, AstNode* checkp) {
// For example, "0 * n" -> 0 if n has no side effects
// Else strength reduce it to 0 & n.
// If ever change the operation note AstAnd rule specially ignores this created pattern
if (isTPure(checkp)) {
VL_DO_DANGLING(replaceNum(nodep, 0), nodep);
} else {
AstNode* newp = new AstAnd(nodep->fileline(), new AstConst(nodep->fileline(), 0),
checkp->unlinkFrBack());
newp->dtypeFrom(nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
}
void replaceAllOnes(AstNode* nodep) {
V3Number ones(nodep, nodep->width(), 0);
ones.setMask(nodep->width());
VL_DO_DANGLING(replaceNum(nodep, ones), nodep);
}
void replaceConst(AstNodeUniop* nodep) {
V3Number num(nodep, nodep->width());
nodep->numberOperate(num, VN_CAST(nodep->lhsp(), Const)->num());
UINFO(4, "UNICONST -> " << num << endl);
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
}
void replaceConst(AstNodeBiop* nodep) {
V3Number num(nodep, nodep->width());
nodep->numberOperate(num, VN_CAST(nodep->lhsp(), Const)->num(),
VN_CAST(nodep->rhsp(), Const)->num());
UINFO(4, "BICONST -> " << num << endl);
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
}
void replaceConst(AstNodeTriop* nodep) {
V3Number num(nodep, nodep->width());
nodep->numberOperate(num, VN_CAST(nodep->lhsp(), Const)->num(),
VN_CAST(nodep->rhsp(), Const)->num(),
VN_CAST(nodep->thsp(), Const)->num());
UINFO(4, "TRICONST -> " << num << endl);
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
}
void replaceConst(AstNodeQuadop* nodep) {
V3Number num(nodep, nodep->width());
nodep->numberOperate(
num, VN_CAST(nodep->lhsp(), Const)->num(), VN_CAST(nodep->rhsp(), Const)->num(),
VN_CAST(nodep->thsp(), Const)->num(), VN_CAST(nodep->fhsp(), Const)->num());
UINFO(4, "QUADCONST -> " << num << endl);
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
}
void replaceConstString(AstNode* oldp, const string& num) {
// Replace oldp node with a constant set to specified value
UASSERT(oldp, "Null old");
AstNode* newp = new AstConst(oldp->fileline(), AstConst::String(), num);
if (debug() > 5) oldp->dumpTree(cout, " const_old: ");
if (debug() > 5) newp->dumpTree(cout, " _new: ");
oldp->replaceWith(newp);
VL_DO_DANGLING(oldp->deleteTree(), oldp);
}
//----------------------------------------
// Replacement functions.
// These all take a node and replace it with something else
void replaceWChild(AstNode* nodep, AstNode* childp) {
// NODE(..., CHILD(...)) -> CHILD(...)
childp->unlinkFrBackWithNext();
// If replacing a SEL for example, the data type comes from the parent (is less wide).
// This may adversely affect the operation of the node being replaced.
childp->dtypeFrom(nodep);
nodep->replaceWith(childp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
//! Replace a ternary node with its RHS after iterating
//! Used with short-circuiting, where the RHS has not yet been iterated.
void replaceWIteratedRhs(AstNodeTriop* nodep) {
if (AstNode* rhsp = nodep->rhsp()) iterateAndNextNull(rhsp);
replaceWChild(nodep, nodep->rhsp()); // May have changed
}
//! Replace a ternary node with its THS after iterating
//! Used with short-circuiting, where the THS has not yet been iterated.
void replaceWIteratedThs(AstNodeTriop* nodep) {
if (AstNode* thsp = nodep->thsp()) iterateAndNextNull(thsp);
replaceWChild(nodep, nodep->thsp()); // May have changed
}
void replaceWLhs(AstNodeUniop* nodep) {
// Keep LHS, remove RHS
replaceWChild(nodep, nodep->lhsp());
}
void replaceWLhs(AstNodeBiop* nodep) {
// Keep LHS, remove RHS
replaceWChild(nodep, nodep->lhsp());
}
void replaceWRhs(AstNodeBiop* nodep) {
// Keep RHS, remove LHS
replaceWChild(nodep, nodep->rhsp());
}
void replaceAsv(AstNodeBiop* nodep) {
// BIASV(CONSTa, BIASV(CONSTb, c)) -> BIASV( BIASV_CONSTED(a,b), c)
// BIASV(SAMEa, BIASV(SAMEb, c)) -> BIASV( BIASV(SAMEa,SAMEb), c)
// nodep->dumpTree(cout, " repAsvConst_old: ");
AstNode* ap = nodep->lhsp();
AstNodeBiop* rp = VN_CAST(nodep->rhsp(), NodeBiop);
AstNode* bp = rp->lhsp();
AstNode* cp = rp->rhsp();
ap->unlinkFrBack();
bp->unlinkFrBack();
cp->unlinkFrBack();
rp->unlinkFrBack();
nodep->lhsp(rp);
nodep->rhsp(cp);
rp->lhsp(ap);
rp->rhsp(bp);
if (VN_IS(rp->lhsp(), Const) && VN_IS(rp->rhsp(), Const)) replaceConst(rp);
// nodep->dumpTree(cout, " repAsvConst_new: ");
}
void replaceAsvLUp(AstNodeBiop* nodep) {
// BIASV(BIASV(CONSTll,lr),r) -> BIASV(CONSTll,BIASV(lr,r))
AstNodeBiop* lp = VN_CAST(nodep->lhsp()->unlinkFrBack(), NodeBiop);
AstNode* llp = lp->lhsp()->unlinkFrBack();
AstNode* lrp = lp->rhsp()->unlinkFrBack();
AstNode* rp = nodep->rhsp()->unlinkFrBack();
nodep->lhsp(llp);
nodep->rhsp(lp);
lp->lhsp(lrp);
lp->rhsp(rp);
// nodep->dumpTree(cout, " repAsvLUp_new: ");
}
void replaceAsvRUp(AstNodeBiop* nodep) {
// BIASV(l,BIASV(CONSTrl,rr)) -> BIASV(CONSTrl,BIASV(l,rr))
AstNode* lp = nodep->lhsp()->unlinkFrBack();
AstNodeBiop* rp = VN_CAST(nodep->rhsp()->unlinkFrBack(), NodeBiop);
AstNode* rlp = rp->lhsp()->unlinkFrBack();
AstNode* rrp = rp->rhsp()->unlinkFrBack();
nodep->lhsp(rlp);
nodep->rhsp(rp);
rp->lhsp(lp);
rp->rhsp(rrp);
// nodep->dumpTree(cout, " repAsvRUp_new: ");
}
void replaceAndOr(AstNodeBiop* nodep) {
// OR (AND (CONSTll,lr), AND(CONSTrl==ll,rr)) -> AND (CONSTll, OR(lr,rr))
// OR (AND (CONSTll,lr), AND(CONSTrl, rr=lr)) -> AND (OR(ll,rl), rr)
// nodep ^lp ^llp ^lrp ^rp ^rlp ^rrp
// (Or/And may also be reversed)
AstNodeBiop* lp = VN_CAST(nodep->lhsp()->unlinkFrBack(), NodeBiop);
AstNode* llp = lp->lhsp()->unlinkFrBack();
AstNode* lrp = lp->rhsp()->unlinkFrBack();
AstNodeBiop* rp = VN_CAST(nodep->rhsp()->unlinkFrBack(), NodeBiop);
AstNode* rlp = rp->lhsp()->unlinkFrBack();
AstNode* rrp = rp->rhsp()->unlinkFrBack();
nodep->replaceWith(lp);
if (operandsSame(llp, rlp)) {
lp->lhsp(llp);
lp->rhsp(nodep);
nodep->lhsp(lrp);
nodep->rhsp(rrp);
VL_DO_DANGLING(rp->deleteTree(), rp);
VL_DO_DANGLING(rlp->deleteTree(), rlp);
} else if (operandsSame(lrp, rrp)) {
lp->lhsp(nodep);
lp->rhsp(rrp);
nodep->lhsp(llp);
nodep->rhsp(rlp);
VL_DO_DANGLING(rp->deleteTree(), rp);
VL_DO_DANGLING(lrp->deleteTree(), lrp);
} else {
nodep->v3fatalSrc("replaceAndOr on something operandAndOrSame shouldn't have matched");
}
// nodep->dumpTree(cout, " repAndOr_new: ");
}
void replaceShiftSame(AstNodeBiop* nodep) {
// Or(Shift(ll,CONSTlr),Shift(rl,CONSTrr==lr)) -> Shift(Or(ll,rl),CONSTlr)
// (Or/And may also be reversed)
AstNodeBiop* lp = VN_CAST(nodep->lhsp()->unlinkFrBack(), NodeBiop);
AstNode* llp = lp->lhsp()->unlinkFrBack();
AstNode* lrp = lp->rhsp()->unlinkFrBack();
AstNodeBiop* rp = VN_CAST(nodep->rhsp()->unlinkFrBack(), NodeBiop);
AstNode* rlp = rp->lhsp()->unlinkFrBack();
AstNode* rrp = rp->rhsp()->unlinkFrBack();
nodep->replaceWith(lp);
lp->lhsp(nodep);
lp->rhsp(lrp);
nodep->lhsp(llp);
nodep->rhsp(rlp);
VL_DO_DANGLING(rp->deleteTree(), rp);
VL_DO_DANGLING(rrp->deleteTree(), rrp);
// nodep->dumpTree(cout, " repShiftSame_new: ");
}
void replaceConcatSel(AstConcat* nodep) {
// {a[1], a[0]} -> a[1:0]
AstSel* lselp = VN_CAST(nodep->lhsp()->unlinkFrBack(), Sel);
AstSel* rselp = VN_CAST(nodep->rhsp()->unlinkFrBack(), Sel);
int lstart = lselp->lsbConst();
int lwidth = lselp->widthConst();
int rstart = rselp->lsbConst();
int rwidth = rselp->widthConst();
UASSERT_OBJ((rstart + rwidth) == lstart, nodep,
"tried to merge two selects which are not adjacent");
AstSel* newselp = new AstSel(lselp->fromp()->fileline(), rselp->fromp()->cloneTree(false),
rstart, lwidth + rwidth);
UINFO(5, "merged two adjacent sel " << lselp << " and " << rselp << " to one " << newselp
<< endl);
nodep->replaceWith(newselp);
VL_DO_DANGLING(lselp->deleteTree(), lselp);
VL_DO_DANGLING(rselp->deleteTree(), rselp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void replaceConcatMerge(AstConcat* nodep) {
AstNodeBiop* lp = VN_CAST(nodep->lhsp(), NodeBiop);
AstNodeBiop* rp = VN_CAST(nodep->rhsp(), NodeBiop);
AstNode* llp = lp->lhsp()->cloneTree(false);
AstNode* lrp = lp->rhsp()->cloneTree(false);
AstNode* rlp = rp->lhsp()->cloneTree(false);
AstNode* rrp = rp->rhsp()->cloneTree(false);
if (concatMergeable(lp, rp)) {
AstConcat* newlp = new AstConcat(rlp->fileline(), llp, rlp);
AstConcat* newrp = new AstConcat(rrp->fileline(), lrp, rrp);
// use the lhs to replace the parent concat
lp->lhsp()->replaceWith(newlp);
lp->rhsp()->replaceWith(newrp);
lp->dtypeChgWidthSigned(newlp->width(), newlp->width(), VSigning::UNSIGNED);
UINFO(5, "merged " << nodep << endl);
VL_DO_DANGLING(rp->unlinkFrBack()->deleteTree(), rp);
nodep->replaceWith(lp->unlinkFrBack());
VL_DO_DANGLING(nodep->deleteTree(), nodep);
iterate(lp->lhsp());
iterate(lp->rhsp());
} else {
nodep->v3fatalSrc("tried to merge two Concat which are not adjacent");
}
}
void replaceExtend(AstNode* nodep, AstNode* arg0p) {
// -> EXTEND(nodep)
// like a AstExtend{$rhsp}, but we need to set the width correctly from base node
arg0p->unlinkFrBack();
AstNode* newp = (VN_IS(nodep, ExtendS)
? static_cast<AstNode*>(new AstExtendS(nodep->fileline(), arg0p))
: static_cast<AstNode*>(new AstExtend(nodep->fileline(), arg0p)));
newp->dtypeFrom(nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void replacePowShift(AstNodeBiop* nodep) { // Pow or PowS
UINFO(5, "POW(2,b)->SHIFTL(1,b) " << nodep << endl);
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
AstShiftL* newp
= new AstShiftL(nodep->fileline(), new AstConst(nodep->fileline(), 1), rhsp);
newp->dtypeFrom(nodep);
newp->lhsp()->dtypeFrom(nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void replaceMulShift(AstMul* nodep) { // Mul, but not MulS as not simple shift
UINFO(5, "MUL(2^n,b)->SHIFTL(b,n) " << nodep << endl);
int amount = VN_CAST(nodep->lhsp(), Const)->num().mostSetBitP1() - 1; // 2^n->n+1
AstNode* opp = nodep->rhsp()->unlinkFrBack();
AstShiftL* newp
= new AstShiftL(nodep->fileline(), opp, new AstConst(nodep->fileline(), amount));
newp->dtypeFrom(nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void replaceDivShift(AstDiv* nodep) { // Mul, but not MulS as not simple shift
UINFO(5, "DIV(b,2^n)->SHIFTR(b,n) " << nodep << endl);
int amount = VN_CAST(nodep->rhsp(), Const)->num().mostSetBitP1() - 1; // 2^n->n+1
AstNode* opp = nodep->lhsp()->unlinkFrBack();
AstShiftR* newp
= new AstShiftR(nodep->fileline(), opp, new AstConst(nodep->fileline(), amount));
newp->dtypeFrom(nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void replaceModAnd(AstModDiv* nodep) { // Mod, but not ModS as not simple shift
UINFO(5, "MOD(b,2^n)->AND(b,2^n-1) " << nodep << endl);
int amount = VN_CAST(nodep->rhsp(), Const)->num().mostSetBitP1() - 1; // 2^n->n+1
V3Number mask(nodep, nodep->width());
mask.setMask(amount);
AstNode* opp = nodep->lhsp()->unlinkFrBack();
AstAnd* newp = new AstAnd(nodep->fileline(), opp, new AstConst(nodep->fileline(), mask));
newp->dtypeFrom(nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void replaceShiftOp(AstNodeBiop* nodep) {
UINFO(5, "SHIFT(AND(a,b),CONST)->AND(SHIFT(a,CONST),SHIFT(b,CONST)) " << nodep << endl);
AstNRelinker handle;
nodep->unlinkFrBack(&handle);
AstNodeBiop* lhsp = VN_CAST(nodep->lhsp(), NodeBiop);
lhsp->unlinkFrBack();
AstNode* shiftp = nodep->rhsp()->unlinkFrBack();
AstNode* ap = lhsp->lhsp()->unlinkFrBack();
AstNode* bp = lhsp->rhsp()->unlinkFrBack();
AstNodeBiop* shift1p = nodep;
AstNodeBiop* shift2p = nodep->cloneTree(true);
shift1p->lhsp(ap);
shift1p->rhsp(shiftp->cloneTree(true));
shift2p->lhsp(bp);
shift2p->rhsp(shiftp);
AstNodeBiop* newp = lhsp;
newp->lhsp(shift1p);
newp->rhsp(shift2p);
handle.relink(newp);
iterate(newp); // Further reduce, either node may have more reductions.
}
void replaceShiftShift(AstNodeBiop* nodep) {
UINFO(4, "SHIFT(SHIFT(a,s1),s2)->SHIFT(a,ADD(s1,s2)) " << nodep << endl);
if (debug() >= 9) nodep->dumpTree(cout, " repShiftShift_old: ");
AstNodeBiop* lhsp = VN_CAST(nodep->lhsp(), NodeBiop);
lhsp->unlinkFrBack();
AstNode* ap = lhsp->lhsp()->unlinkFrBack();
AstNode* shift1p = lhsp->rhsp()->unlinkFrBack();
AstNode* shift2p = nodep->rhsp()->unlinkFrBack();
// Shift1p and shift2p may have different sizes, both are
// self-determined so sum with infinite width
if (nodep->type() == lhsp->type()) {
int shift1 = VN_CAST(shift1p, Const)->toUInt();
int shift2 = VN_CAST(shift2p, Const)->toUInt();
int newshift = shift1 + shift2;
VL_DO_DANGLING(shift1p->deleteTree(), shift1p);
VL_DO_DANGLING(shift2p->deleteTree(), shift2p);
nodep->lhsp(ap);
nodep->rhsp(new AstConst(nodep->fileline(), newshift));
iterate(nodep); // Further reduce, either node may have more reductions.
} else {
// We know shift amounts are constant, but might be a mixed left/right shift
int shift1 = VN_CAST(shift1p, Const)->toUInt();
if (VN_IS(lhsp, ShiftR)) shift1 = -shift1;
int shift2 = VN_CAST(shift2p, Const)->toUInt();
if (VN_IS(nodep, ShiftR)) shift2 = -shift2;
int newshift = shift1 + shift2;
VL_DO_DANGLING(shift1p->deleteTree(), shift1p);
VL_DO_DANGLING(shift2p->deleteTree(), shift2p);
AstNode* newp;
V3Number mask1(nodep, nodep->width());
V3Number ones(nodep, nodep->width());
ones.setMask(nodep->width());
if (shift1 < 0) {
mask1.opShiftR(ones, V3Number(nodep, VL_IDATASIZE, -shift1));
} else {
mask1.opShiftL(ones, V3Number(nodep, VL_IDATASIZE, shift1));
}
V3Number mask(nodep, nodep->width());
if (shift2 < 0) {
mask.opShiftR(mask1, V3Number(nodep, VL_IDATASIZE, -shift2));
} else {
mask.opShiftL(mask1, V3Number(nodep, VL_IDATASIZE, shift2));
}
if (newshift < 0) {
newp = new AstShiftR(nodep->fileline(), ap,
new AstConst(nodep->fileline(), -newshift));
} else {
newp = new AstShiftL(nodep->fileline(), ap,
new AstConst(nodep->fileline(), newshift));
}
newp->dtypeFrom(nodep);
newp = new AstAnd(nodep->fileline(), newp, new AstConst(nodep->fileline(), mask));
newp->dtypeFrom(nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
// newp->dumpTree(cout, " repShiftShift_new: ");
iterate(newp); // Further reduce, either node may have more reductions.
}
VL_DO_DANGLING(lhsp->deleteTree(), lhsp);
}
bool replaceAssignMultiSel(AstNodeAssign* nodep) {
// Multiple assignments to sequential bits can be concated
// ASSIGN(SEL(a),aq), ASSIGN(SEL(a+1),bq) -> ASSIGN(SEL(a:b),CONCAT(aq,bq)
// ie. assign var[2]=a, assign var[3]=b -> assign var[3:2]={b,a}
// Skip if we're not const'ing an entire module (IE doing only one assign, etc)
if (!m_modp) return false;
AstSel* sel1p = VN_CAST(nodep->lhsp(), Sel);
if (!sel1p) return false;
AstNodeAssign* nextp = VN_CAST(nodep->nextp(), NodeAssign);
if (!nextp) return false;
if (nodep->type() != nextp->type()) return false;
AstSel* sel2p = VN_CAST(nextp->lhsp(), Sel);
if (!sel2p) return false;
AstVarRef* varref1p = VN_CAST(sel1p->fromp(), VarRef);
if (!varref1p) return false;
AstVarRef* varref2p = VN_CAST(sel2p->fromp(), VarRef);
if (!varref2p) return false;
if (!varref1p->sameGateTree(varref2p)) return false;
AstConst* con1p = VN_CAST(sel1p->lsbp(), Const);
if (!con1p) return false;
AstConst* con2p = VN_CAST(sel2p->lsbp(), Const);
if (!con2p) return false;
// We need to make sure there's no self-references involved in either
// assignment. For speed, we only look 3 deep, then give up.
if (!varNotReferenced(nodep->rhsp(), varref1p->varp())) return false;
if (!varNotReferenced(nextp->rhsp(), varref2p->varp())) return false;
// Swap?
if ((con1p->toSInt() != con2p->toSInt() + sel2p->width())
&& (con2p->toSInt() != con1p->toSInt() + sel1p->width())) {
return false;
}
bool lsbFirstAssign = (con1p->toUInt() < con2p->toUInt());
UINFO(4, "replaceAssignMultiSel " << nodep << endl);
UINFO(4, " && " << nextp << endl);
// nodep->dumpTree(cout, "comb1: ");
// nextp->dumpTree(cout, "comb2: ");
AstNode* rhs1p = nodep->rhsp()->unlinkFrBack();
AstNode* rhs2p = nextp->rhsp()->unlinkFrBack();
AstNode* newp;
if (lsbFirstAssign) {
newp = nodep->cloneType(new AstSel(sel1p->fileline(), varref1p->unlinkFrBack(),
sel1p->lsbConst(), sel1p->width() + sel2p->width()),
new AstConcat(rhs1p->fileline(), rhs2p, rhs1p));
} else {
newp = nodep->cloneType(new AstSel(sel1p->fileline(), varref1p->unlinkFrBack(),
sel2p->lsbConst(), sel1p->width() + sel2p->width()),
new AstConcat(rhs1p->fileline(), rhs1p, rhs2p));
}
// pnewp->dumpTree(cout, "conew: ");
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
VL_DO_DANGLING(nextp->unlinkFrBack()->deleteTree(), nextp);
return true;
}
bool varNotReferenced(AstNode* nodep, AstVar* varp, int level = 0) {
// Return true if varp never referenced under node.
// Return false if referenced, or tree too deep to be worth it, or side effects
if (!nodep) return true;
if (level > 2) return false;
if (nodep->isPure()) return false; // For example a $fgetc can't be reordered
if (VN_IS(nodep, NodeVarRef) && VN_CAST(nodep, NodeVarRef)->varp() == varp) return false;
return (varNotReferenced(nodep->nextp(), varp, level + 1)
&& varNotReferenced(nodep->op1p(), varp, level + 1)
&& varNotReferenced(nodep->op2p(), varp, level + 1)
&& varNotReferenced(nodep->op3p(), varp, level + 1)
&& varNotReferenced(nodep->op4p(), varp, level + 1));
}
bool replaceNodeAssign(AstNodeAssign* nodep) {
if (VN_IS(nodep->lhsp(), VarRef) && VN_IS(nodep->rhsp(), VarRef)
&& VN_CAST(nodep->lhsp(), VarRef)->sameNoLvalue(VN_CAST(nodep->rhsp(), VarRef))
&& !VN_IS(nodep, AssignDly)) {
// X = X. Quite pointless, though X <= X may override another earlier assignment
if (VN_IS(nodep, AssignW)) {
nodep->v3error("Wire inputs its own output, creating circular logic (wire x=x)");
return false; // Don't delete the assign, or V3Gate will freak out
} else {
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return true;
}
} else if (m_doV && VN_IS(nodep->lhsp(), Concat)) {
bool need_temp = false;
if (m_warn && !VN_IS(nodep, AssignDly)) { // Is same var on LHS and RHS?
// Note only do this (need user4) when m_warn, which is
// done as unique visitor
AstUser4InUse m_inuser4;
ConstVarMarkVisitor mark(nodep->lhsp());
ConstVarFindVisitor find(nodep->rhsp());
if (find.found()) need_temp = true;
}
if (need_temp) {
// The first time we constify, there may be the same variable on the LHS
// and RHS. In that case, we must use temporaries, or {a,b}={b,a} will break.
UINFO(4, " ASSITEMP " << nodep << endl);
// ASSIGN(CONCAT(lc1,lc2),rhs) -> ASSIGN(temp1,SEL(rhs,{size})),
// ASSIGN(temp2,SEL(newrhs,{size}))
// ASSIGN(lc1,temp1),
// ASSIGN(lc2,temp2)
} else {
UINFO(4, " ASSI " << nodep << endl);
// ASSIGN(CONCAT(lc1,lc2),rhs) -> ASSIGN(lc1,SEL(rhs,{size})),
// ASSIGN(lc2,SEL(newrhs,{size}))
}
if (debug() >= 9) nodep->dumpTree(cout, " Ass_old: ");
// Unlink the stuff
AstNode* lc1p = VN_CAST(nodep->lhsp(), Concat)->lhsp()->unlinkFrBack();
AstNode* lc2p = VN_CAST(nodep->lhsp(), Concat)->rhsp()->unlinkFrBack();
AstNode* conp = VN_CAST(nodep->lhsp(), Concat)->unlinkFrBack();
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
AstNode* rhs2p = rhsp->cloneTree(false);
// Calc widths
int lsb2 = 0;
int msb2 = lsb2 + lc2p->width() - 1;
int lsb1 = msb2 + 1;
int msb1 = lsb1 + lc1p->width() - 1;
UASSERT_OBJ(msb1 == (conp->width() - 1), nodep, "Width calc mismatch");
// Form ranges
AstSel* sel1p = new AstSel(conp->fileline(), rhsp, lsb1, msb1 - lsb1 + 1);
AstSel* sel2p = new AstSel(conp->fileline(), rhs2p, lsb2, msb2 - lsb2 + 1);
// Make new assigns of same flavor as old one
//*** Not cloneTree; just one node.
AstNode* newp = nullptr;
if (!need_temp) {
AstNodeAssign* asn1ap = VN_CAST(nodep->cloneType(lc1p, sel1p), NodeAssign);
AstNodeAssign* asn2ap = VN_CAST(nodep->cloneType(lc2p, sel2p), NodeAssign);
asn1ap->dtypeFrom(sel1p);
asn2ap->dtypeFrom(sel2p);
newp = AstNode::addNext(newp, asn1ap);
newp = AstNode::addNext(newp, asn2ap);
} else {
UASSERT_OBJ(m_modp, nodep, "Not under module");
// We could create just one temp variable, but we'll get better optimization
// if we make one per term.
string name1 = (string("__Vconcswap") + cvtToStr(m_modp->varNumGetInc()));
string name2 = (string("__Vconcswap") + cvtToStr(m_modp->varNumGetInc()));
AstVar* temp1p = new AstVar(sel1p->fileline(), AstVarType::BLOCKTEMP, name1,
VFlagLogicPacked(), msb1 - lsb1 + 1);
AstVar* temp2p = new AstVar(sel2p->fileline(), AstVarType::BLOCKTEMP, name2,
VFlagLogicPacked(), msb2 - lsb2 + 1);
m_modp->addStmtp(temp1p);
m_modp->addStmtp(temp2p);
AstNodeAssign* asn1ap
= VN_CAST(nodep->cloneType(
new AstVarRef(sel1p->fileline(), temp1p, VAccess::WRITE), sel1p),
NodeAssign);
AstNodeAssign* asn2ap
= VN_CAST(nodep->cloneType(
new AstVarRef(sel2p->fileline(), temp2p, VAccess::WRITE), sel2p),
NodeAssign);
AstNodeAssign* asn1bp
= VN_CAST(nodep->cloneType(
lc1p, new AstVarRef(sel1p->fileline(), temp1p, VAccess::READ)),
NodeAssign);
AstNodeAssign* asn2bp
= VN_CAST(nodep->cloneType(
lc2p, new AstVarRef(sel2p->fileline(), temp2p, VAccess::READ)),
NodeAssign);
asn1ap->dtypeFrom(temp1p);
asn1bp->dtypeFrom(temp1p);
asn2ap->dtypeFrom(temp2p);
asn2bp->dtypeFrom(temp2p);
// This order matters
newp = AstNode::addNext(newp, asn1ap);
newp = AstNode::addNext(newp, asn2ap);
newp = AstNode::addNext(newp, asn1bp);
newp = AstNode::addNext(newp, asn2bp);
}
if (debug() >= 9 && newp) newp->dumpTreeAndNext(cout, " _new: ");
nodep->addNextHere(newp);
// Cleanup
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
VL_DO_DANGLING(conp->deleteTree(), conp);
// Further reduce, either node may have more reductions.
return true;
} else if (m_doV && VN_IS(nodep->rhsp(), StreamR)) {
// The right-streaming operator on rhs of assignment does not
// change the order of bits. Eliminate stream but keep its lhsp
// Unlink the stuff
AstNode* srcp = VN_CAST(nodep->rhsp(), StreamR)->lhsp()->unlinkFrBack();
AstNode* sizep = VN_CAST(nodep->rhsp(), StreamR)->rhsp()->unlinkFrBack();
AstNode* streamp = VN_CAST(nodep->rhsp(), StreamR)->unlinkFrBack();
nodep->rhsp(srcp);
// Cleanup
VL_DO_DANGLING(sizep->deleteTree(), sizep);
VL_DO_DANGLING(streamp->deleteTree(), streamp);
// Further reduce, any of the nodes may have more reductions.
return true;
} else if (m_doV && VN_IS(nodep->lhsp(), StreamL)) {
// Push the stream operator to the rhs of the assignment statement
int dWidth = VN_CAST(nodep->lhsp(), StreamL)->lhsp()->width();
int sWidth = nodep->rhsp()->width();
// Unlink the stuff
AstNode* dstp = VN_CAST(nodep->lhsp(), StreamL)->lhsp()->unlinkFrBack();
AstNode* streamp = VN_CAST(nodep->lhsp(), StreamL)->unlinkFrBack();
AstNode* srcp = nodep->rhsp()->unlinkFrBack();
// Connect the rhs to the stream operator and update its width
VN_CAST(streamp, StreamL)->lhsp(srcp);
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED);
// Shrink the RHS if necessary
if (sWidth > dWidth) {
streamp = new AstSel(streamp->fileline(), streamp, sWidth - dWidth, dWidth);
}
// Link the nodes back in
nodep->lhsp(dstp);
nodep->rhsp(streamp);
return true;
} else if (m_doV && VN_IS(nodep->lhsp(), StreamR)) {
// The right stream operator on lhs of assignment statement does
// not reorder bits. However, if the rhs is wider than the lhs,
// then we select bits from the left-most, not the right-most.
int dWidth = VN_CAST(nodep->lhsp(), StreamR)->lhsp()->width();
int sWidth = nodep->rhsp()->width();
// Unlink the stuff
AstNode* dstp = VN_CAST(nodep->lhsp(), StreamR)->lhsp()->unlinkFrBack();
AstNode* sizep = VN_CAST(nodep->lhsp(), StreamR)->rhsp()->unlinkFrBack();
AstNode* streamp = VN_CAST(nodep->lhsp(), StreamR)->unlinkFrBack();
AstNode* srcp = nodep->rhsp()->unlinkFrBack();
if (sWidth > dWidth) {
srcp = new AstSel(streamp->fileline(), srcp, sWidth - dWidth, dWidth);
}
nodep->lhsp(dstp);
nodep->rhsp(srcp);
// Cleanup
VL_DO_DANGLING(sizep->deleteTree(), sizep);
VL_DO_DANGLING(streamp->deleteTree(), streamp);
// Further reduce, any of the nodes may have more reductions.
return true;
} else if (replaceAssignMultiSel(nodep)) {
return true;
}
return false;
}
// Boolean replacements
bool operandBoolShift(const AstNode* nodep) {
// boolean test of AND(const,SHIFTR(x,const)) -> test of AND(SHIFTL(x,const), x)
if (!VN_IS(nodep, And)) return false;
if (!VN_IS(VN_CAST_CONST(nodep, And)->lhsp(), Const)) return false;
if (!VN_IS(VN_CAST_CONST(nodep, And)->rhsp(), ShiftR)) return false;
const AstShiftR* shiftp = VN_CAST(VN_CAST_CONST(nodep, And)->rhsp(), ShiftR);
if (!VN_IS(shiftp->rhsp(), Const)) return false;
if (static_cast<uint32_t>(nodep->width())
<= VN_CAST_CONST(shiftp->rhsp(), Const)->toUInt()) {
return false;
}
return true;
}
void replaceBoolShift(AstNode* nodep) {
if (debug() >= 9) nodep->dumpTree(cout, " bshft_old: ");
AstConst* andConstp = VN_CAST(VN_CAST(nodep, And)->lhsp(), Const);
AstNode* fromp = VN_CAST(VN_CAST(nodep, And)->rhsp(), ShiftR)->lhsp()->unlinkFrBack();
AstConst* shiftConstp
= VN_CAST(VN_CAST(VN_CAST(nodep, And)->rhsp(), ShiftR)->rhsp(), Const);
V3Number val(andConstp, andConstp->width());
val.opShiftL(andConstp->num(), shiftConstp->num());
AstAnd* newp = new AstAnd(nodep->fileline(), new AstConst(nodep->fileline(), val), fromp);
// widthMin no longer applicable if different C-expanded width
newp->dtypeSetLogicSized(nodep->width(), VSigning::UNSIGNED);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
if (debug() >= 9) newp->dumpTree(cout, " _new: ");
}
void replaceWithSimulation(AstNode* nodep) {
SimulateVisitor simvis;
// Run it - may be unoptimizable due to large for loop, etc
simvis.mainParamEmulate(nodep);
if (!simvis.optimizable()) {
AstNode* errorp = simvis.whyNotNodep();
if (!errorp) errorp = nodep;
nodep->v3error("Expecting expression to be constant, but can't determine constant for "
<< nodep->prettyTypeName() << '\n'
<< errorp->warnOther() << "... Location of non-constant "
<< errorp->prettyTypeName() << ": " << simvis.whyNotMessage());
VL_DO_DANGLING(replaceZero(nodep), nodep);
} else {
// Fetch the result
AstNode* valuep = simvis.fetchValueNull(nodep); // valuep is owned by Simulate
UASSERT_OBJ(valuep, nodep, "No value returned from simulation");
// Replace it
AstNode* newp = valuep->cloneTree(false);
newp->dtypeFrom(nodep);
newp->fileline(nodep->fileline());
UINFO(4, "Simulate->" << newp << endl);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
}
//----------------------------------------
// VISITORS
virtual void visit(AstNetlist* nodep) override {
// Iterate modules backwards, in bottom-up order. That's faster
iterateChildrenBackwards(nodep);
}
virtual void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
{
m_modp = nodep;
iterateChildren(nodep);
}
}
virtual void visit(AstCFunc* nodep) override {
// No ASSIGNW removals under funcs, we've long eliminated INITIALs
// (We should perhaps rename the assignw's to just assigns)
VL_RESTORER(m_wremove);
{
m_wremove = false;
iterateChildren(nodep);
}
}
virtual void visit(AstScope* nodep) override {
// No ASSIGNW removals under scope, we've long eliminated INITIALs
VL_RESTORER(m_wremove);
VL_RESTORER(m_scopep);
{
m_wremove = false;
m_scopep = nodep;
iterateChildren(nodep);
}
}
void swapSides(AstNodeBiCom* nodep) {
// COMMUTATIVE({a},CONST) -> COMMUTATIVE(CONST,{a})
// This simplifies later optimizations
AstNode* lhsp = nodep->lhsp()->unlinkFrBackWithNext();
AstNode* rhsp = nodep->rhsp()->unlinkFrBackWithNext();
nodep->lhsp(rhsp);
nodep->rhsp(lhsp);
iterate(nodep); // Again?
}
int operandConcatMove(AstConcat* nodep) {
// CONCAT under concat (See moveConcat)
// Return value: true indicates to do it; 2 means move to LHS
AstConcat* abConcp = VN_CAST(nodep->lhsp(), Concat);
AstConcat* bcConcp = VN_CAST(nodep->rhsp(), Concat);
if (!abConcp && !bcConcp) return 0;
if (bcConcp) {
AstNode* ap = nodep->lhsp();
AstNode* bp = bcConcp->lhsp();
// If a+b == 32,64,96 etc, then we want to have a+b together on LHS
if (VL_BITBIT_I(ap->width() + bp->width()) == 0) return 2; // Transform 2: to abConc
} else { // abConcp
// Unless lhs is already 32 bits due to above, reorder it
if (VL_BITBIT_I(nodep->lhsp()->width()) != 0) return 1; // Transform 1: to bcConc
}
return 0; // ok
}
void moveConcat(AstConcat* nodep) {
// 1: CONCAT(CONCAT({a},{b}),{c}) -> CONCAT({a},CONCAT({b}, {c}))
// or 2: CONCAT({a}, CONCAT({b},{c})) -> CONCAT(CONCAT({a},{b}),{c})
// Because the lhs of a concat needs a shift but the rhs doesn't,
// putting additional CONCATs on the RHS leads to fewer assembler operations.
// However, we'll end up with lots of wide moves if we make huge trees
// like that, so on 32 bit boundaries, we'll do the opposite form.
UINFO(4, "Move concat: " << nodep << endl);
if (operandConcatMove(nodep) > 1) {
AstNode* ap = nodep->lhsp()->unlinkFrBack();
AstConcat* bcConcp = VN_CAST(nodep->rhsp(), Concat);
bcConcp->unlinkFrBack();
AstNode* bp = bcConcp->lhsp()->unlinkFrBack();
AstNode* cp = bcConcp->rhsp()->unlinkFrBack();
AstConcat* abConcp = new AstConcat(bcConcp->fileline(), ap, bp);
nodep->lhsp(abConcp);
nodep->rhsp(cp);
// If bp was a concat, then we have this exact same form again!
// Recurse rather then calling node->iterate to prevent 2^n recursion!
if (operandConcatMove(abConcp)) moveConcat(abConcp);
VL_DO_DANGLING(bcConcp->deleteTree(), bcConcp);
} else {
AstConcat* abConcp = VN_CAST(nodep->lhsp(), Concat);
abConcp->unlinkFrBack();
AstNode* ap = abConcp->lhsp()->unlinkFrBack();
AstNode* bp = abConcp->rhsp()->unlinkFrBack();
AstNode* cp = nodep->rhsp()->unlinkFrBack();
AstConcat* bcConcp = new AstConcat(abConcp->fileline(), bp, cp);
nodep->lhsp(ap);
nodep->rhsp(bcConcp);
if (operandConcatMove(bcConcp)) moveConcat(bcConcp);
VL_DO_DANGLING(abConcp->deleteTree(), abConcp);
}
}
// Special cases
virtual void visit(AstConst*) override {} // Already constant
virtual void visit(AstCell* nodep) override {
if (m_params) {
iterateAndNextNull(nodep->paramsp());
} else {
iterateChildren(nodep);
}
}
virtual void visit(AstPin* nodep) override { iterateChildren(nodep); }
void replaceLogEq(AstLogEq* nodep) {
// LOGEQ(a,b) => AstLogAnd{AstLogOr{AstLogNot{a},b},AstLogOr{AstLogNot{b},a}}
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
// Do exactly as IEEE says, might result in extra terms, so in future may do differently
AstLogAnd* newp = new AstLogAnd(
nodep->fileline(),
new AstLogOr(nodep->fileline(), new AstLogNot(nodep->fileline(), lhsp), rhsp),
new AstLogOr(nodep->fileline(),
new AstLogNot(nodep->fileline(), rhsp->cloneTree(false)),
lhsp->cloneTree(false)));
newp->dtypeFrom(nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void replaceSelSel(AstSel* nodep) {
// SEL(SEL({x},a,b),c,d) => SEL({x},a+c,d)
AstSel* belowp = VN_CAST(nodep->fromp(), Sel);
AstNode* fromp = belowp->fromp()->unlinkFrBack();
AstNode* widthp = nodep->widthp()->unlinkFrBack();
AstNode* lsb1p = nodep->lsbp()->unlinkFrBack();
AstNode* lsb2p = belowp->lsbp()->unlinkFrBack();
// Eliminate lower range
UINFO(4, "Elim Lower range: " << nodep << endl);
AstNode* newlsbp;
if (VN_IS(lsb1p, Const) && VN_IS(lsb2p, Const)) {
newlsbp = new AstConst(lsb1p->fileline(), VN_CAST(lsb1p, Const)->toUInt()
+ VN_CAST(lsb2p, Const)->toUInt());
VL_DO_DANGLING(lsb1p->deleteTree(), lsb1p);
VL_DO_DANGLING(lsb2p->deleteTree(), lsb2p);
} else {
// Width is important, we need the width of the fromp's
// expression, not the potentially smaller lsb1p's width
newlsbp
= new AstAdd(lsb1p->fileline(), lsb2p, new AstExtend(lsb1p->fileline(), lsb1p));
newlsbp->dtypeFrom(lsb2p); // Unsigned
VN_CAST(newlsbp, Add)->rhsp()->dtypeFrom(lsb2p);
}
AstSel* newp = new AstSel(nodep->fileline(), fromp, newlsbp, widthp);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void replaceSelConcat(AstSel* nodep) {
// SEL(CONCAT(a,b),c,d) => SEL(a or b, . .)
AstConcat* conp = VN_CAST(nodep->fromp(), Concat);
AstNode* conLhsp = conp->lhsp();
AstNode* conRhsp = conp->rhsp();
if (static_cast<int>(nodep->lsbConst()) >= conRhsp->width()) {
conLhsp->unlinkFrBack();
AstSel* newp = new AstSel(nodep->fileline(), conLhsp,
nodep->lsbConst() - conRhsp->width(), nodep->widthConst());
nodep->replaceWith(newp);
} else if (static_cast<int>(nodep->msbConst()) < conRhsp->width()) {
conRhsp->unlinkFrBack();
AstSel* newp
= new AstSel(nodep->fileline(), conRhsp, nodep->lsbConst(), nodep->widthConst());
nodep->replaceWith(newp);
} else {
// Yuk, split between the two
conRhsp->unlinkFrBack();
conLhsp->unlinkFrBack();
AstConcat* newp
= new AstConcat(nodep->fileline(),
new AstSel(nodep->fileline(), conLhsp, 0,
nodep->msbConst() - conRhsp->width() + 1),
new AstSel(nodep->fileline(), conRhsp, nodep->lsbConst(),
conRhsp->width() - nodep->lsbConst()));
nodep->replaceWith(newp);
}
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
bool operandSelReplicate(AstSel* nodep) {
// SEL(REPLICATE(from,rep),lsb,width) => SEL(from,0,width) as long
// as SEL's width <= b's width
AstReplicate* repp = VN_CAST(nodep->fromp(), Replicate);
AstNode* fromp = repp->lhsp();
AstConst* lsbp = VN_CAST(nodep->lsbp(), Const);
if (!lsbp) return false;
AstNode* widthp = nodep->widthp();
if (!VN_IS(widthp, Const)) return false;
UASSERT_OBJ(fromp->width(), nodep, "Not widthed");
if ((lsbp->toUInt() / fromp->width())
!= ((lsbp->toUInt() + nodep->width() - 1) / fromp->width())) {
return false;
}
//
fromp->unlinkFrBack();
widthp->unlinkFrBack();
AstSel* newp
= new AstSel(nodep->fileline(), fromp,
new AstConst(lsbp->fileline(), lsbp->toUInt() % fromp->width()), widthp);
newp->dtypeFrom(nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return true;
}
bool operandRepRep(AstReplicate* nodep) {
// REPLICATE(REPLICATE2(from2,cnt2),cnt1) => REPLICATE(from2,(cnt1+cnt2))
AstReplicate* rep2p = VN_CAST(nodep->lhsp(), Replicate);
AstNode* from2p = rep2p->lhsp();
AstConst* cnt1p = VN_CAST(nodep->rhsp(), Const);
if (!cnt1p) return false;
AstConst* cnt2p = VN_CAST(rep2p->rhsp(), Const);
if (!cnt2p) return false;
//
from2p->unlinkFrBack();
cnt1p->unlinkFrBack();
cnt2p->unlinkFrBack();
AstReplicate* newp
= new AstReplicate(nodep->fileline(), from2p, cnt1p->toUInt() * cnt2p->toUInt());
newp->dtypeFrom(nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return true;
}
bool operandConcatSame(AstConcat* nodep) {
// CONCAT(fromp,fromp) -> REPLICATE(fromp,1+1)
// CONCAT(REP(fromp,cnt1),fromp) -> REPLICATE(fromp,cnt1+1)
// CONCAT(fromp,REP(fromp,cnt1)) -> REPLICATE(fromp,1+cnt1)
// CONCAT(REP(fromp,cnt1),REP(fromp,cnt2)) -> REPLICATE(fromp,cnt1+cnt2)
AstNode* from1p = nodep->lhsp();
uint32_t cnt1 = 1;
AstNode* from2p = nodep->rhsp();
uint32_t cnt2 = 1;
if (VN_IS(from1p, Replicate)) {
AstConst* cnt1p = VN_CAST(VN_CAST(from1p, Replicate)->rhsp(), Const);
if (!cnt1p) return false;
from1p = VN_CAST(from1p, Replicate)->lhsp();
cnt1 = cnt1p->toUInt();
}
if (VN_IS(from2p, Replicate)) {
AstConst* cnt2p = VN_CAST(VN_CAST(from2p, Replicate)->rhsp(), Const);
if (!cnt2p) return false;
from2p = VN_CAST(from2p, Replicate)->lhsp();
cnt2 = cnt2p->toUInt();
}
if (!operandsSame(from1p, from2p)) return false;
//
from1p->unlinkFrBack();
AstReplicate* newp = new AstReplicate(nodep->fileline(), from1p, cnt1 + cnt2);
newp->dtypeFrom(nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return true;
}
void replaceSelIntoBiop(AstSel* nodep) {
// SEL(BUFIF1(a,b),1,bit) => BUFIF1(SEL(a,1,bit),SEL(b,1,bit))
AstNodeBiop* fromp = VN_CAST(nodep->fromp()->unlinkFrBack(), NodeBiop);
UASSERT_OBJ(fromp, nodep, "Called on non biop");
AstNode* lsbp = nodep->lsbp()->unlinkFrBack();
AstNode* widthp = nodep->widthp()->unlinkFrBack();
//
AstNode* bilhsp = fromp->lhsp()->unlinkFrBack();
AstNode* birhsp = fromp->rhsp()->unlinkFrBack();
//
fromp->lhsp(
new AstSel(nodep->fileline(), bilhsp, lsbp->cloneTree(true), widthp->cloneTree(true)));
fromp->rhsp(new AstSel(nodep->fileline(), birhsp, lsbp, widthp));
fromp->dtypeFrom(nodep);
nodep->replaceWith(fromp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void replaceSelIntoUniop(AstSel* nodep) {
// SEL(NOT(a),1,bit) => NOT(SEL(a,bit))
AstNodeUniop* fromp = VN_CAST(nodep->fromp()->unlinkFrBack(), NodeUniop);
UASSERT_OBJ(fromp, nodep, "Called on non biop");
AstNode* lsbp = nodep->lsbp()->unlinkFrBack();
AstNode* widthp = nodep->widthp()->unlinkFrBack();
//
AstNode* bilhsp = fromp->lhsp()->unlinkFrBack();
//
fromp->lhsp(new AstSel(nodep->fileline(), bilhsp, lsbp, widthp));
fromp->dtypeFrom(nodep);
nodep->replaceWith(fromp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
virtual void visit(AstAttrOf* nodep) override {
VL_RESTORER(m_attrp);
{
m_attrp = nodep;
iterateChildren(nodep);
}
}
virtual void visit(AstArraySel* nodep) override {
iterateAndNextNull(nodep->bitp());
if (VN_IS(nodep->bitp(), Const)
&& VN_IS(nodep->fromp(), VarRef)
// Need to make sure it's an array object so don't mis-allow a constant (bug509.)
&& VN_CAST(nodep->fromp(), VarRef)->varp()
&& VN_IS(VN_CAST(nodep->fromp(), VarRef)->varp()->valuep(), InitArray)) {
m_selp = nodep; // Ask visit(AstVarRef) to replace varref with const
}
iterateAndNextNull(nodep->fromp());
if (VN_IS(nodep->fromp(), Const)) { // It did.
if (!m_selp) {
nodep->v3error("Illegal assignment of constant to unpacked array");
} else {
AstNode* fromp = nodep->fromp()->unlinkFrBack();
nodep->replaceWith(fromp);
if (VN_IS(fromp->dtypep()->skipRefp(), NodeArrayDType)) {
// Strip off array to find what array references
fromp->dtypeFrom(
VN_CAST(fromp->dtypep()->skipRefp(), NodeArrayDType)->subDTypep());
}
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
}
m_selp = nullptr;
}
virtual void visit(AstNodeVarRef* nodep) override {
iterateChildren(nodep);
UASSERT_OBJ(nodep->varp(), nodep, "Not linked");
bool did = false;
if (m_doV && nodep->varp()->valuep() && !m_attrp) {
// if (debug()) valuep->dumpTree(cout, " visitvaref: ");
iterateAndNextNull(nodep->varp()->valuep()); // May change nodep->varp()->valuep()
AstNode* valuep = nodep->varp()->valuep();
if (nodep->access().isReadOnly()
&& ((!m_params // Can reduce constant wires into equations
&& m_doNConst
&& v3Global.opt.oConst()
// Default value, not a "known" constant for this usage
&& !nodep->varp()->isClassMember()
&& !(nodep->varp()->isFuncLocal() && nodep->varp()->isNonOutput())
&& !nodep->varp()->noSubst() && !nodep->varp()->isSigPublic())
|| nodep->varp()->isParam())) {
if (operandConst(valuep)) {
const V3Number& num = VN_CAST(valuep, Const)->num();
// UINFO(2,"constVisit "<<cvtToHex(valuep)<<" "<<num<<endl);
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
did = true;
} else if (m_selp && VN_IS(valuep, InitArray)) {
AstInitArray* initarp = VN_CAST(valuep, InitArray);
uint32_t bit = m_selp->bitConst();
AstNode* itemp = initarp->getIndexDefaultedValuep(bit);
if (VN_IS(itemp, Const)) {
const V3Number& num = VN_CAST(itemp, Const)->num();
// UINFO(2,"constVisit "<<cvtToHex(valuep)<<" "<<num<<endl);
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
did = true;
}
} else if (m_params && VN_IS(valuep, InitArray) && VN_IS(nodep->backp(), Pin)) {
// Allow parameters to pass arrays
// Earlier recursion of InitArray made sure each array value is constant
// This exception is fairly fragile, i.e. doesn't
// support arrays of arrays or other stuff
AstNode* newp = valuep->cloneTree(false);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
did = true;
} else if (nodep->varp()->isParam() && VN_IS(valuep, Unbounded)) {
AstNode* newp = valuep->cloneTree(false);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
did = true;
}
}
}
if (!did && m_required) {
nodep->v3error("Expecting expression to be constant, but variable isn't const: "
<< nodep->varp()->prettyNameQ());
}
}
virtual void visit(AstEnumItemRef* nodep) override {
iterateChildren(nodep);
UASSERT_OBJ(nodep->itemp(), nodep, "Not linked");
bool did = false;
if (nodep->itemp()->valuep()) {
// if (debug()) nodep->itemp()->valuep()->dumpTree(cout, " visitvaref: ");
if (nodep->itemp()->user4()) {
nodep->v3error("Recursive enum value: " << nodep->itemp()->prettyNameQ());
} else {
nodep->itemp()->user4(true);
iterateAndNextNull(nodep->itemp()->valuep());
nodep->itemp()->user4(false);
}
if (AstConst* valuep = VN_CAST(nodep->itemp()->valuep(), Const)) {
const V3Number& num = valuep->num();
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
did = true;
}
}
if (!did && m_required) {
nodep->v3error("Expecting expression to be constant, but variable isn't const: "
<< nodep->itemp()->prettyNameQ());
}
}
// virtual void visit(AstCvtPackString* nodep) override {
// Not constant propagated (for today) because AstNodeMath::isOpaque is set
// Someday if lower is constant, convert to quoted "string".
bool onlySenItemInSenTree(AstSenItem* nodep) {
// Only one if it's not in a list
return (!nodep->nextp() && nodep->backp()->nextp() != nodep);
}
virtual void visit(AstSenItem* nodep) override {
iterateChildren(nodep);
if (m_doNConst
&& (VN_IS(nodep->sensp(), Const) || VN_IS(nodep->sensp(), EnumItemRef)
|| (nodep->varrefp() && nodep->varrefp()->varp()->isParam()))) {
// Constants in sensitivity lists may be removed (we'll simplify later)
if (nodep->isClocked()) { // A constant can never get a pos/negedge
if (onlySenItemInSenTree(nodep)) {
nodep->replaceWith(new AstSenItem(nodep->fileline(), AstSenItem::Never()));
VL_DO_DANGLING(nodep->deleteTree(), nodep);
} else {
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
} else { // Otherwise it may compute a result that needs to settle out
nodep->replaceWith(new AstSenItem(nodep->fileline(), AstSenItem::Combo()));
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
} else if (m_doNConst && VN_IS(nodep->sensp(), Not)) {
// V3Gate may propagate NOTs into clocks... Just deal with it
AstNode* sensp = nodep->sensp();
AstNode* lastSensp = sensp;
bool invert = false;
while (VN_IS(lastSensp, Not)) {
lastSensp = VN_CAST(lastSensp, Not)->lhsp();
invert = !invert;
}
UINFO(8, "senItem(NOT...) " << nodep << " " << invert << endl);
if (invert) nodep->edgeType(nodep->edgeType().invert());
AstNodeVarRef* senvarp = VN_CAST(lastSensp->unlinkFrBack(), NodeVarRef);
UASSERT_OBJ(senvarp, sensp, "Non-varref sensitivity variable");
sensp->replaceWith(senvarp);
VL_DO_DANGLING(sensp->deleteTree(), sensp);
} else if (!m_doNConst // Deal with later when doNConst missing
&& (VN_IS(nodep->sensp(), EnumItemRef) || VN_IS(nodep->sensp(), Const))) {
} else if (nodep->isIllegal()) { // Deal with later
} else {
UASSERT_OBJ(!(nodep->hasVar() && !nodep->varrefp()), nodep,
"Null sensitivity variable");
}
}
struct SenItemCmp {
bool operator()(const AstSenItem* lhsp, const AstSenItem* rhsp) const {
if (lhsp->type() < rhsp->type()) return true;
if (lhsp->type() > rhsp->type()) return false;
// Looks visually better if we keep sorted by name
if (!lhsp->varrefp() && rhsp->varrefp()) return true;
if (lhsp->varrefp() && !rhsp->varrefp()) return false;
if (lhsp->varrefp() && rhsp->varrefp()) {
if (lhsp->varrefp()->name() < rhsp->varrefp()->name()) return true;
if (lhsp->varrefp()->name() > rhsp->varrefp()->name()) return false;
// But might be same name with different scopes
if (lhsp->varrefp()->varScopep() < rhsp->varrefp()->varScopep()) { return true; }
if (lhsp->varrefp()->varScopep() > rhsp->varrefp()->varScopep()) { return false; }
// Or rarely, different data types
if (lhsp->varrefp()->dtypep() < rhsp->varrefp()->dtypep()) return true;
if (lhsp->varrefp()->dtypep() > rhsp->varrefp()->dtypep()) return false;
}
// Sort by edge, AFTER variable, as we want multiple edges for same var adjacent.
// note the SenTree optimizer requires this order (more
// general first, less general last)
if (lhsp->edgeType() < rhsp->edgeType()) return true;
if (lhsp->edgeType() > rhsp->edgeType()) return false;
return false;
}
};
virtual void visit(AstSenTree* nodep) override {
iterateChildren(nodep);
if (m_doExpensive) {
// cout<<endl; nodep->dumpTree(cout, "ssin: ");
// Optimize ideas for the future:
// SENTREE(... SENGATE(x,a), SENGATE(SENITEM(x),b) ...) => SENGATE(x,OR(a,b))
// SENTREE(... SENITEM(x), SENGATE(SENITEM(x),*) ...) => SENITEM(x)
// Do we need the SENITEM's to be identical? No because we're
// ORing between them; we just need to ensure that the result is at
// least as frequently activating. So we simply
// SENGATE(SENITEM(x)) -> SENITEM(x), then let it collapse with the
// other SENITEM(x).
{
AstUser4InUse m_inuse4;
// Mark x in SENITEM(x)
for (AstSenItem* senp = VN_CAST(nodep->sensesp(), SenItem); senp;
senp = VN_CAST(senp->nextp(), SenItem)) {
if (senp->varrefp() && senp->varrefp()->varScopep()) {
senp->varrefp()->varScopep()->user4(1);
}
}
}
// Sort the sensitivity names so "posedge a or b" and "posedge b or a" end up together.
// Also, remove duplicate assignments, and fold POS&NEGs into ANYEDGEs
// Make things a little faster; check first if we need a sort
for (AstSenItem *nextp, *senp = VN_CAST(nodep->sensesp(), SenItem); senp;
senp = nextp) {
nextp = VN_CAST(senp->nextp(), SenItem);
// cppcheck-suppress unassignedVariable // cppcheck bug
SenItemCmp cmp;
if (nextp && !cmp(senp, nextp)) {
// Something's out of order, sort it
senp = nullptr;
std::vector<AstSenItem*> vec;
for (AstSenItem* senp = VN_CAST(nodep->sensesp(), SenItem); senp;
senp = VN_CAST(senp->nextp(), SenItem)) {
vec.push_back(senp);
}
stable_sort(vec.begin(), vec.end(), SenItemCmp());
for (const auto& ip : vec) ip->unlinkFrBack();
for (const auto& ip : vec) nodep->addSensesp(ip);
break;
}
}
// Pass2, remove dup edges
for (AstSenItem *nextp, *senp = VN_CAST(nodep->sensesp(), SenItem); senp;
senp = nextp) {
nextp = VN_CAST(senp->nextp(), SenItem);
AstSenItem* const litemp = senp;
AstSenItem* const ritemp = nextp;
if (ritemp) {
if ((litemp->varrefp() && ritemp->varrefp()
&& litemp->varrefp()->sameGateTree(ritemp->varrefp()))
|| (!litemp->varrefp() && !ritemp->varrefp())) {
// We've sorted in the order ANY, BOTH, POS, NEG,
// so we don't need to try opposite orders
if ((litemp->edgeType()
== VEdgeType::ET_ANYEDGE) // ANY or {BOTH|POS|NEG} -> ANY
|| (litemp->edgeType()
== VEdgeType::ET_BOTHEDGE) // BOTH or {POS|NEG} -> BOTH
|| (litemp->edgeType() == VEdgeType::ET_POSEDGE // POS or NEG -> BOTH
&& ritemp->edgeType() == VEdgeType::ET_NEGEDGE)
|| (litemp->edgeType() == ritemp->edgeType()) // Identical edges
) {
// Fix edge of old node
if (litemp->edgeType() == VEdgeType::ET_POSEDGE
&& ritemp->edgeType() == VEdgeType::ET_NEGEDGE)
litemp->edgeType(VEdgeType::ET_BOTHEDGE);
// Remove redundant node
VL_DO_DANGLING(ritemp->unlinkFrBack()->deleteTree(), ritemp);
VL_DANGLING(ritemp);
// Try to collapse again
nextp = litemp;
}
}
}
}
// nodep->dumpTree(cout, "ssou: ");
}
}
//-----
// Zero elimination
virtual void visit(AstNodeAssign* nodep) override {
iterateChildren(nodep);
if (m_doNConst && replaceNodeAssign(nodep)) return;
}
virtual void visit(AstAssignAlias* nodep) override {
// Don't perform any optimizations, keep the alias around
}
virtual void visit(AstAssignVarScope* nodep) override {
// Don't perform any optimizations, the node won't be linked yet
}
virtual void visit(AstAssignW* nodep) override {
iterateChildren(nodep);
if (m_doNConst && replaceNodeAssign(nodep)) return;
AstNodeVarRef* varrefp = VN_CAST(
nodep->lhsp(),
VarRef); // Not VarXRef, as different refs may set different values to each hierarchy
if (m_wremove && !m_params && m_doNConst && m_modp && operandConst(nodep->rhsp())
&& !VN_CAST(nodep->rhsp(), Const)->num().isFourState()
&& varrefp // Don't do messes with BITREFs/ARRAYREFs
&& !varrefp->varp()->valuep() // Not already constified
&& !varrefp->varScopep()) { // Not scoped (or each scope may have different initial
// value)
// ASSIGNW (VARREF, const) -> INITIAL ( ASSIGN (VARREF, const) )
UINFO(4, "constAssignW " << nodep << endl);
// Make a initial assignment
AstNode* exprp = nodep->rhsp()->unlinkFrBack();
varrefp->unlinkFrBack();
AstInitial* newinitp = new AstInitial(
nodep->fileline(), new AstAssign(nodep->fileline(), varrefp, exprp));
m_modp->addStmtp(newinitp);
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
// Set the initial value right in the variable so we can constant propagate
AstNode* initvaluep = exprp->cloneTree(false);
varrefp->varp()->valuep(initvaluep);
}
}
virtual void visit(AstNodeIf* nodep) override {
iterateChildren(nodep);
if (m_doNConst) {
if (const AstConst* constp = VN_CAST(nodep->condp(), Const)) {
AstNode* keepp = nullptr;
if (constp->isZero()) {
UINFO(4, "IF(0,{any},{x}) => {x}: " << nodep << endl);
keepp = nodep->elsesp();
} else if (!m_doV || constp->isNeqZero()) { // Might be X in Verilog
UINFO(4, "IF(!0,{x},{any}) => {x}: " << nodep << endl);
keepp = nodep->ifsp();
} else {
UINFO(4, "IF condition is X, retaining: " << nodep << endl);
return;
}
if (keepp) {
keepp->unlinkFrBackWithNext();
nodep->replaceWith(keepp);
} else {
nodep->unlinkFrBack();
}
VL_DO_DANGLING(nodep->deleteTree(), nodep);
} else if (!afterComment(nodep->ifsp()) && !afterComment(nodep->elsesp())) {
// Empty block, remove it
// Note if we support more C++ then there might be side
// effects in the condition itself
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
} else if (!afterComment(nodep->ifsp())) {
UINFO(4, "IF({x}) nullptr {...} => IF(NOT{x}}: " << nodep << endl);
AstNode* condp = nodep->condp();
AstNode* elsesp = nodep->elsesp();
condp->unlinkFrBackWithNext();
elsesp->unlinkFrBackWithNext();
if (nodep->ifsp()) { // Must have been comment
nodep->ifsp()->unlinkFrBackWithNext()->deleteTree();
}
nodep->condp(new AstLogNot(condp->fileline(),
condp)); // LogNot, as C++ optimization also possible
nodep->addIfsp(elsesp);
} else if (((VN_IS(nodep->condp(), Not) && nodep->condp()->width() == 1)
|| VN_IS(nodep->condp(), LogNot))
&& nodep->ifsp() && nodep->elsesp()) {
UINFO(4, "IF(NOT {x}) => IF(x) swapped if/else" << nodep << endl);
AstNode* condp
= VN_CAST(nodep->condp(), NodeUniop)->lhsp()->unlinkFrBackWithNext();
AstNode* ifsp = nodep->ifsp()->unlinkFrBackWithNext();
AstNode* elsesp = nodep->elsesp()->unlinkFrBackWithNext();
AstIf* ifp = new AstIf(nodep->fileline(), condp, elsesp, ifsp);
ifp->branchPred(nodep->branchPred().invert());
nodep->replaceWith(ifp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
} else if (ifSameAssign(nodep)) {
UINFO(
4,
"IF({a}) ASSIGN({b},{c}) else ASSIGN({b},{d}) => ASSIGN({b}, {a}?{c}:{d})\n");
AstNodeAssign* ifp = VN_CAST(nodep->ifsp(), NodeAssign);
AstNodeAssign* elsep = VN_CAST(nodep->elsesp(), NodeAssign);
ifp->unlinkFrBack();
AstNode* condp = nodep->condp()->unlinkFrBack();
AstNode* truep = ifp->rhsp()->unlinkFrBack();
AstNode* falsep = elsep->rhsp()->unlinkFrBack();
ifp->rhsp(new AstCond(truep->fileline(), condp, truep, falsep));
nodep->replaceWith(ifp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
} else if (false // Disabled, as vpm assertions are faster
// without due to short-circuiting
&& operandIfIf(nodep)) {
UINFO(9, "IF({a}) IF({b}) => IF({a} && {b})" << endl);
AstNodeIf* lowerIfp = VN_CAST(nodep->ifsp(), NodeIf);
AstNode* condp = nodep->condp()->unlinkFrBack();
AstNode* lowerIfsp = lowerIfp->ifsp()->unlinkFrBackWithNext();
AstNode* lowerCondp = lowerIfp->condp()->unlinkFrBackWithNext();
nodep->condp(new AstLogAnd(lowerIfp->fileline(), condp, lowerCondp));
lowerIfp->replaceWith(lowerIfsp);
VL_DO_DANGLING(lowerIfp->deleteTree(), lowerIfp);
} else if (operandBoolShift(nodep->condp())) {
replaceBoolShift(nodep->condp());
}
}
}
virtual void visit(AstDisplay* nodep) override {
// DISPLAY(SFORMAT(text1)),DISPLAY(SFORMAT(text2)) -> DISPLAY(SFORMAT(text1+text2))
iterateChildren(nodep);
if (stmtDisplayDisplay(nodep)) return;
}
bool stmtDisplayDisplay(AstDisplay* nodep) {
// DISPLAY(SFORMAT(text1)),DISPLAY(SFORMAT(text2)) -> DISPLAY(SFORMAT(text1+text2))
if (!m_modp) return false; // Don't optimize under single statement
AstDisplay* prevp = VN_CAST(nodep->backp(), Display);
if (!prevp) return false;
if (!((prevp->displayType() == nodep->displayType())
|| (prevp->displayType() == AstDisplayType::DT_WRITE
&& nodep->displayType() == AstDisplayType::DT_DISPLAY)
|| (prevp->displayType() == AstDisplayType::DT_DISPLAY
&& nodep->displayType() == AstDisplayType::DT_WRITE)))
return false;
if ((prevp->filep() && !nodep->filep()) || (!prevp->filep() && nodep->filep())
|| !prevp->filep()->sameTree(nodep->filep()))
return false;
if (!prevp->fmtp() || prevp->fmtp()->nextp() || !nodep->fmtp() || nodep->fmtp()->nextp())
return false;
AstSFormatF* pformatp = prevp->fmtp();
if (!pformatp) return false;
AstSFormatF* nformatp = nodep->fmtp();
if (!nformatp) return false;
// We don't merge scopeNames as can have only one and might be different scopes (late in
// process) Also rare for real code to print %m multiple times in same message
if (nformatp->scopeNamep() && pformatp->scopeNamep()) return false;
// We don't early merge arguments as might need to later print warnings with
// right line numbers, nor scopeNames as might be different scopes (late in process)
if (!m_doCpp && pformatp->exprsp()) return false;
if (!m_doCpp && nformatp->exprsp()) return false;
// Avoid huge merges
static constexpr int DISPLAY_MAX_MERGE_LENGTH = 500;
if (pformatp->text().length() + nformatp->text().length() > DISPLAY_MAX_MERGE_LENGTH)
return false;
//
UINFO(9, "DISPLAY(SF({a})) DISPLAY(SF({b})) -> DISPLAY(SF({a}+{b}))" << endl);
// Convert DT_DISPLAY to DT_WRITE as may allow later optimizations
if (prevp->displayType() == AstDisplayType::DT_DISPLAY) {
prevp->displayType(AstDisplayType::DT_WRITE);
pformatp->text(pformatp->text() + "\n");
}
// We can't replace prev() as the edit tracking iterators will get confused.
// So instead we edit the prev note itself.
if (prevp->addNewline()) pformatp->text(pformatp->text() + "\n");
pformatp->text(pformatp->text() + nformatp->text());
if (!prevp->addNewline() && nodep->addNewline()) pformatp->text(pformatp->text() + "\n");
if (nformatp->exprsp()) pformatp->addExprsp(nformatp->exprsp()->unlinkFrBackWithNext());
if (nformatp->scopeNamep())
pformatp->scopeNamep(nformatp->scopeNamep()->unlinkFrBackWithNext());
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return true;
}
virtual void visit(AstSFormatF* nodep) override {
// Substitute constants into displays. The main point of this is to
// simplify assertion methodologies which call functions with display's.
// This eliminates a pile of wide temps, and makes the C a whole lot more readable.
iterateChildren(nodep);
bool anyconst = false;
for (AstNode* argp = nodep->exprsp(); argp; argp = argp->nextp()) {
if (VN_IS(argp, Const)) {
anyconst = true;
break;
}
}
if (m_doNConst && anyconst) {
// UINFO(9," Display in "<<nodep->text()<<endl);
string newFormat;
string fmt;
bool inPct = false;
AstNode* argp = nodep->exprsp();
string text = nodep->text();
for (const char ch : text) {
if (!inPct && ch == '%') {
inPct = true;
fmt = ch;
} else if (inPct && (isdigit(ch) || ch == '.' || ch == '-')) {
fmt += ch;
} else if (inPct) {
inPct = false;
fmt += ch;
switch (tolower(ch)) {
case '%': break; // %% - just output a %
case 'm': break; // %m - auto insert "name"
case 'l': break; // %l - auto insert "library"
case 't': // FALLTHRU
case '^': // %t/%^ - don't know $timeformat so can't constify
if (argp) argp = argp->nextp();
break;
default: // Most operators, just move to next argument
if (argp) {
AstNode* nextp = argp->nextp();
if (VN_IS(argp, Const)) { // Convert it
string out = VN_CAST(argp, Const)->num().displayed(nodep, fmt);
UINFO(9, " DispConst: " << fmt << " -> " << out << " for "
<< argp << endl);
// fmt = out w/ replace % with %% as it must be literal.
fmt = VString::quotePercent(out);
VL_DO_DANGLING(argp->unlinkFrBack()->deleteTree(), argp);
}
argp = nextp;
}
break;
} // switch
newFormat += fmt;
} else {
newFormat += ch;
}
}
if (newFormat != nodep->text()) {
nodep->text(newFormat);
UINFO(9, " Display out " << nodep << endl);
}
}
if (!nodep->exprsp() && nodep->name().find('%') == string::npos && !nodep->hidden()) {
// Just a simple constant string - the formatting is pointless
VL_DO_DANGLING(replaceConstString(nodep, nodep->name()), nodep);
}
}
virtual void visit(AstFuncRef* nodep) override {
iterateChildren(nodep);
if (m_params) { // Only parameters force us to do constant function call propagation
replaceWithSimulation(nodep);
}
}
virtual void visit(AstArg* nodep) override {
// replaceWithSimulation on the Arg's parent FuncRef replaces these
iterateChildren(nodep);
}
virtual void visit(AstWhile* nodep) override {
bool oldHasJumpDelay = m_hasJumpDelay;
m_hasJumpDelay = false;
{ iterateChildren(nodep); }
bool thisWhileHasJumpDelay = m_hasJumpDelay;
m_hasJumpDelay = thisWhileHasJumpDelay || oldHasJumpDelay;
if (m_doNConst) {
if (nodep->condp()->isZero()) {
UINFO(4, "WHILE(0) => nop " << nodep << endl);
if (nodep->precondsp()) {
nodep->replaceWith(nodep->precondsp());
} else {
nodep->unlinkFrBack();
}
VL_DO_DANGLING(nodep->deleteTree(), nodep);
} else if (nodep->condp()->isNeqZero()) {
if (!thisWhileHasJumpDelay) {
nodep->v3warn(INFINITELOOP, "Infinite loop (condition always true)");
nodep->fileline()->modifyWarnOff(V3ErrorCode::INFINITELOOP,
true); // Complain just once
}
} else if (operandBoolShift(nodep->condp())) {
replaceBoolShift(nodep->condp());
}
}
}
virtual void visit(AstInitArray* nodep) override { iterateChildren(nodep); }
virtual void visit(AstInitItem* nodep) override { iterateChildren(nodep); }
virtual void visit(AstUnbounded* nodep) override { iterateChildren(nodep); }
// These are converted by V3Param. Don't constify as we don't want the
// from() VARREF to disappear, if any.
// If output of a presel didn't get consted, chances are V3Param didn't visit properly
virtual void visit(AstNodePreSel*) override {}
// Ignored, can eliminate early
virtual void visit(AstSysIgnore* nodep) override {
iterateChildren(nodep);
if (m_doNConst) { VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); }
}
// Simplify
virtual void visit(AstBasicDType* nodep) override {
iterateChildren(nodep);
nodep->cvtRangeConst();
}
//-----
// Jump elimination
virtual void visit(AstDelay* nodep) override {
iterateChildren(nodep);
m_hasJumpDelay = true;
}
virtual void visit(AstJumpGo* nodep) override {
iterateChildren(nodep);
// Jump to label where label immediately follows label is not useful
if (nodep->labelp() == VN_CAST(nodep->nextp(), JumpLabel)) {
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
// Keep the label, might be other jumps pointing to it, gets cleaned later
return;
}
if (m_doExpensive) {
// Any non-label statements (at this statement level) can never execute
while (nodep->nextp() && !VN_IS(nodep->nextp(), JumpLabel)) {
nodep->nextp()->unlinkFrBack()->deleteTree();
}
// If last statement in a jump label we have JumpLabel(...., JumpGo)
// Often caused by "return" in a Verilog function. The Go is pointless, remove.
if (!nodep->nextp()) {
if (AstJumpBlock* aboveBlockp = VN_CAST(nodep->abovep(), JumpBlock)) {
if (aboveBlockp == nodep->labelp()->blockp()) {
if (aboveBlockp->endStmtsp() == nodep->labelp()) {
UINFO(4, "JUMPGO => last remove " << nodep << endl);
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
}
}
}
nodep->labelp()->blockp()->user4(true);
}
m_hasJumpDelay = true;
}
virtual void visit(AstJumpBlock* nodep) override {
// Because JumpLabels disable many optimizations,
// remove JumpLabels that are not pointed to by any AstJumpGos
// Note this assumes all AstJumpGos are underneath the given label; V3Broken asserts this
iterateChildren(nodep);
// AstJumpGo's below here that point to this node will set user4
if (m_doExpensive && !nodep->user4()) {
UINFO(4, "JUMPLABEL => unused " << nodep << endl);
AstNode* underp = nullptr;
if (nodep->stmtsp()) underp = nodep->stmtsp()->unlinkFrBackWithNext();
if (underp) {
nodep->replaceWith(underp);
} else {
nodep->unlinkFrBack();
}
nodep->labelp()->unlinkFrBack()->deleteTree();
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
}
//-----
// Below lines are magic expressions processed by astgen
// TREE_SKIP_VISIT("AstNODETYPE") # Rename normal visit to visitGen and don't iterate
//-----
// clang-format off
TREE_SKIP_VISIT("ArraySel");
//-----
// "AstNODETYPE { # bracket not paren
// $accessor_name, ...
// # .castFoo is the test VN_IS(object,Foo)
// # ,, gets replaced with a , rather than &&
// }" # bracket not paren
// ,"what to call"
//
// Where "what_to_call" is:
// "function to call"
// "AstREPLACEMENT_TYPE{ $accessor }"
// "! # Print line number when matches, so can see operations
// "NEVER" # Print error message
// "DONE" # Process of matching did the transform already
// In the future maybe support more complicated match & replace:
// ("AstOr {%a, AstAnd{AstNot{%b}, %c}} if %a.width1 if %a==%b", "AstOr{%a,%c}; %b.delete");
// Lhs/rhs would be implied; for non math operations you'd need $lhsp etc.
// v--- * * This op done on Verilog or C+++ mode, in all non-m_doConst stages
// v--- *1* These ops are always first, as we warn before replacing
// v--- *C* This op is a (C)++ op, only in m_doCpp mode
// v--- *V* This op is a (V)erilog op, only in m_doV mode
// v--- *A* This op works on (A)ll constant children, allowed in m_doConst mode
// v--- *S* This op specifies a type should use (S)hort-circuiting of its lhs op
TREEOP1("AstSel{warnSelect(nodep)}", "NEVER");
// Generic constants on both side. Do this first to avoid other replacements
TREEOPA("AstNodeBiop {$lhsp.castConst, $rhsp.castConst, nodep->isPredictOptimizable()}", "replaceConst(nodep)");
TREEOPA("AstNodeUniop{$lhsp.castConst, !nodep->isOpaque(), nodep->isPredictOptimizable()}", "replaceConst(nodep)");
TREEOPA("AstNodeQuadop{$lhsp.castConst, $rhsp.castConst, $thsp.castConst, $fhsp.castConst}", "replaceConst(nodep)");
// Zero on one side or the other
TREEOP ("AstAdd {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
TREEOP ("AstAnd {$lhsp.isZero, $rhsp, isTPure($rhsp)}", "replaceZero(nodep)"); // Can't use replaceZeroChkPure as we make this pattern in ChkPure
// This visit function here must allow for short-circuiting.
TREEOPS("AstLogAnd {$lhsp.isZero}", "replaceZero(nodep)");
TREEOP ("AstLogAnd{$lhsp.isZero, $rhsp}", "replaceZero(nodep)");
// This visit function here must allow for short-circuiting.
TREEOPS("AstLogOr {$lhsp.isOne}", "replaceNum(nodep, 1)");
TREEOP ("AstLogOr {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
TREEOP ("AstDiv {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
TREEOP ("AstDivS {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
TREEOP ("AstMul {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
TREEOP ("AstMulS {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
TREEOP ("AstPow {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule
TREEOP ("AstPowSS {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule
TREEOP ("AstPowSU {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule
TREEOP ("AstPowUS {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule
TREEOP ("AstPow {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)");
TREEOP ("AstPowSU {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)");
TREEOP ("AstPowUS {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)");
TREEOP ("AstPowSU {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)");
TREEOP ("AstOr {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
TREEOP ("AstShiftL{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
TREEOP ("AstShiftR{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
TREEOP ("AstShiftRS{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
TREEOP ("AstXor {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
TREEOP ("AstSub {$lhsp.isZero, $rhsp}", "AstNegate{$rhsp}");
TREEOP ("AstAdd {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
TREEOP ("AstAnd {$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)");
TREEOP ("AstLogAnd{$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)");
TREEOP ("AstLogOr {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
TREEOP ("AstMul {$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)");
TREEOP ("AstMulS {$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)");
TREEOP ("AstOr {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
TREEOP ("AstShiftL{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
TREEOP ("AstShiftR{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
TREEOP ("AstShiftRS{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
TREEOP ("AstSub {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
TREEOP ("AstXor {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
// Non-zero on one side or the other
TREEOP ("AstAnd {$lhsp.isAllOnes, $rhsp}", "replaceWRhs(nodep)");
TREEOP ("AstLogAnd{$lhsp.isNeqZero, $rhsp}", "replaceWRhs(nodep)");
TREEOP ("AstOr {$lhsp.isAllOnes, $rhsp, isTPure($rhsp)}", "replaceWLhs(nodep)"); //->allOnes
TREEOP ("AstLogOr {$lhsp.isNeqZero, $rhsp}", "replaceNum(nodep,1)");
TREEOP ("AstAnd {$lhsp, $rhsp.isAllOnes}", "replaceWLhs(nodep)");
TREEOP ("AstLogAnd{$lhsp, $rhsp.isNeqZero}", "replaceWLhs(nodep)");
TREEOP ("AstOr {$lhsp, $rhsp.isAllOnes, isTPure($lhsp)}", "replaceWRhs(nodep)"); //->allOnes
TREEOP ("AstLogOr {$lhsp, $rhsp.isNeqZero, isTPure($lhsp)}", "replaceNum(nodep,1)");
TREEOP ("AstXor {$lhsp.isAllOnes, $rhsp}", "AstNot{$rhsp}");
TREEOP ("AstMul {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)");
TREEOP ("AstMulS {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)");
TREEOP ("AstDiv {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)");
TREEOP ("AstDivS {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)");
TREEOP ("AstMul {operandIsPowTwo($lhsp), operandsSameSize($lhsp,,$rhsp)}", "replaceMulShift(nodep)"); // a*2^n -> a<<n
TREEOP ("AstDiv {$lhsp, operandIsPowTwo($rhsp)}", "replaceDivShift(nodep)"); // a/2^n -> a>>n
TREEOP ("AstModDiv{$lhsp, operandIsPowTwo($rhsp)}", "replaceModAnd(nodep)"); // a % 2^n -> a&(2^n-1)
TREEOP ("AstPow {operandIsTwo($lhsp), $rhsp}", "replacePowShift(nodep)"); // 2**a == 1<<a
TREEOP ("AstSub {$lhsp.castAdd, operandSubAdd(nodep)}", "AstAdd{AstSub{$lhsp->castAdd()->lhsp(),$rhsp}, $lhsp->castAdd()->rhsp()}"); // ((a+x)-y) -> (a+(x-y))
// Trinary ops
// Note V3Case::Sel requires Cond to always be conditionally executed in C to prevent core dump!
TREEOP ("AstNodeCond{$condp.isZero, $expr1p, $expr2p}", "replaceWChild(nodep,$expr2p)");
TREEOP ("AstNodeCond{$condp.isNeqZero, $expr1p, $expr2p}", "replaceWChild(nodep,$expr1p)");
TREEOPA("AstNodeCond{$condp.isZero, $expr1p.castConst, $expr2p.castConst}", "replaceWChild(nodep,$expr2p)");
TREEOPA("AstNodeCond{$condp.isNeqZero, $expr1p.castConst, $expr2p.castConst}", "replaceWChild(nodep,$expr1p)");
TREEOP ("AstNodeCond{$condp, operandsSame($expr1p,,$expr2p)}","replaceWChild(nodep,$expr1p)");
// This visit function here must allow for short-circuiting.
TREEOPS("AstCond {$lhsp.isZero}", "replaceWIteratedThs(nodep)");
TREEOPS("AstCond {$lhsp.isNeqZero}", "replaceWIteratedRhs(nodep)");
TREEOP ("AstCond{$condp.castNot, $expr1p, $expr2p}", "AstCond{$condp->op1p(), $expr2p, $expr1p}");
TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p.isAllOnes, $expr2p}", "AstLogOr {$condp, $expr2p}"); // a?1:b == a||b
TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p, $expr2p.isZero}", "AstLogAnd{$condp, $expr1p}"); // a?b:0 == a&&b
TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p, $expr2p.isAllOnes}", "AstLogOr {AstNot{$condp}, $expr1p}"); // a?b:1 == ~a||b
TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p.isZero, $expr2p}", "AstLogAnd{AstNot{$condp}, $expr2p}"); // a?0:b == ~a&&b
TREEOP ("AstNodeCond{!$condp.width1, operandBoolShift(nodep->condp())}", "replaceBoolShift(nodep->condp())");
// Prefer constants on left, since that often needs a shift, it lets
// constant red remove the shift
TREEOP ("AstNodeBiCom{!$lhsp.castConst, $rhsp.castConst}", "swapSides(nodep)");
TREEOP ("AstNodeBiComAsv{operandAsvConst(nodep)}", "replaceAsv(nodep)");
TREEOP ("AstNodeBiComAsv{operandAsvSame(nodep)}", "replaceAsv(nodep)");
TREEOP ("AstNodeBiComAsv{operandAsvLUp(nodep)}", "replaceAsvLUp(nodep)");
TREEOP ("AstNodeBiComAsv{operandAsvRUp(nodep)}", "replaceAsvRUp(nodep)");
TREEOP ("AstLt {!$lhsp.castConst,$rhsp.castConst}", "AstGt {$rhsp,$lhsp}");
TREEOP ("AstLtS {!$lhsp.castConst,$rhsp.castConst}", "AstGtS {$rhsp,$lhsp}");
TREEOP ("AstLte {!$lhsp.castConst,$rhsp.castConst}", "AstGte {$rhsp,$lhsp}");
TREEOP ("AstLteS {!$lhsp.castConst,$rhsp.castConst}", "AstGteS{$rhsp,$lhsp}");
TREEOP ("AstGt {!$lhsp.castConst,$rhsp.castConst}", "AstLt {$rhsp,$lhsp}");
TREEOP ("AstGtS {!$lhsp.castConst,$rhsp.castConst}", "AstLtS {$rhsp,$lhsp}");
TREEOP ("AstGte {!$lhsp.castConst,$rhsp.castConst}", "AstLte {$rhsp,$lhsp}");
TREEOP ("AstGteS {!$lhsp.castConst,$rhsp.castConst}", "AstLteS{$rhsp,$lhsp}");
// v--- *1* as These ops are always first, as we warn before replacing
TREEOP1("AstLt {$lhsp, $rhsp.isZero}", "replaceNumSigned(nodep,0)");
TREEOP1("AstGte {$lhsp, $rhsp.isZero}", "replaceNumSigned(nodep,1)");
TREEOP1("AstGt {$lhsp.isZero, $rhsp}", "replaceNumSigned(nodep,0)");
TREEOP1("AstLte {$lhsp.isZero, $rhsp}", "replaceNumSigned(nodep,1)");
TREEOP1("AstGt {$lhsp, $rhsp.isAllOnes, $lhsp->width()==$rhsp->width()}", "replaceNumLimited(nodep,0)");
TREEOP1("AstLte {$lhsp, $rhsp.isAllOnes, $lhsp->width()==$rhsp->width()}", "replaceNumLimited(nodep,1)");
TREEOP1("AstLt {$lhsp.isAllOnes, $rhsp, $lhsp->width()==$rhsp->width()}", "replaceNumLimited(nodep,0)");
TREEOP1("AstGte {$lhsp.isAllOnes, $rhsp, $lhsp->width()==$rhsp->width()}", "replaceNumLimited(nodep,1)");
// Two level bubble pushing
TREEOP ("AstNot {$lhsp.castNot, $lhsp->width()==VN_CAST($lhsp,,Not)->lhsp()->width()}", "replaceWChild(nodep, $lhsp->op1p())"); // NOT(NOT(x))->x
TREEOP ("AstLogNot{$lhsp.castLogNot}", "replaceWChild(nodep, $lhsp->op1p())"); // LOGNOT(LOGNOT(x))->x
TREEOPV("AstNot {$lhsp.castEqCase, $lhsp.width1}","AstNeqCase{$lhsp->op1p(),$lhsp->op2p()}");
TREEOP ("AstLogNot{$lhsp.castEqCase}", "AstNeqCase{$lhsp->op1p(),$lhsp->op2p()}");
TREEOPV("AstNot {$lhsp.castNeqCase, $lhsp.width1}","AstEqCase {$lhsp->op1p(),$lhsp->op2p()}");
TREEOP ("AstLogNot{$lhsp.castNeqCase}", "AstEqCase {$lhsp->op1p(),$lhsp->op2p()}");
TREEOPV("AstNot {$lhsp.castEqWild, $lhsp.width1}","AstNeqWild{$lhsp->op1p(),$lhsp->op2p()}");
TREEOP ("AstLogNot{$lhsp.castEqWild}", "AstNeqWild{$lhsp->op1p(),$lhsp->op2p()}");
TREEOPV("AstNot {$lhsp.castNeqWild, $lhsp.width1}","AstEqWild {$lhsp->op1p(),$lhsp->op2p()}");
TREEOP ("AstLogNot{$lhsp.castNeqWild}", "AstEqWild {$lhsp->op1p(),$lhsp->op2p()}");
TREEOPV("AstNot {$lhsp.castEq, $lhsp.width1}", "AstNeq {$lhsp->op1p(),$lhsp->op2p()}");
TREEOP ("AstLogNot{$lhsp.castEq}", "AstNeq {$lhsp->op1p(),$lhsp->op2p()}");
TREEOPV("AstNot {$lhsp.castNeq, $lhsp.width1}", "AstEq {$lhsp->op1p(),$lhsp->op2p()}");
TREEOP ("AstLogNot{$lhsp.castNeq}", "AstEq {$lhsp->op1p(),$lhsp->op2p()}");
TREEOPV("AstNot {$lhsp.castLt, $lhsp.width1}", "AstGte {$lhsp->op1p(),$lhsp->op2p()}");
TREEOP ("AstLogNot{$lhsp.castLt}", "AstGte {$lhsp->op1p(),$lhsp->op2p()}");
TREEOPV("AstNot {$lhsp.castLtS, $lhsp.width1}", "AstGteS{$lhsp->op1p(),$lhsp->op2p()}");
TREEOP ("AstLogNot{$lhsp.castLtS}", "AstGteS{$lhsp->op1p(),$lhsp->op2p()}");
TREEOPV("AstNot {$lhsp.castLte, $lhsp.width1}", "AstGt {$lhsp->op1p(),$lhsp->op2p()}");
TREEOP ("AstLogNot{$lhsp.castLte}", "AstGt {$lhsp->op1p(),$lhsp->op2p()}");
TREEOPV("AstNot {$lhsp.castLteS, $lhsp.width1}", "AstGtS {$lhsp->op1p(),$lhsp->op2p()}");
TREEOP ("AstLogNot{$lhsp.castLteS}", "AstGtS {$lhsp->op1p(),$lhsp->op2p()}");
TREEOPV("AstNot {$lhsp.castGt, $lhsp.width1}", "AstLte {$lhsp->op1p(),$lhsp->op2p()}");
TREEOP ("AstLogNot{$lhsp.castGt}", "AstLte {$lhsp->op1p(),$lhsp->op2p()}");
TREEOPV("AstNot {$lhsp.castGtS, $lhsp.width1}", "AstLteS{$lhsp->op1p(),$lhsp->op2p()}");
TREEOP ("AstLogNot{$lhsp.castGtS}", "AstLteS{$lhsp->op1p(),$lhsp->op2p()}");
TREEOPV("AstNot {$lhsp.castGte, $lhsp.width1}", "AstLt {$lhsp->op1p(),$lhsp->op2p()}");
TREEOP ("AstLogNot{$lhsp.castGte}", "AstLt {$lhsp->op1p(),$lhsp->op2p()}");
TREEOPV("AstNot {$lhsp.castGteS, $lhsp.width1}", "AstLtS {$lhsp->op1p(),$lhsp->op2p()}");
TREEOP ("AstLogNot{$lhsp.castGteS}", "AstLtS {$lhsp->op1p(),$lhsp->op2p()}");
// Not common, but avoids compiler warnings about over shifting
TREEOP ("AstShiftL{operandHugeShiftL(nodep)}", "replaceZero(nodep)");
TREEOP ("AstShiftR{operandHugeShiftR(nodep)}", "replaceZero(nodep)");
TREEOP ("AstShiftL{operandShiftOp(nodep)}", "replaceShiftOp(nodep)");
TREEOP ("AstShiftR{operandShiftOp(nodep)}", "replaceShiftOp(nodep)");
TREEOP ("AstShiftL{operandShiftShift(nodep)}", "replaceShiftShift(nodep)");
TREEOP ("AstShiftR{operandShiftShift(nodep)}", "replaceShiftShift(nodep)");
TREEOP ("AstWordSel{operandWordOOB(nodep)}", "replaceZero(nodep)");
// Compress out EXTENDs to appease loop unroller
TREEOPV("AstEq {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE");
TREEOPV("AstNeq {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE");
TREEOPV("AstGt {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE");
TREEOPV("AstGte {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE");
TREEOPV("AstLt {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE");
TREEOPV("AstLte {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE");
TREEOPV("AstEq {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceZero(nodep)");
TREEOPV("AstNeq {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceNum(nodep,1)");
TREEOPV("AstGt {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceNum(nodep,1)");
TREEOPV("AstGte {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceNum(nodep,1)");
TREEOPV("AstLt {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceZero(nodep)");
TREEOPV("AstLte {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceZero(nodep)");
// Identical operands on both sides
// AstLogAnd/AstLogOr already converted to AstAnd/AstOr for these rules
// AstAdd->ShiftL(#,1) but uncommon
TREEOP ("AstAnd {operandsSame($lhsp,,$rhsp)}", "replaceWLhs(nodep)");
TREEOP ("AstChangeXor{operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
TREEOP ("AstDiv {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
TREEOP ("AstDivS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
TREEOP ("AstOr {operandsSame($lhsp,,$rhsp)}", "replaceWLhs(nodep)");
TREEOP ("AstSub {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
TREEOP ("AstXor {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
TREEOP ("AstEq {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); // We let X==X -> 1, although in a true 4-state sim it's X.
TREEOP ("AstEqD {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); // We let X==X -> 1, although in a true 4-state sim it's X.
TREEOP ("AstEqN {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); // We let X==X -> 1, although in a true 4-state sim it's X.
TREEOP ("AstEqCase {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
TREEOP ("AstEqWild {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
TREEOP ("AstGt {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
TREEOP ("AstGtD {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
TREEOP ("AstGtN {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
TREEOP ("AstGtS {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
TREEOP ("AstGte {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
TREEOP ("AstGteD {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
TREEOP ("AstGteN {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
TREEOP ("AstGteS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
TREEOP ("AstLt {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
TREEOP ("AstLtD {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
TREEOP ("AstLtN {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
TREEOP ("AstLtS {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
TREEOP ("AstLte {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
TREEOP ("AstLteD {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
TREEOP ("AstLteN {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
TREEOP ("AstLteS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
TREEOP ("AstNeq {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
TREEOP ("AstNeqD {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
TREEOP ("AstNeqN {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
TREEOP ("AstNeqCase{operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
TREEOP ("AstNeqWild{operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
TREEOP ("AstLogAnd {operandsSame($lhsp,,$rhsp), $lhsp.width1}", "replaceWLhs(nodep)");
TREEOP ("AstLogOr {operandsSame($lhsp,,$rhsp), $lhsp.width1}", "replaceWLhs(nodep)");
///=== Verilog operators
// Comparison against 1'b0/1'b1; must be careful about widths.
// These use Not, so must be Verilog only
TREEOPV("AstEq {$rhsp.width1, $lhsp.isZero, $rhsp}", "AstNot{$rhsp}");
TREEOPV("AstEq {$lhsp.width1, $lhsp, $rhsp.isZero}", "AstNot{$lhsp}");
TREEOPV("AstEq {$rhsp.width1, $lhsp.isAllOnes, $rhsp}", "replaceWRhs(nodep)");
TREEOPV("AstEq {$lhsp.width1, $lhsp, $rhsp.isAllOnes}", "replaceWLhs(nodep)");
TREEOPV("AstNeq {$rhsp.width1, $lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
TREEOPV("AstNeq {$lhsp.width1, $lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
TREEOPV("AstNeq {$rhsp.width1, $lhsp.isAllOnes, $rhsp}", "AstNot{$rhsp}");
TREEOPV("AstNeq {$lhsp.width1, $lhsp, $rhsp.isAllOnes}", "AstNot{$lhsp}");
TREEOPV("AstLt {$rhsp.width1, $lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); // Because not signed #s
TREEOPV("AstGt {$lhsp.width1, $lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); // Because not signed #s
// Useful for CONDs added around ARRAYSEL's in V3Case step
TREEOPV("AstLte {$lhsp->width()==$rhsp->width(), $rhsp.isAllOnes}", "replaceNum(nodep,1)");
// Simplify reduction operators
// This also gets &{...,0,....} => const 0 (Common for unused_ok signals)
TREEOPV("AstRedAnd{$lhsp, $lhsp.width1}", "replaceWLhs(nodep)");
TREEOPV("AstRedOr {$lhsp, $lhsp.width1}", "replaceWLhs(nodep)");
TREEOPV("AstRedXor{$lhsp, $lhsp.width1}", "replaceWLhs(nodep)");
TREEOPV("AstRedAnd{$lhsp.castConcat}", "AstAnd{AstRedAnd{$lhsp->castConcat()->lhsp()}, AstRedAnd{$lhsp->castConcat()->rhsp()}}"); // &{a,b} => {&a}&{&b}
TREEOPV("AstRedOr {$lhsp.castConcat}", "AstOr {AstRedOr {$lhsp->castConcat()->lhsp()}, AstRedOr {$lhsp->castConcat()->rhsp()}}"); // |{a,b} => {|a}|{|b}
TREEOPV("AstRedXor{$lhsp.castConcat}", "AstXor{AstRedXor{$lhsp->castConcat()->lhsp()}, AstRedXor{$lhsp->castConcat()->rhsp()}}"); // ^{a,b} => {^a}^{^b}
TREEOPV("AstRedAnd{$lhsp.castExtend, $lhsp->width() > VN_CAST($lhsp,,Extend)->lhsp()->width()}", "replaceZero(nodep)"); // &{0,...} => 0 Prevents compiler limited range error
TREEOPV("AstRedOr {$lhsp.castExtend}", "AstRedOr {$lhsp->castExtend()->lhsp()}");
TREEOPV("AstRedXor{$lhsp.castExtend}", "AstRedXor{$lhsp->castExtend()->lhsp()}");
TREEOPV("AstOneHot{$lhsp.width1}", "replaceWLhs(nodep)");
TREEOPV("AstOneHot0{$lhsp.width1}", "replaceNum(nodep,1)");
// Binary AND/OR is faster than logical and/or (usually)
TREEOPV("AstLogAnd{$lhsp.width1, $rhsp.width1, isTPure($lhsp), isTPure($rhsp)}", "AstAnd{$lhsp,$rhsp}");
TREEOPV("AstLogOr {$lhsp.width1, $rhsp.width1, isTPure($lhsp), isTPure($rhsp)}", "AstOr{$lhsp,$rhsp}");
TREEOPV("AstLogNot{$lhsp.width1, isTPure($lhsp)}", "AstNot{$lhsp}");
// CONCAT(CONCAT({a},{b}),{c}) -> CONCAT({a},CONCAT({b},{c}))
// CONCAT({const},CONCAT({const},{c})) -> CONCAT((constifiedCONC{const|const},{c}))
TREEOPV("AstConcat{operandConcatMove(nodep)}", "moveConcat(nodep)");
TREEOPV("AstConcat{$lhsp.isZero, $rhsp}", "replaceExtend(nodep, nodep->rhsp())");
// CONCAT(a[1],a[0]) -> a[1:0]
TREEOPV("AstConcat{$lhsp.castSel, $rhsp.castSel, ifAdjacentSel(VN_CAST($lhsp,,Sel),,VN_CAST($rhsp,,Sel))}", "replaceConcatSel(nodep)");
TREEOPV("AstConcat{ifConcatMergeableBiop($lhsp), concatMergeable($lhsp,,$rhsp)}", "replaceConcatMerge(nodep)");
// Common two-level operations that can be simplified
TREEOP ("AstAnd {$lhsp.castConst,matchAndCond(nodep)}", "DONE");
TREEOP ("AstAnd {$lhsp.castOr, $rhsp.castOr, operandAndOrSame(nodep)}", "replaceAndOr(nodep)");
TREEOP ("AstOr {$lhsp.castAnd,$rhsp.castAnd,operandAndOrSame(nodep)}", "replaceAndOr(nodep)");
TREEOP ("AstOr {matchOrAndNot(nodep)}", "DONE");
TREEOP ("AstAnd {operandShiftSame(nodep)}", "replaceShiftSame(nodep)");
TREEOP ("AstOr {operandShiftSame(nodep)}", "replaceShiftSame(nodep)");
TREEOP ("AstXor {operandShiftSame(nodep)}", "replaceShiftSame(nodep)");
// Note can't simplify a extend{extends}, extends{extend}, as the sign
// bits end up in the wrong places
TREEOPV("AstExtend {$lhsp.castExtend}", "replaceExtend(nodep, VN_CAST(nodep->lhsp(), Extend)->lhsp())");
TREEOPV("AstExtendS{$lhsp.castExtendS}", "replaceExtend(nodep, VN_CAST(nodep->lhsp(), ExtendS)->lhsp())");
TREEOPV("AstReplicate{$lhsp, $rhsp.isOne, $lhsp->width()==nodep->width()}", "replaceWLhs(nodep)"); // {1{lhs}}->lhs
TREEOPV("AstReplicateN{$lhsp, $rhsp.isOne, $lhsp->width()==nodep->width()}", "replaceWLhs(nodep)"); // {1{lhs}}->lhs
TREEOPV("AstReplicate{$lhsp.castReplicate, operandRepRep(nodep)}", "DONE"); // {2{3{lhs}}}->{6{lhs}}
TREEOPV("AstConcat{operandConcatSame(nodep)}", "DONE"); // {a,a}->{2{a}}, {a,2{a}}->{3{a}, etc
// Next rule because AUTOINST puts the width of bits in
// to pins, even when the widths are exactly the same across the hierarchy.
TREEOPV("AstSel{operandSelExtend(nodep)}", "DONE");
TREEOPV("AstSel{operandSelFull(nodep)}", "replaceWChild(nodep, nodep->fromp())");
TREEOPV("AstSel{$fromp.castSel}", "replaceSelSel(nodep)");
TREEOPV("AstSel{$fromp.castAdd, operandSelBiLower(nodep)}", "DONE");
TREEOPV("AstSel{$fromp.castAnd, operandSelBiLower(nodep)}", "DONE");
TREEOPV("AstSel{$fromp.castOr, operandSelBiLower(nodep)}", "DONE");
TREEOPV("AstSel{$fromp.castSub, operandSelBiLower(nodep)}", "DONE");
TREEOPV("AstSel{$fromp.castXor, operandSelBiLower(nodep)}", "DONE");
TREEOPV("AstSel{$fromp.castShiftR, operandSelShiftLower(nodep)}", "DONE");
TREEOPA("AstSel{$fromp.castConst, $lsbp.castConst, $widthp.castConst, }", "replaceConst(nodep)");
TREEOPV("AstSel{$fromp.castConcat, $lsbp.castConst, $widthp.castConst, }", "replaceSelConcat(nodep)");
TREEOPV("AstSel{$fromp.castReplicate, $lsbp.castConst, $widthp.castConst, operandSelReplicate(nodep) }", "DONE");
// V3Tristate requires selects below BufIf1.
// Also do additional operators that are bit-independent, but only definite
// win if bit select is a constant (otherwise we may need to compute bit index several times)
TREEOPV("AstSel{$fromp.castBufIf1}", "replaceSelIntoBiop(nodep)");
TREEOPV("AstSel{$fromp.castNot}", "replaceSelIntoUniop(nodep)");
TREEOPV("AstSel{$fromp.castAnd,$lhsp.castConst}", "replaceSelIntoUniop(nodep)");
TREEOPV("AstSel{$fromp.castOr,$lhsp.castConst}", "replaceSelIntoUniop(nodep)");
TREEOPV("AstSel{$fromp.castXor,$lhsp.castConst}", "replaceSelIntoUniop(nodep)");
// This visit function here must allow for short-circuiting.
TREEOPS("AstLogIf{$lhsp.isZero}", "replaceNum(nodep, 1)");
TREEOPV("AstLogIf{$lhsp, $rhsp}", "AstLogOr{AstLogNot{$lhsp},$rhsp}");
TREEOPV("AstLogEq{$lhsp, $rhsp}", "replaceLogEq(nodep)");
// Strings
TREEOPA("AstPutcN{$lhsp.castConst, $rhsp.castConst, $thsp.castConst}", "replaceConst(nodep)");
TREEOPA("AstSubstrN{$lhsp.castConst, $rhsp.castConst, $thsp.castConst}", "replaceConst(nodep)");
TREEOPA("AstCvtPackString{$lhsp.castConst}", "replaceConstString(nodep, VN_CAST(nodep->lhsp(), Const)->num().toString())");
// Custom
// Implied by AstIsUnbounded::numberOperate: V("AstIsUnbounded{$lhsp.castConst}", "replaceNum(nodep, 0)");
TREEOPV("AstIsUnbounded{$lhsp.castUnbounded}", "replaceNum(nodep, 1)");
// clang-format on
// Possible futures:
// (a?(b?y:x):y) -> (a&&!b)?x:y
// (a?(b?x:y):y) -> (a&&b)?x:y
// (a?x:(b?x:y)) -> (a||b)?x:y
// (a?x:(b?y:x)) -> (a||!b)?x:y
// Note we can't convert EqCase/NeqCase to Eq/Neq here because that would break 3'b1x1==3'b101
//-----
virtual void visit(AstNode* nodep) override {
// Default: Just iterate
if (m_required) {
if (VN_IS(nodep, NodeDType) || VN_IS(nodep, Range)) {
// Ignore dtypes for parameter type pins
} else {
nodep->v3error("Expecting expression to be constant, but can't convert a "
<< nodep->prettyTypeName() << " to constant.");
}
} else {
// Calculate the width of this operation
if (m_params && !nodep->width()) nodep = V3Width::widthParamsEdit(nodep);
iterateChildren(nodep);
}
}
public:
// Processing Mode Enum
enum ProcMode : uint8_t {
PROC_PARAMS,
PROC_GENERATE,
PROC_LIVE,
PROC_V_WARN,
PROC_V_NOWARN,
PROC_V_EXPENSIVE,
PROC_CPP
};
// CONSTRUCTORS
explicit ConstVisitor(ProcMode pmode) {
// clang-format off
switch (pmode) {
case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true;
m_required = true; break;
case PROC_GENERATE: m_doV = true; m_doNConst = true; m_params = true;
m_required = true; m_doGenerate = true; break;
case PROC_LIVE: break;
case PROC_V_WARN: m_doV = true; m_doNConst = true; m_warn = true; break;
case PROC_V_NOWARN: m_doV = true; m_doNConst = true; break;
case PROC_V_EXPENSIVE: m_doV = true; m_doNConst = true; m_doExpensive = true; break;
case PROC_CPP: m_doV = false; m_doNConst = true; m_doCpp = true; break;
default: v3fatalSrc("Bad case"); break;
}
// clang-format on
}
virtual ~ConstVisitor() override = default;
AstNode* mainAcceptEdit(AstNode* nodep) {
// Operate starting at a random place
return iterateSubtreeReturnEdits(nodep);
}
};
//######################################################################
// Const class functions
//! Force this cell node's parameter list to become a constant
//! @return Pointer to the edited node.
AstNode* V3Const::constifyParamsEdit(AstNode* nodep) {
// if (debug() > 0) nodep->dumpTree(cout, " forceConPRE : ");
// Resize even if the node already has a width, because buried in the tree
// we may have a node we just created with signing, etc, that isn't sized yet.
// Make sure we've sized everything first
nodep = V3Width::widthParamsEdit(nodep);
ConstVisitor visitor(ConstVisitor::PROC_PARAMS);
if (AstVar* varp = VN_CAST(nodep, Var)) {
// If a var wants to be constified, it's really a param, and
// we want the value to be constant. We aren't passed just the
// init value because we need widthing above to handle the var's type.
if (varp->valuep()) visitor.mainAcceptEdit(varp->valuep());
} else {
nodep = visitor.mainAcceptEdit(nodep);
}
// Because we do edits, nodep links may get trashed and core dump this.
// if (debug() > 0) nodep->dumpTree(cout, " forceConDONE: ");
return nodep;
}
//! Force this cell node's parameter list to become a constant inside generate.
//! If we are inside a generated "if", "case" or "for", we don't want to
//! trigger warnings when we deal with the width. It is possible that these
//! are spurious, existing within sub-expressions that will not actually be
//! generated. Since such occurrences, must be constant, in order to be
//! something a generate block can depend on, we can wait until later to do the
//! width check.
//! @return Pointer to the edited node.
AstNode* V3Const::constifyGenerateParamsEdit(AstNode* nodep) {
// if (debug() > 0) nodep->dumpTree(cout, " forceConPRE : ");
// Resize even if the node already has a width, because buried in the tree
// we may have a node we just created with signing, etc, that isn't sized
// yet.
// Make sure we've sized everything first
nodep = V3Width::widthGenerateParamsEdit(nodep);
ConstVisitor visitor(ConstVisitor::PROC_GENERATE);
if (AstVar* varp = VN_CAST(nodep, Var)) {
// If a var wants to be constified, it's really a param, and
// we want the value to be constant. We aren't passed just the
// init value because we need widthing above to handle the var's type.
if (varp->valuep()) visitor.mainAcceptEdit(varp->valuep());
} else {
nodep = visitor.mainAcceptEdit(nodep);
}
// Because we do edits, nodep links may get trashed and core dump this.
// if (debug() > 0) nodep->dumpTree(cout, " forceConDONE: ");
return nodep;
}
void V3Const::constifyAllLint(AstNetlist* nodep) {
// Only call from Verilator.cpp, as it uses user#'s
UINFO(2, __FUNCTION__ << ": " << endl);
{
ConstVisitor visitor(ConstVisitor::PROC_V_WARN);
(void)visitor.mainAcceptEdit(nodep);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("const", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
void V3Const::constifyCpp(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{
ConstVisitor visitor(ConstVisitor::PROC_CPP);
(void)visitor.mainAcceptEdit(nodep);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("const_cpp", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
AstNode* V3Const::constifyEdit(AstNode* nodep) {
ConstVisitor visitor(ConstVisitor::PROC_V_NOWARN);
nodep = visitor.mainAcceptEdit(nodep);
return nodep;
}
void V3Const::constifyAllLive(AstNetlist* nodep) {
// Only call from Verilator.cpp, as it uses user#'s
// This only pushes constants up, doesn't make any other edits
// IE doesn't prune dead statements, as we need to do some usability checks after this
UINFO(2, __FUNCTION__ << ": " << endl);
{
ConstVisitor visitor(ConstVisitor::PROC_LIVE);
(void)visitor.mainAcceptEdit(nodep);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("const", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
void V3Const::constifyAll(AstNetlist* nodep) {
// Only call from Verilator.cpp, as it uses user#'s
UINFO(2, __FUNCTION__ << ": " << endl);
{
ConstVisitor visitor(ConstVisitor::PROC_V_EXPENSIVE);
(void)visitor.mainAcceptEdit(nodep);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("const", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
AstNode* V3Const::constifyExpensiveEdit(AstNode* nodep) {
ConstVisitor visitor(ConstVisitor::PROC_V_EXPENSIVE);
nodep = visitor.mainAcceptEdit(nodep);
return nodep;
}