2012-04-13 01:08:20 +00:00
|
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Constant folding
|
|
|
|
|
//
|
2008-04-25 12:14:27 +00:00
|
|
|
|
// Code available from: http://www.veripool.org/verilator
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2013-01-01 14:42:59 +00:00
|
|
|
|
// Copyright 2003-2013 by Wilson Snyder. This program is free software; you can
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 21:07:57 +00:00
|
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//
|
|
|
|
|
// Verilator is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// 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.
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
2006-12-18 19:20:45 +00:00
|
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2008-06-30 17:11:25 +00:00
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstdarg>
|
2006-08-26 11:35:28 +00:00
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <map>
|
2009-01-09 16:28:50 +00:00
|
|
|
|
#include <algorithm>
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3Const.h"
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
#include "V3Width.h"
|
2009-07-17 18:13:11 +00:00
|
|
|
|
#include "V3Simulate.h"
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
2008-02-20 16:54:41 +00:00
|
|
|
|
//######################################################################
|
|
|
|
|
// Utilities
|
|
|
|
|
|
|
|
|
|
class ConstVarMarkVisitor : public AstNVisitor {
|
|
|
|
|
// NODE STATE
|
2008-11-22 22:27:55 +00:00
|
|
|
|
// AstVar::user4p -> bool, Var marked, 0=not set yet
|
2008-02-20 16:54:41 +00:00
|
|
|
|
private:
|
|
|
|
|
// VISITORS
|
|
|
|
|
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
2008-11-22 22:27:55 +00:00
|
|
|
|
if (nodep->varp()) nodep->varp()->user4(1);
|
2008-02-20 16:54:41 +00:00
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNode* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
|
|
|
|
ConstVarMarkVisitor(AstNode* nodep) {
|
2008-11-22 22:27:55 +00:00
|
|
|
|
AstNode::user4ClearTree(); // Check marked InUse before we're called
|
2009-10-02 02:33:11 +00:00
|
|
|
|
nodep->accept(*this);
|
2008-02-20 16:54:41 +00:00
|
|
|
|
}
|
|
|
|
|
virtual ~ConstVarMarkVisitor() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class ConstVarFindVisitor : public AstNVisitor {
|
|
|
|
|
// NODE STATE
|
2008-11-22 22:27:55 +00:00
|
|
|
|
// AstVar::user4p -> bool, input from ConstVarMarkVisitor
|
2008-02-20 16:54:41 +00:00
|
|
|
|
// MEMBERS
|
|
|
|
|
bool m_found;
|
|
|
|
|
private:
|
|
|
|
|
// VISITORS
|
|
|
|
|
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
2008-11-22 22:27:55 +00:00
|
|
|
|
if (nodep->varp() && nodep->varp()->user4()) m_found = true;
|
2008-02-20 16:54:41 +00:00
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNode* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
|
|
|
|
ConstVarFindVisitor(AstNode* nodep) {
|
|
|
|
|
m_found = false;
|
|
|
|
|
nodep->iterateAndNext(*this, NULL);
|
|
|
|
|
}
|
|
|
|
|
virtual ~ConstVarFindVisitor() {}
|
|
|
|
|
// METHODS
|
|
|
|
|
bool found() const { return m_found; }
|
|
|
|
|
};
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//######################################################################
|
|
|
|
|
// Const state, as a visitor of each AstNode
|
|
|
|
|
|
|
|
|
|
class ConstVisitor : public AstNVisitor {
|
|
|
|
|
private:
|
2008-02-20 16:54:41 +00:00
|
|
|
|
// NODE STATE
|
2010-12-29 01:46:13 +00:00
|
|
|
|
// ** only when m_warn/m_doExpensive is set. If state is needed other times,
|
2008-02-20 16:54:41 +00:00
|
|
|
|
// ** must track down everywhere V3Const is called and make sure no overlaps.
|
2008-11-22 22:27:55 +00:00
|
|
|
|
// AstVar::user4p -> Used by ConstVarMarkVisitor/ConstVarFindVisitor
|
2010-02-14 15:01:21 +00:00
|
|
|
|
// AstJumpLabel::user4 -> bool. Set when AstJumpGo uses this label
|
2008-02-20 16:54:41 +00:00
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// STATE
|
|
|
|
|
bool m_params; // If true, propogate parameterized and true numbers only
|
|
|
|
|
bool m_required; // If true, must become a constant
|
|
|
|
|
bool m_wremove; // Inside scope, no assignw removal
|
|
|
|
|
bool m_warn; // Output warnings
|
2010-12-29 01:46:13 +00:00
|
|
|
|
bool m_doExpensive; // Enable computationally expensive optimizations
|
2010-12-29 02:23:16 +00:00
|
|
|
|
bool m_doNConst; // Enable non-constant-child simplifications
|
2011-12-22 13:33:16 +00:00
|
|
|
|
bool m_doShort; // Remove expressions that short circuit
|
2010-12-29 02:23:16 +00:00
|
|
|
|
bool m_doV; // Verilog, not C++ conversion
|
2012-04-20 02:53:52 +00:00
|
|
|
|
bool m_doGenerate; // Postpone width checking inside generate
|
2009-11-07 11:20:20 +00:00
|
|
|
|
AstNodeModule* m_modp; // Current module
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstNode* m_scopep; // Current scope
|
2012-07-28 14:52:29 +00:00
|
|
|
|
AstAttrOf* m_attrp; // Current attribute
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
// METHODS
|
2009-01-21 21:56:50 +00:00
|
|
|
|
static int debug() {
|
|
|
|
|
static int level = -1;
|
|
|
|
|
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
|
|
|
|
return level;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
bool operandConst (AstNode* nodep) {
|
|
|
|
|
return (nodep->castConst());
|
|
|
|
|
}
|
|
|
|
|
bool operandAsvConst (AstNode* nodep) {
|
|
|
|
|
// BIASV(CONST, BIASV(CONST,...)) -> BIASV( BIASV_CONSTED(a,b), ...)
|
|
|
|
|
AstNodeBiComAsv* bnodep = nodep->castNodeBiComAsv();
|
|
|
|
|
if (!bnodep) return false;
|
|
|
|
|
if (!bnodep->lhsp()->castConst()) return false;
|
|
|
|
|
AstNodeBiComAsv* rnodep = bnodep->rhsp()->castNodeBiComAsv();
|
|
|
|
|
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 (!rnodep->lhsp()->castConst()) return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2006-09-28 14:37:28 +00:00
|
|
|
|
bool operandAsvSame (AstNode* nodep) {
|
|
|
|
|
// BIASV(SAMEa, BIASV(SAMEb,...)) -> BIASV( BIASV(SAMEa,SAMEb), ...)
|
|
|
|
|
AstNodeBiComAsv* bnodep = nodep->castNodeBiComAsv();
|
|
|
|
|
if (!bnodep) return false;
|
|
|
|
|
AstNodeBiComAsv* rnodep = bnodep->rhsp()->castNodeBiComAsv();
|
|
|
|
|
if (!rnodep) return false;
|
|
|
|
|
if (rnodep->type() != bnodep->type()) return false;
|
|
|
|
|
if (rnodep->width() != bnodep->width()) return false;
|
|
|
|
|
return operandsSame(bnodep->lhsp(), rnodep->lhsp());
|
|
|
|
|
}
|
2008-11-17 15:40:58 +00:00
|
|
|
|
bool operandAsvLUp (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
|
2012-03-20 20:01:53 +00:00
|
|
|
|
// BIASV(CONST_a_c,BIASV(c...,d...)))
|
2008-11-17 15:40:58 +00:00
|
|
|
|
//
|
|
|
|
|
// Idea for the future: All BiComAsvs could be lists, sorted by if they're constant
|
|
|
|
|
AstNodeBiComAsv* bnodep = nodep->castNodeBiComAsv();
|
|
|
|
|
if (!bnodep) return false;
|
|
|
|
|
AstNodeBiComAsv* lnodep = bnodep->lhsp()->castNodeBiComAsv();
|
|
|
|
|
if (!lnodep) return false;
|
|
|
|
|
if (lnodep->type() != bnodep->type()) return false;
|
|
|
|
|
if (lnodep->width() != bnodep->width()) return false;
|
|
|
|
|
return lnodep->lhsp()->castConst();
|
|
|
|
|
}
|
|
|
|
|
bool operandAsvRUp (AstNode* nodep) {
|
|
|
|
|
// BIASV(l,BIASV(CONSTrl,rr)) -> BIASV(CONSTrl,BIASV(l,rr))
|
|
|
|
|
AstNodeBiComAsv* bnodep = nodep->castNodeBiComAsv();
|
|
|
|
|
if (!bnodep) return false;
|
|
|
|
|
AstNodeBiComAsv* rnodep = bnodep->rhsp()->castNodeBiComAsv();
|
|
|
|
|
if (!rnodep) return false;
|
|
|
|
|
if (rnodep->type() != bnodep->type()) return false;
|
|
|
|
|
if (rnodep->width() != bnodep->width()) return false;
|
|
|
|
|
return rnodep->lhsp()->castConst();
|
|
|
|
|
}
|
2008-11-17 16:36:01 +00:00
|
|
|
|
static bool operandAndOrSame(AstNode* nodep) {
|
2012-05-08 23:20:23 +00:00
|
|
|
|
// OR( AND(VAL,x), AND(VAL,y)) -> AND(VAL,OR(x,y))
|
|
|
|
|
// OR( AND(x,VAL), AND(y,VAL)) -> AND(OR(x,y),VAL)
|
2008-11-17 16:36:01 +00:00
|
|
|
|
AstNodeBiop* np = nodep->castNodeBiop();
|
|
|
|
|
AstNodeBiop* lp = np->lhsp()->castNodeBiop();
|
|
|
|
|
AstNodeBiop* rp = np->rhsp()->castNodeBiop();
|
|
|
|
|
return (lp && rp
|
|
|
|
|
&& lp->width()==rp->width()
|
|
|
|
|
&& lp->type()==rp->type()
|
2012-05-08 23:20:23 +00:00
|
|
|
|
&& (operandsSame(lp->lhsp(),rp->lhsp())
|
|
|
|
|
|| operandsSame(lp->rhsp(),rp->rhsp())));
|
2008-11-17 16:36:01 +00:00
|
|
|
|
}
|
2009-01-07 14:37:59 +00:00
|
|
|
|
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 (nodep->lhsp()->castAnd()) { andp=nodep->lhsp()->castAnd(); ap=nodep->rhsp(); }
|
|
|
|
|
else if (nodep->rhsp()->castAnd()) { andp=nodep->rhsp()->castAnd(); ap=nodep->lhsp(); }
|
|
|
|
|
else return false;
|
|
|
|
|
AstNodeUniop* notp;
|
|
|
|
|
AstNode* cp;
|
|
|
|
|
if (andp->lhsp()->castNot()) { notp=andp->lhsp()->castNot(); cp=andp->rhsp(); }
|
|
|
|
|
else if (andp->rhsp()->castNot()) { notp=andp->rhsp()->castNot(); cp=andp->lhsp(); }
|
|
|
|
|
else return false;
|
|
|
|
|
AstNode* bp = notp->lhsp();
|
|
|
|
|
if (!operandsSame(ap, bp)) return false;
|
|
|
|
|
// Do it
|
|
|
|
|
cp->unlinkFrBack();
|
|
|
|
|
andp->unlinkFrBack()->deleteTree(); andp=NULL; notp=NULL;
|
|
|
|
|
// Replace whichever branch is now dangling
|
|
|
|
|
if (nodep->rhsp()) nodep->lhsp(cp);
|
|
|
|
|
else nodep->rhsp(cp);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2008-11-17 16:36:01 +00:00
|
|
|
|
static bool operandShiftSame(AstNode* nodep) {
|
|
|
|
|
AstNodeBiop* np = nodep->castNodeBiop();
|
|
|
|
|
{
|
|
|
|
|
AstShiftL* lp = np->lhsp()->castShiftL();
|
|
|
|
|
AstShiftL* rp = np->rhsp()->castShiftL();
|
|
|
|
|
if (lp && rp) {
|
|
|
|
|
return (lp->width() == rp->width()
|
|
|
|
|
&& lp->lhsp()->width() == rp->lhsp()->width()
|
|
|
|
|
&& operandsSame(lp->rhsp(), rp->rhsp()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
AstShiftR* lp = np->lhsp()->castShiftR();
|
|
|
|
|
AstShiftR* rp = np->rhsp()->castShiftR();
|
|
|
|
|
if (lp && rp) {
|
|
|
|
|
return (lp->width() == rp->width()
|
|
|
|
|
&& lp->lhsp()->width() == rp->lhsp()->width()
|
|
|
|
|
&& operandsSame(lp->rhsp(), rp->rhsp()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
bool operandHugeShiftL(AstNodeBiop* nodep) {
|
|
|
|
|
return (nodep->rhsp()->castConst()
|
2011-12-22 13:33:16 +00:00
|
|
|
|
&& nodep->rhsp()->castConst()->toUInt() >= (uint32_t)(nodep->width())
|
|
|
|
|
&& isTPure(nodep->lhsp()));
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
bool operandHugeShiftR(AstNodeBiop* nodep) {
|
|
|
|
|
return (nodep->rhsp()->castConst()
|
2011-12-22 13:33:16 +00:00
|
|
|
|
&& nodep->rhsp()->castConst()->toUInt() >= (uint32_t)(nodep->lhsp()->width())
|
|
|
|
|
&& isTPure(nodep->lhsp()));
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
bool operandIsTwo(AstNode* nodep) {
|
|
|
|
|
return (nodep->castConst()
|
2008-06-10 01:25:10 +00:00
|
|
|
|
&& nodep->width() <= VL_QUADSIZE
|
2008-09-03 21:40:01 +00:00
|
|
|
|
&& nodep->castConst()->toUQuad()==2);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
bool operandIsTwostate(AstNode* nodep) {
|
|
|
|
|
return (nodep->castConst()
|
|
|
|
|
&& !nodep->castConst()->num().isFourState());
|
|
|
|
|
}
|
2006-09-01 16:53:14 +00:00
|
|
|
|
bool operandIsPowTwo(AstNode* nodep) {
|
|
|
|
|
if (!operandIsTwostate(nodep)) return false;
|
|
|
|
|
return (1==nodep->castConst()->num().countOnes());
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
bool operandShiftOp(AstNodeBiop* nodep) {
|
|
|
|
|
if (!nodep->rhsp()->castConst()) return false;
|
|
|
|
|
AstNodeBiop* lhsp = nodep->lhsp()->castNodeBiop();
|
|
|
|
|
if (!lhsp || !(lhsp->castAnd()||lhsp->castOr()||lhsp->castXor())) 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(AstNodeBiop* nodep) {
|
|
|
|
|
// We could add a AND though.
|
|
|
|
|
AstNodeBiop* lhsp = nodep->lhsp()->castNodeBiop();
|
|
|
|
|
if (!lhsp || !(lhsp->castShiftL()||lhsp->castShiftR())) 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 (!(nodep->rhsp()->castConst() && lhsp->rhsp()->castConst())) return false;
|
|
|
|
|
if (nodep->width()!=lhsp->width()) return false;
|
|
|
|
|
if (nodep->width()!=lhsp->lhsp()->width()) return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
bool operandWordOOB(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 (nodep->rhsp()->castConst()
|
|
|
|
|
&& nodep->fromp()->castNodeVarRef()
|
|
|
|
|
&& !nodep->fromp()->castNodeVarRef()->lvalue()
|
2008-09-04 15:03:46 +00:00
|
|
|
|
&& ((int)(nodep->rhsp()->castConst()->toUInt())
|
2006-08-26 11:35:28 +00:00
|
|
|
|
>= nodep->fromp()->castNodeVarRef()->varp()->widthWords()));
|
|
|
|
|
}
|
|
|
|
|
bool operandSelFull(AstSel* nodep) {
|
|
|
|
|
return (nodep->lsbp()->castConst()
|
|
|
|
|
&& nodep->widthp()->castConst()
|
|
|
|
|
&& nodep->lsbConst()==0
|
|
|
|
|
&& (int)nodep->widthConst()==nodep->fromp()->width()
|
|
|
|
|
&& 1);
|
|
|
|
|
}
|
|
|
|
|
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 = nodep->fromp()->castExtend();
|
2011-03-12 12:45:04 +00:00
|
|
|
|
if (!(m_doV
|
|
|
|
|
&& extendp
|
|
|
|
|
&& nodep->lsbp()->castConst()
|
|
|
|
|
&& nodep->widthp()->castConst()
|
|
|
|
|
&& nodep->lsbConst()==0
|
|
|
|
|
&& (int)nodep->widthConst()==extendp->lhsp()->width()
|
|
|
|
|
)) return false;
|
|
|
|
|
replaceWChild(nodep, extendp->lhsp()); nodep=NULL;
|
|
|
|
|
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 = nodep->fromp()->castNodeBiop();
|
|
|
|
|
if (!(m_doV
|
|
|
|
|
&& bip
|
|
|
|
|
&& nodep->lsbp()->castConst()
|
|
|
|
|
&& nodep->widthp()->castConst()
|
|
|
|
|
&& 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:");
|
|
|
|
|
replaceWChild(nodep, bip); nodep=NULL;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool operandBiExtendConst(AstNodeBiop* nodep) {
|
|
|
|
|
// Loop unrolling likes standalone compares
|
|
|
|
|
// EQ(EXTEND(xx{width3}), const{width32}) -> EQ(xx{3},const{3})
|
|
|
|
|
// Beware that the constant must have zero bits (+ 1 if signed) or compare
|
|
|
|
|
// would be incorrect
|
|
|
|
|
AstExtend* extendp = nodep->lhsp()->castExtend();
|
|
|
|
|
if (!extendp) return false;
|
|
|
|
|
AstNode* smallerp = extendp->lhsp();
|
|
|
|
|
int subsize = smallerp->width();
|
|
|
|
|
AstConst* constp = nodep->rhsp()->castConst();
|
|
|
|
|
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();
|
|
|
|
|
extendp->unlinkFrBack()->deleteTree(); // aka nodep->lhsp.
|
|
|
|
|
nodep->lhsp(smallerp);
|
|
|
|
|
|
|
|
|
|
constp->unlinkFrBack();
|
|
|
|
|
V3Number num (constp->fileline(), subsize);
|
|
|
|
|
num.opAssign(constp->num());
|
|
|
|
|
nodep->rhsp(new AstConst(constp->fileline(), num));
|
|
|
|
|
constp->deleteTree(); constp=NULL;
|
|
|
|
|
if (debug()>=9) nodep->dumpTree(cout,"BI(EXTEND)-ou:");
|
|
|
|
|
return true;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-08-27 15:20:55 +00:00
|
|
|
|
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 && nodep->castComment()) { nodep = nodep->nextp(); }
|
|
|
|
|
return nodep;
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-22 13:33:16 +00:00
|
|
|
|
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 || nodep->castVarRef() || nodep->castConst();
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Extraction checks
|
|
|
|
|
bool warnSelect(AstSel* nodep) {
|
2012-04-25 22:17:10 +00:00
|
|
|
|
if (m_doGenerate) {
|
|
|
|
|
// Never checked yet
|
|
|
|
|
V3Width::widthParamsEdit(nodep);
|
|
|
|
|
nodep->iterateChildren(*this); // May need "constifying"
|
|
|
|
|
}
|
2013-01-13 19:54:12 +00:00
|
|
|
|
// Find range of dtype we are selecting from
|
|
|
|
|
// Similar code in V3Unknown::AstSel
|
2013-01-15 02:49:22 +00:00
|
|
|
|
bool doit = true;
|
2013-01-13 19:54:12 +00:00
|
|
|
|
if (m_warn
|
|
|
|
|
&& nodep->lsbp()->castConst()
|
|
|
|
|
&& nodep->widthp()->castConst()
|
|
|
|
|
&& doit) {
|
2013-01-18 01:29:20 +00:00
|
|
|
|
int maxDeclBit = nodep->declRange().hiMaxSelect()*nodep->declElWidth() + (nodep->declElWidth()-1);
|
2013-01-13 19:54:12 +00:00
|
|
|
|
if (nodep->lsbp()->castConst()->num().isFourState()
|
|
|
|
|
|| nodep->widthp()->castConst()->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());
|
|
|
|
|
}
|
2013-01-15 02:49:22 +00:00
|
|
|
|
else if (nodep->declRange().ranged()
|
|
|
|
|
&& (nodep->msbConst() > maxDeclBit
|
|
|
|
|
|| nodep->lsbConst() > maxDeclBit)) {
|
2013-01-13 19:54:12 +00:00
|
|
|
|
// See also warning in V3Width
|
2013-01-15 02:49:22 +00:00
|
|
|
|
// Must adjust by element width as declRange() is in number of elements
|
2013-01-13 19:54:12 +00:00
|
|
|
|
nodep->v3warn(SELRANGE, "Selection index out of range: "
|
2013-01-15 02:49:22 +00:00
|
|
|
|
<<(nodep->msbConst()/nodep->declElWidth())
|
|
|
|
|
<<":"<<(nodep->lsbConst()/nodep->declElWidth())
|
2013-01-18 01:29:20 +00:00
|
|
|
|
<<" outside "<<nodep->declRange().hiMaxSelect()<<":0"
|
|
|
|
|
<<(nodep->declRange().lo()>=0 ? ""
|
|
|
|
|
:(" (adjusted +"+cvtToStr(-nodep->declRange().lo())
|
2013-01-15 02:49:22 +00:00
|
|
|
|
+" to account for negative lsb)")));
|
|
|
|
|
UINFO(1," Related Raw index is "<<nodep->msbConst()<<":"<<nodep->lsbConst()<<endl);
|
2013-01-13 19:54:12 +00:00
|
|
|
|
// Don't replace with zero, we'll do it later
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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 (node1p->castConst() && node2p->castConst()) {
|
2006-09-27 18:00:53 +00:00
|
|
|
|
return node1p->sameTree(node2p);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2006-09-27 18:00:53 +00:00
|
|
|
|
else if (node1p->castVarRef() && node2p->castVarRef()) {
|
2012-05-08 23:20:23 +00:00
|
|
|
|
// Avoid comparing widthMin's, which results in lost optimization attempts
|
|
|
|
|
// If cleanup sameTree to be smarter, this can be restored.
|
|
|
|
|
//return node1p->sameTree(node2p);
|
2012-11-29 01:18:41 +00:00
|
|
|
|
return node1p->same(node2p);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bool ifSameAssign(AstNodeIf* nodep) {
|
|
|
|
|
AstNodeAssign* ifp = nodep->ifsp()->castNodeAssign();
|
|
|
|
|
AstNodeAssign* elsep = nodep->elsesp()->castNodeAssign();
|
|
|
|
|
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
|
|
|
|
|
AstVarRef* ifvarp = ifp->lhsp()->castVarRef();
|
|
|
|
|
AstVarRef* elsevarp = elsep->lhsp()->castVarRef();
|
|
|
|
|
if (!ifvarp || !elsevarp) return false;
|
|
|
|
|
if (ifvarp->isWide()) return false; // Would need temporaries, so not worth it
|
2012-11-29 01:18:41 +00:00
|
|
|
|
if (!ifvarp->sameTree(elsevarp)) return false;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
bool operandIfIf(AstNodeIf* nodep) {
|
|
|
|
|
if (nodep->elsesp()) return false;
|
|
|
|
|
AstNodeIf* lowerIfp = nodep->ifsp()->castNodeIf();
|
|
|
|
|
if (!lowerIfp || lowerIfp->nextp()) return false;
|
|
|
|
|
if (nodep->type() != lowerIfp->type()) return false;
|
2006-08-27 15:20:55 +00:00
|
|
|
|
if (afterComment(lowerIfp->elsesp())) return false;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//----------------------------------------
|
|
|
|
|
// 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\n");
|
2009-09-17 00:52:52 +00:00
|
|
|
|
if (oldp->castConst() && !oldp->castConst()->num().isFourState()) {
|
|
|
|
|
oldp->v3fatalSrc("Already constant??\n");
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstNode* newp = new AstConst(oldp->fileline(), num);
|
2012-04-29 13:42:17 +00:00
|
|
|
|
newp->dtypeFrom(oldp);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (debug()>5) oldp->dumpTree(cout," const_old: ");
|
|
|
|
|
if (debug()>5) newp->dumpTree(cout," _new: ");
|
|
|
|
|
oldp->replaceWith(newp);
|
|
|
|
|
oldp->deleteTree(); oldp=NULL;
|
|
|
|
|
}
|
|
|
|
|
void replaceNum (AstNode* nodep, uint32_t val) {
|
|
|
|
|
V3Number num (nodep->fileline(), nodep->width(), val);
|
|
|
|
|
replaceNum(nodep, num); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
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 && !(nodep->lhsp()->castConst() && nodep->rhsp()->castConst())) {
|
2010-01-16 01:07:16 +00:00
|
|
|
|
nodep->v3warn(UNSIGNED,"Comparison is constant due to unsigned arithmetic");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
replaceNum(nodep, val); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
void replaceNumLimited(AstNodeBiop* nodep, uint32_t val) {
|
|
|
|
|
// Avoids gcc warning about same
|
2010-01-16 01:07:16 +00:00
|
|
|
|
if (m_warn) nodep->v3warn(CMPCONST,"Comparison is constant due to limited range");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
replaceNum(nodep, val); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
void replaceZero(AstNode* nodep) {
|
|
|
|
|
replaceNum(nodep, 0); nodep=NULL;
|
|
|
|
|
}
|
2011-12-22 13:33:16 +00:00
|
|
|
|
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)) {
|
|
|
|
|
replaceNum(nodep, 0); nodep=NULL;
|
|
|
|
|
} else {
|
|
|
|
|
AstNode* newp = new AstAnd(nodep->fileline(),
|
|
|
|
|
new AstConst(nodep->fileline(), 0),
|
|
|
|
|
checkp->unlinkFrBack());
|
2012-04-29 13:42:17 +00:00
|
|
|
|
newp->dtypeFrom(nodep);
|
2011-12-22 13:33:16 +00:00
|
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-09-27 18:00:53 +00:00
|
|
|
|
void replaceAllOnes (AstNode* nodep) {
|
2007-03-09 15:18:56 +00:00
|
|
|
|
V3Number ones (nodep->fileline(), nodep->width(), 0);
|
|
|
|
|
ones.setMask(nodep->width());
|
2006-09-27 18:00:53 +00:00
|
|
|
|
replaceNum(nodep, ones); nodep=NULL;
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
void replaceConst(AstNodeUniop* nodep) {
|
|
|
|
|
V3Number num (nodep->fileline(), nodep->width());
|
|
|
|
|
nodep->numberOperate(num, nodep->lhsp()->castConst()->num());
|
|
|
|
|
UINFO(4,"UNICONST -> "<<num<<endl);
|
|
|
|
|
replaceNum(nodep, num); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
void replaceConst(AstNodeBiop* nodep) {
|
|
|
|
|
V3Number num (nodep->fileline(), nodep->width());
|
|
|
|
|
nodep->numberOperate(num, nodep->lhsp()->castConst()->num(), nodep->rhsp()->castConst()->num());
|
|
|
|
|
UINFO(4,"BICONST -> "<<num<<endl);
|
|
|
|
|
replaceNum(nodep, num); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
void replaceConst(AstNodeTriop* nodep) {
|
|
|
|
|
V3Number num (nodep->fileline(), nodep->width());
|
|
|
|
|
nodep->numberOperate(num, nodep->lhsp()->castConst()->num(),
|
|
|
|
|
nodep->rhsp()->castConst()->num(),
|
|
|
|
|
nodep->thsp()->castConst()->num());
|
|
|
|
|
UINFO(4,"TRICONST -> "<<num<<endl);
|
|
|
|
|
replaceNum(nodep, num); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-18 01:06:08 +00:00
|
|
|
|
void replaceConstString (AstNode* oldp, const string& num) {
|
|
|
|
|
// Replace oldp node with a constant set to specified value
|
|
|
|
|
UASSERT (oldp, "Null old\n");
|
|
|
|
|
AstNode* newp = new AstConstString(oldp->fileline(), num);
|
|
|
|
|
if (debug()>5) oldp->dumpTree(cout," const_old: ");
|
|
|
|
|
if (debug()>5) newp->dumpTree(cout," _new: ");
|
|
|
|
|
oldp->replaceWith(newp);
|
|
|
|
|
oldp->deleteTree(); oldp=NULL;
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//----------------------------------------
|
|
|
|
|
// Replacement functions.
|
|
|
|
|
// These all take a node and replace it with something else
|
|
|
|
|
|
|
|
|
|
void replaceWChild(AstNode* nodep, AstNode* childp) {
|
|
|
|
|
// NODE(..., CHILD(...)) -> CHILD(...)
|
|
|
|
|
childp->unlinkFrBackWithNext();
|
2012-04-29 13:42:17 +00:00
|
|
|
|
childp->dtypeFrom(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->replaceWith(childp);
|
|
|
|
|
nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
2012-04-20 02:53:52 +00:00
|
|
|
|
|
|
|
|
|
//! Replace a ternary node with its RHS after iterating
|
|
|
|
|
//! Used with short-circuting, where the RHS has not yet been iterated.
|
|
|
|
|
void replaceWIteratedRhs(AstNodeTriop* nodep) {
|
|
|
|
|
if (AstNode *rhsp = nodep->rhsp()) rhsp->iterateAndNext(*this);
|
|
|
|
|
replaceWChild(nodep, nodep->rhsp()); // May have changed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Replace a ternary node with its THS after iterating
|
|
|
|
|
//! Used with short-circuting, where the THS has not yet been iterated.
|
|
|
|
|
void replaceWIteratedThs(AstNodeTriop* nodep) {
|
|
|
|
|
if (AstNode *thsp = nodep->thsp()) thsp->iterateAndNext(*this);
|
|
|
|
|
replaceWChild(nodep, nodep->thsp()); // May have changed
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
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());
|
|
|
|
|
}
|
2006-09-28 14:37:28 +00:00
|
|
|
|
void replaceAsv (AstNodeBiop* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// BIASV(CONSTa, BIASV(CONSTb, c)) -> BIASV( BIASV_CONSTED(a,b), c)
|
2006-09-28 14:37:28 +00:00
|
|
|
|
// BIASV(SAMEa, BIASV(SAMEb, c)) -> BIASV( BIASV(SAMEa,SAMEb), c)
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//nodep->dumpTree(cout, " repAsvConst_old: ");
|
|
|
|
|
AstNode* ap = nodep->lhsp();
|
|
|
|
|
AstNodeBiop* rp = nodep->rhsp()->castNodeBiop();
|
|
|
|
|
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);
|
2006-09-28 14:37:28 +00:00
|
|
|
|
if (rp->lhsp()->castConst() && rp->rhsp()->castConst()) replaceConst(rp);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//nodep->dumpTree(cout, " repAsvConst_new: ");
|
|
|
|
|
}
|
2008-11-17 15:40:58 +00:00
|
|
|
|
void replaceAsvLUp (AstNodeBiop* nodep) {
|
|
|
|
|
// BIASV(BIASV(CONSTll,lr),r) -> BIASV(CONSTll,BIASV(lr,r))
|
|
|
|
|
AstNodeBiop* lp = nodep->lhsp()->unlinkFrBack()->castNodeBiop();
|
|
|
|
|
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 = nodep->rhsp()->unlinkFrBack()->castNodeBiop();
|
|
|
|
|
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: ");
|
|
|
|
|
}
|
2008-11-17 16:36:01 +00:00
|
|
|
|
void replaceAndOr (AstNodeBiop* nodep) {
|
2012-05-08 23:20:23 +00:00
|
|
|
|
// 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
|
2008-11-17 16:36:01 +00:00
|
|
|
|
// (Or/And may also be reversed)
|
|
|
|
|
AstNodeBiop* lp = nodep->lhsp()->unlinkFrBack()->castNodeBiop();
|
|
|
|
|
AstNode* llp = lp->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNode* lrp = lp->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeBiop* rp = nodep->rhsp()->unlinkFrBack()->castNodeBiop();
|
|
|
|
|
AstNode* rlp = rp->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNode* rrp = rp->rhsp()->unlinkFrBack();
|
|
|
|
|
nodep->replaceWith(lp);
|
2012-05-08 23:20:23 +00:00
|
|
|
|
if (operandsSame(llp,rlp)) {
|
|
|
|
|
lp->lhsp(llp);
|
|
|
|
|
lp->rhsp(nodep);
|
|
|
|
|
nodep->lhsp(lrp);
|
|
|
|
|
nodep->rhsp(rrp);
|
|
|
|
|
rp->deleteTree();
|
|
|
|
|
rlp->deleteTree();
|
|
|
|
|
} else if (operandsSame(lrp, rrp)) {
|
|
|
|
|
lp->lhsp(nodep);
|
|
|
|
|
lp->rhsp(rrp);
|
|
|
|
|
nodep->lhsp(llp);
|
|
|
|
|
nodep->rhsp(rlp);
|
|
|
|
|
rp->deleteTree();
|
|
|
|
|
lrp->deleteTree();
|
|
|
|
|
} else {
|
|
|
|
|
nodep->v3fatalSrc("replaceAndOr on something operandAndOrSame shouldn't have matched");
|
|
|
|
|
}
|
2008-11-17 16:36:01 +00:00
|
|
|
|
//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 = nodep->lhsp()->unlinkFrBack()->castNodeBiop();
|
|
|
|
|
AstNode* llp = lp->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNode* lrp = lp->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeBiop* rp = nodep->rhsp()->unlinkFrBack()->castNodeBiop();
|
|
|
|
|
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);
|
|
|
|
|
rp->deleteTree();
|
|
|
|
|
rrp->deleteTree();
|
|
|
|
|
//nodep->dumpTree(cout, " repShiftSame_new: ");
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
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 = (nodep->castExtendS()
|
|
|
|
|
? (new AstExtendS(nodep->fileline(), arg0p))->castNode()
|
|
|
|
|
: (new AstExtend (nodep->fileline(), arg0p))->castNode());
|
2012-04-29 13:42:17 +00:00
|
|
|
|
newp->dtypeFrom(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->replaceWith(newp); nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
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);
|
2012-04-29 13:42:17 +00:00
|
|
|
|
newp->dtypeFrom(nodep);
|
|
|
|
|
newp->lhsp()->dtypeFrom(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->replaceWith(newp); nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
2006-09-01 16:53:14 +00:00
|
|
|
|
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 = nodep->lhsp()->castConst()->num().mostSetBitP1()-1; // 2^n->n+1
|
|
|
|
|
AstNode* opp = nodep->rhsp()->unlinkFrBack();
|
|
|
|
|
AstShiftL* newp = new AstShiftL(nodep->fileline(),
|
|
|
|
|
opp, new AstConst(nodep->fileline(), amount));
|
2012-04-29 13:42:17 +00:00
|
|
|
|
newp->dtypeFrom(nodep);
|
2006-09-01 16:53:14 +00:00
|
|
|
|
nodep->replaceWith(newp); nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
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 = nodep->rhsp()->castConst()->num().mostSetBitP1()-1; // 2^n->n+1
|
|
|
|
|
AstNode* opp = nodep->lhsp()->unlinkFrBack();
|
|
|
|
|
AstShiftR* newp = new AstShiftR(nodep->fileline(),
|
|
|
|
|
opp, new AstConst(nodep->fileline(), amount));
|
2012-04-29 13:42:17 +00:00
|
|
|
|
newp->dtypeFrom(nodep);
|
2006-09-01 16:53:14 +00:00
|
|
|
|
nodep->replaceWith(newp); nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
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 = nodep->lhsp()->castNodeBiop(); lhsp->unlinkFrBack();
|
|
|
|
|
AstNode* shiftp = nodep->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNode* ap = lhsp->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNode* bp = lhsp->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeBiop* shift1p = nodep;
|
2008-11-20 01:15:05 +00:00
|
|
|
|
AstNodeBiop* shift2p = nodep->cloneTree(true);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
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);
|
|
|
|
|
newp->accept(*this); // 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 = nodep->lhsp()->castNodeBiop(); lhsp->unlinkFrBack();
|
|
|
|
|
AstNode* ap = lhsp->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNode* shift1p = lhsp->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNode* shift2p = nodep->rhsp()->unlinkFrBack();
|
|
|
|
|
if (nodep->type()==lhsp->type()) {
|
|
|
|
|
nodep->lhsp(ap);
|
|
|
|
|
nodep->rhsp(new AstAdd(nodep->fileline(), shift1p, shift2p));
|
|
|
|
|
nodep->accept(*this); // Further reduce, either node may have more reductions.
|
|
|
|
|
} else {
|
|
|
|
|
// We know shift amounts are constant, but might be a mixed left/right shift
|
2008-10-06 13:59:22 +00:00
|
|
|
|
int shift1 = shift1p->castConst()->toUInt(); if (lhsp->castShiftR()) shift1=-shift1;
|
|
|
|
|
int shift2 = shift2p->castConst()->toUInt(); if (nodep->castShiftR()) shift2=-shift2;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
int newshift = shift1+shift2;
|
2007-11-02 11:23:03 +00:00
|
|
|
|
shift1p->deleteTree(); shift1p=NULL;
|
|
|
|
|
shift2p->deleteTree(); shift1p=NULL;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstNode* newp;
|
|
|
|
|
V3Number mask1 (nodep->fileline(), nodep->width());
|
|
|
|
|
V3Number ones (nodep->fileline(), nodep->width());
|
2007-03-09 15:18:56 +00:00
|
|
|
|
ones.setMask(nodep->width());
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (shift1<0) {
|
|
|
|
|
mask1.opShiftR(ones,V3Number(nodep->fileline(),VL_WORDSIZE,-shift1));
|
|
|
|
|
} else {
|
|
|
|
|
mask1.opShiftL(ones,V3Number(nodep->fileline(),VL_WORDSIZE,shift1));
|
|
|
|
|
}
|
|
|
|
|
V3Number mask (nodep->fileline(), nodep->width());
|
|
|
|
|
if (shift2<0) {
|
|
|
|
|
mask.opShiftR(mask1,V3Number(nodep->fileline(),VL_WORDSIZE,-shift2));
|
|
|
|
|
} else {
|
|
|
|
|
mask.opShiftL(mask1,V3Number(nodep->fileline(),VL_WORDSIZE,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));
|
|
|
|
|
}
|
2012-04-29 13:42:17 +00:00
|
|
|
|
newp->dtypeFrom(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
newp = new AstAnd (nodep->fileline(),
|
|
|
|
|
newp,
|
|
|
|
|
new AstConst (nodep->fileline(), mask));
|
2012-04-29 13:42:17 +00:00
|
|
|
|
newp->dtypeFrom(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->replaceWith(newp); nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
//newp->dumpTree(cout, " repShiftShift_new: ");
|
|
|
|
|
newp->accept(*this); // Further reduce, either node may have more reductions.
|
|
|
|
|
}
|
|
|
|
|
lhsp->deleteTree(); lhsp=NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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}
|
|
|
|
|
if (!m_modp) return false; // Skip if we're not const'ing an entire module (IE doing only one assign, etc)
|
|
|
|
|
AstSel* sel1p = nodep->lhsp()->castSel(); if (!sel1p) return false;
|
|
|
|
|
AstNodeAssign* nextp = nodep->nextp()->castNodeAssign(); if (!nextp) return false;
|
|
|
|
|
if (nodep->type() != nextp->type()) return false;
|
|
|
|
|
AstSel* sel2p = nextp->lhsp()->castSel(); if (!sel2p) return false;
|
|
|
|
|
AstVarRef* varref1p = sel1p->fromp()->castVarRef(); if (!varref1p) return false;
|
|
|
|
|
AstVarRef* varref2p = sel2p->fromp()->castVarRef(); if (!varref2p) return false;
|
2012-11-29 01:18:41 +00:00
|
|
|
|
if (!varref1p->sameTree(varref2p)) return false;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstConst* con1p = sel1p->lsbp()->castConst(); if (!con1p) return false;
|
|
|
|
|
AstConst* con2p = sel2p->lsbp()->castConst(); 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?
|
2008-10-06 13:59:22 +00:00
|
|
|
|
if (( con1p->toSInt() != con2p->toSInt() + sel2p->width())
|
|
|
|
|
&&(con2p->toSInt() != con1p->toSInt() + sel1p->width())) return false;
|
|
|
|
|
bool lsbFirstAssign = (con1p->toUInt() < con2p->toUInt());
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// If the user already has nice 32-bit divisions, keep them to aid later subdivision
|
2008-09-04 15:03:46 +00:00
|
|
|
|
//if (VL_BITBIT_I(con1p->toUInt()) == 0) return false;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
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); nodep->deleteTree();
|
|
|
|
|
nextp->unlinkFrBack()->deleteTree();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-22 21:32:29 +00:00
|
|
|
|
bool varNotReferenced(AstNode* nodep, AstVar* varp, int level=0) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Return true if varp never referenced under node.
|
|
|
|
|
// Return false if referenced, or tree too deep to be worth it
|
|
|
|
|
if (!nodep) return true;
|
|
|
|
|
if (level>2) return false;
|
|
|
|
|
if (nodep->castNodeVarRef() && nodep->castNodeVarRef()->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 (nodep->lhsp()->castVarRef()
|
|
|
|
|
&& nodep->rhsp()->castVarRef()
|
2012-11-29 01:18:41 +00:00
|
|
|
|
&& nodep->lhsp()->castVarRef()->sameNoLvalue(nodep->rhsp()->castVarRef())
|
2006-08-26 11:35:28 +00:00
|
|
|
|
&& !nodep->castAssignDly()) {
|
|
|
|
|
// X = X. Quite pointless, though X <= X may override another earlier assignment
|
|
|
|
|
if (nodep->castAssignW()) {
|
|
|
|
|
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 {
|
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-12-29 01:46:13 +00:00
|
|
|
|
else if (m_doV && nodep->lhsp()->castConcat()) {
|
2008-02-20 16:54:41 +00:00
|
|
|
|
bool need_temp = false;
|
|
|
|
|
if (m_warn && !nodep->castAssignDly()) { // Is same var on LHS and RHS?
|
2008-11-22 22:27:55 +00:00
|
|
|
|
// Note only do this (need user4) when m_warn, which is
|
|
|
|
|
// done as unique visitor
|
2008-11-25 14:03:49 +00:00
|
|
|
|
AstUser4InUse m_inuser4;
|
2008-02-20 16:54:41 +00:00
|
|
|
|
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}))
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (debug()>=9) nodep->dumpTree(cout," Ass_old: ");
|
|
|
|
|
// Unlink the stuff
|
|
|
|
|
AstNode* lc1p = nodep->lhsp()->castConcat()->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNode* lc2p = nodep->lhsp()->castConcat()->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNode* conp = nodep->lhsp()->castConcat()->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;
|
|
|
|
|
if (msb1!=(conp->width()-1)) nodep->v3fatalSrc("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.
|
2008-02-20 16:54:41 +00:00
|
|
|
|
AstNode* newp = NULL;
|
|
|
|
|
if (!need_temp) {
|
|
|
|
|
AstNodeAssign* asn1ap=nodep->cloneType(lc1p, sel1p)->castNodeAssign();
|
|
|
|
|
AstNodeAssign* asn2ap=nodep->cloneType(lc2p, sel2p)->castNodeAssign();
|
2012-04-29 14:14:13 +00:00
|
|
|
|
asn1ap->dtypeFrom(sel1p);
|
|
|
|
|
asn2ap->dtypeFrom(sel2p);
|
2011-08-05 01:58:45 +00:00
|
|
|
|
// cppcheck-suppress nullPointer // addNext deals with it
|
2008-02-20 16:54:41 +00:00
|
|
|
|
newp = newp->addNext(asn1ap);
|
2011-08-05 01:58:45 +00:00
|
|
|
|
// cppcheck-suppress nullPointer // addNext deals with it
|
2008-02-20 16:54:41 +00:00
|
|
|
|
newp = newp->addNext(asn2ap);
|
|
|
|
|
} else {
|
|
|
|
|
if (!m_modp) nodep->v3fatalSrc("Not under module");
|
|
|
|
|
// We could create just one temp variable, but we'll get better optimization
|
|
|
|
|
// if we make one per term.
|
2008-11-18 02:02:10 +00:00
|
|
|
|
string name1 = ((string)"__Vconcswap"+cvtToStr(m_modp->varNumGetInc()));
|
|
|
|
|
string name2 = ((string)"__Vconcswap"+cvtToStr(m_modp->varNumGetInc()));
|
2008-02-20 16:54:41 +00:00
|
|
|
|
AstVar* temp1p = new AstVar(sel1p->fileline(), AstVarType::BLOCKTEMP, name1,
|
2012-03-31 15:10:34 +00:00
|
|
|
|
VFlagLogicPacked(), msb1-lsb1+1);
|
2008-02-20 16:54:41 +00:00
|
|
|
|
AstVar* temp2p = new AstVar(sel2p->fileline(), AstVarType::BLOCKTEMP, name2,
|
2012-03-31 15:10:34 +00:00
|
|
|
|
VFlagLogicPacked(), msb2-lsb2+1);
|
2008-02-20 16:54:41 +00:00
|
|
|
|
m_modp->addStmtp(temp1p);
|
|
|
|
|
m_modp->addStmtp(temp2p);
|
|
|
|
|
AstNodeAssign* asn1ap=nodep->cloneType
|
|
|
|
|
(new AstVarRef(sel1p->fileline(), temp1p, true),
|
|
|
|
|
sel1p)->castNodeAssign();
|
|
|
|
|
AstNodeAssign* asn2ap=nodep->cloneType
|
|
|
|
|
(new AstVarRef(sel2p->fileline(), temp2p, true),
|
|
|
|
|
sel2p)->castNodeAssign();
|
|
|
|
|
AstNodeAssign* asn1bp=nodep->cloneType
|
|
|
|
|
(lc1p, new AstVarRef(sel1p->fileline(), temp1p, false))
|
|
|
|
|
->castNodeAssign();
|
|
|
|
|
AstNodeAssign* asn2bp=nodep->cloneType
|
|
|
|
|
(lc2p, new AstVarRef(sel2p->fileline(), temp2p, false))
|
|
|
|
|
->castNodeAssign();
|
2012-04-29 14:14:13 +00:00
|
|
|
|
asn1ap->dtypeFrom(temp1p);
|
|
|
|
|
asn1bp->dtypeFrom(temp1p);
|
|
|
|
|
asn2ap->dtypeFrom(temp2p);
|
|
|
|
|
asn2bp->dtypeFrom(temp2p);
|
2008-02-20 16:54:41 +00:00
|
|
|
|
// This order matters
|
2011-08-05 01:58:45 +00:00
|
|
|
|
// cppcheck-suppress nullPointer // addNext deals with it
|
2008-02-20 16:54:41 +00:00
|
|
|
|
newp = newp->addNext(asn1ap);
|
2011-08-05 01:58:45 +00:00
|
|
|
|
// cppcheck-suppress nullPointer // addNext deals with it
|
2008-02-20 16:54:41 +00:00
|
|
|
|
newp = newp->addNext(asn2ap);
|
2011-08-05 01:58:45 +00:00
|
|
|
|
// cppcheck-suppress nullPointer // addNext deals with it
|
2008-02-20 16:54:41 +00:00
|
|
|
|
newp = newp->addNext(asn1bp);
|
2011-08-05 01:58:45 +00:00
|
|
|
|
// cppcheck-suppress nullPointer // addNext deals with it
|
2008-02-20 16:54:41 +00:00
|
|
|
|
newp = newp->addNext(asn2bp);
|
|
|
|
|
}
|
2011-08-05 01:58:45 +00:00
|
|
|
|
if (debug()>=9 && newp) newp->dumpTreeAndNext(cout," _new: ");
|
2008-02-20 16:54:41 +00:00
|
|
|
|
nodep->addNextHere(newp);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Cleanup
|
2007-11-02 11:23:03 +00:00
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
|
|
|
|
|
conp->deleteTree(); conp=NULL;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Further reduce, either node may have more reductions.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else if (replaceAssignMultiSel(nodep)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Boolean replacements
|
|
|
|
|
bool operandBoolShift(AstNode* nodep) {
|
|
|
|
|
// boolean test of AND(const,SHIFTR(x,const)) -> test of AND(SHIFTL(x,const), x)
|
|
|
|
|
if (!nodep->castAnd()) return false;
|
|
|
|
|
if (!nodep->castAnd()->lhsp()->castConst()) return false;
|
|
|
|
|
if (!nodep->castAnd()->rhsp()->castShiftR()) return false;
|
|
|
|
|
AstShiftR* shiftp = nodep->castAnd()->rhsp()->castShiftR();
|
|
|
|
|
if (!shiftp->rhsp()->castConst()) return false;
|
2008-10-06 13:59:22 +00:00
|
|
|
|
if ((uint32_t)(nodep->width()) <= shiftp->rhsp()->castConst()->toUInt()) return false;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
void replaceBoolShift(AstNode* nodep) {
|
|
|
|
|
if (debug()>=9) nodep->dumpTree(cout," bshft_old: ");
|
|
|
|
|
AstConst* andConstp = nodep->castAnd()->lhsp()->castConst();
|
|
|
|
|
AstNode* fromp = nodep->castAnd()->rhsp()->castShiftR()->lhsp()->unlinkFrBack();
|
|
|
|
|
AstConst* shiftConstp = nodep->castAnd()->rhsp()->castShiftR()->rhsp()->castConst();
|
|
|
|
|
V3Number val (andConstp->fileline(), andConstp->width());
|
|
|
|
|
val.opShiftL(andConstp->num(), shiftConstp->num());
|
|
|
|
|
AstAnd* newp = new AstAnd(nodep->fileline(),
|
|
|
|
|
new AstConst(nodep->fileline(), val),
|
|
|
|
|
fromp);
|
2012-04-29 14:14:13 +00:00
|
|
|
|
newp->dtypeSetLogicSized(nodep->width(), nodep->width(), AstNumeric::UNSIGNED); // widthMin no longer applicable if different C-expanded width
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
if (debug()>=9) newp->dumpTree(cout," _new: ");
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-17 18:13:11 +00:00
|
|
|
|
void replaceWithSimulation(AstNode* nodep) {
|
|
|
|
|
SimulateVisitor simvis;
|
2009-07-21 18:31:16 +00:00
|
|
|
|
// Run it - may be unoptimizable due to large for loop, etc
|
|
|
|
|
simvis.mainParamEmulate(nodep);
|
2009-07-17 18:13:11 +00:00
|
|
|
|
if (!simvis.optimizable()) {
|
2010-12-25 21:31:22 +00:00
|
|
|
|
AstNode* errorp = simvis.whyNotNodep(); if (!errorp) errorp = nodep;
|
2009-07-17 18:13:11 +00:00
|
|
|
|
nodep->v3error("Expecting expression to be constant, but can't determine constant for "
|
|
|
|
|
<<nodep->prettyTypeName()<<endl
|
2012-05-22 01:24:17 +00:00
|
|
|
|
<<errorp->warnMore()<<"... Location of non-constant "
|
2009-07-17 18:13:11 +00:00
|
|
|
|
<<errorp->prettyTypeName()<<": "<<simvis.whyNotMessage());
|
|
|
|
|
replaceZero(nodep); nodep=NULL;
|
|
|
|
|
} else {
|
|
|
|
|
// Fetch the result
|
|
|
|
|
V3Number* outnump = simvis.fetchNumberNull(nodep);
|
|
|
|
|
if (!outnump) nodep->v3fatalSrc("No number returned from simulation");
|
|
|
|
|
// Replace it
|
|
|
|
|
replaceNum(nodep,*outnump); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//----------------------------------------
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
|
|
|
|
virtual void visit(AstNetlist* nodep, AstNUser*) {
|
|
|
|
|
// Iterate modules backwards, in bottom-up order. That's faster
|
|
|
|
|
nodep->iterateChildrenBackwards(*this);
|
|
|
|
|
}
|
2009-11-07 11:20:20 +00:00
|
|
|
|
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
m_modp = nodep;
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_modp = NULL;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstCFunc* nodep, AstNUser*) {
|
|
|
|
|
// No ASSIGNW removals under funcs, we've long eliminated INITIALs
|
|
|
|
|
// (We should perhaps rename the assignw's to just assigns)
|
|
|
|
|
m_wremove = false;
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_wremove = true;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstScope* nodep, AstNUser*) {
|
|
|
|
|
// No ASSIGNW removals under scope, we've long eliminated INITIALs
|
|
|
|
|
m_scopep = nodep;
|
|
|
|
|
m_wremove = false;
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_wremove = true;
|
|
|
|
|
m_scopep = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void swapSides(AstNodeBiCom* nodep) {
|
|
|
|
|
// COMMUNATIVE({a},CONST) -> COMMUNATIVE(CONST,{a})
|
|
|
|
|
// This simplifies later optimizations
|
|
|
|
|
AstNode* lhsp = nodep->lhsp()->unlinkFrBackWithNext();
|
|
|
|
|
AstNode* rhsp = nodep->rhsp()->unlinkFrBackWithNext();
|
|
|
|
|
nodep->lhsp(rhsp);
|
|
|
|
|
nodep->rhsp(lhsp);
|
|
|
|
|
nodep->accept(*this); // Again?
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int operandConcatMove(AstConcat* nodep) {
|
|
|
|
|
// CONCAT under concat (See moveConcat)
|
|
|
|
|
// Return value: true indicates to do it; 2 means move to LHS
|
|
|
|
|
AstConcat* abConcp = nodep->lhsp()->castConcat();
|
|
|
|
|
AstConcat* bcConcp = nodep->rhsp()->castConcat();
|
|
|
|
|
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 = nodep->rhsp()->castConcat(); 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);
|
2007-11-02 11:23:03 +00:00
|
|
|
|
bcConcp->deleteTree(); bcConcp=NULL;
|
2008-06-10 01:25:10 +00:00
|
|
|
|
} else {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstConcat* abConcp = nodep->lhsp()->castConcat(); 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);
|
2007-11-02 11:23:03 +00:00
|
|
|
|
abConcp->deleteTree(); abConcp=NULL;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Special cases
|
|
|
|
|
virtual void visit(AstConst* nodep, AstNUser*) {} // Already constant
|
|
|
|
|
|
|
|
|
|
virtual void visit(AstCell* nodep, AstNUser*) {
|
|
|
|
|
if (m_params) {
|
|
|
|
|
nodep->paramsp()->iterateAndNext(*this);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstPin* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void replaceSelSel(AstSel* nodep) {
|
|
|
|
|
// SEL(SEL({x},a,b),c,d) => SEL({x},a+c,d)
|
|
|
|
|
AstSel* belowp = nodep->fromp()->castSel();
|
|
|
|
|
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 (lsb1p->castConst() && lsb2p->castConst()) {
|
|
|
|
|
newlsbp = new AstConst(lsb1p->fileline(),
|
2008-10-06 13:59:22 +00:00
|
|
|
|
lsb1p->castConst()->toUInt() + lsb2p->castConst()->toUInt());
|
2007-11-02 11:23:03 +00:00
|
|
|
|
lsb1p->deleteTree(); lsb1p=NULL;
|
|
|
|
|
lsb2p->deleteTree(); lsb2p=NULL;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
} 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));
|
2012-04-29 14:14:13 +00:00
|
|
|
|
newlsbp->dtypeFrom(lsb2p); // Unsigned
|
|
|
|
|
newlsbp->castAdd()->rhsp()->dtypeFrom(lsb2p);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
AstSel* newp = new AstSel(nodep->fileline(),
|
|
|
|
|
fromp,
|
|
|
|
|
newlsbp,
|
|
|
|
|
widthp);
|
|
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void replaceSelConcat(AstSel* nodep) {
|
|
|
|
|
// SEL(CONCAT(a,b),c,d) => SEL(a or b, . .)
|
|
|
|
|
AstConcat* conp = nodep->fromp()->castConcat();
|
|
|
|
|
AstNode* conLhsp = conp->lhsp();
|
|
|
|
|
AstNode* conRhsp = conp->rhsp();
|
|
|
|
|
if ((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 ((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);
|
|
|
|
|
}
|
|
|
|
|
nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void replaceSelReplicate(AstSel* nodep) {
|
|
|
|
|
// SEL(REPLICATE(a,b),1,bit) => SEL(a,1,bit)
|
|
|
|
|
AstReplicate* repp = nodep->fromp()->castReplicate();
|
|
|
|
|
AstNode* fromp = repp->lhsp()->unlinkFrBack();
|
|
|
|
|
AstConst* lsbp = nodep->lsbp()->castConst();
|
|
|
|
|
AstNode* widthp = nodep->widthp()->unlinkFrBack();
|
|
|
|
|
AstSel* newp = new AstSel(nodep->fileline(),
|
|
|
|
|
fromp,
|
2008-10-06 13:59:22 +00:00
|
|
|
|
new AstConst(lsbp->fileline(), lsbp->toUInt() % fromp->width()),
|
2006-08-26 11:35:28 +00:00
|
|
|
|
widthp);
|
2012-04-29 13:42:17 +00:00
|
|
|
|
newp->dtypeFrom(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->replaceWith(newp); nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-24 02:21:59 +00:00
|
|
|
|
void replaceSelIntoBiop(AstSel* nodep) {
|
|
|
|
|
// SEL(BUFIF1(a,b),1,bit) => BUFIF1(SEL(a,1,bit),SEL(b,1,bit))
|
|
|
|
|
AstNodeBiop* fromp = nodep->fromp()->unlinkFrBack()->castNodeBiop();
|
|
|
|
|
if (!fromp) nodep->v3fatalSrc("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));
|
2012-04-29 13:42:17 +00:00
|
|
|
|
fromp->dtypeFrom(nodep);
|
2011-02-24 02:21:59 +00:00
|
|
|
|
nodep->replaceWith(fromp); nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
2011-02-24 02:36:38 +00:00
|
|
|
|
void replaceSelIntoUniop(AstSel* nodep) {
|
|
|
|
|
// SEL(NOT(a),1,bit) => NOT(SEL(a,bit))
|
|
|
|
|
AstNodeUniop* fromp = nodep->fromp()->unlinkFrBack()->castNodeUniop();
|
|
|
|
|
if (!fromp) nodep->v3fatalSrc("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(),
|
2012-05-31 03:17:55 +00:00
|
|
|
|
bilhsp, lsbp, widthp));
|
2012-04-29 13:42:17 +00:00
|
|
|
|
fromp->dtypeFrom(nodep);
|
2011-02-24 02:36:38 +00:00
|
|
|
|
nodep->replaceWith(fromp); nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
2011-02-24 02:21:59 +00:00
|
|
|
|
|
2012-07-28 14:52:29 +00:00
|
|
|
|
virtual void visit(AstAttrOf* nodep, AstNUser*) {
|
|
|
|
|
AstAttrOf* oldAttr = m_attrp;
|
|
|
|
|
m_attrp = nodep;
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_attrp = oldAttr;
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
if (!nodep->varp()) nodep->v3fatalSrc("Not linked");
|
|
|
|
|
bool did=false;
|
2012-07-28 14:52:29 +00:00
|
|
|
|
if (m_doV && nodep->varp()->hasSimpleInit() && !m_attrp) {
|
2010-01-21 23:20:47 +00:00
|
|
|
|
//if (debug()) nodep->varp()->valuep()->dumpTree(cout," visitvaref: ");
|
|
|
|
|
nodep->varp()->valuep()->iterateAndNext(*this);
|
|
|
|
|
if (operandConst(nodep->varp()->valuep())
|
2006-08-26 11:35:28 +00:00
|
|
|
|
&& !nodep->lvalue()
|
2010-12-29 01:46:13 +00:00
|
|
|
|
&& ((!m_params // Can reduce constant wires into equations
|
2010-12-29 02:23:16 +00:00
|
|
|
|
&& m_doNConst
|
2010-12-29 01:46:13 +00:00
|
|
|
|
&& v3Global.opt.oConst()
|
2007-03-02 21:14:22 +00:00
|
|
|
|
&& !nodep->varp()->isSigPublic())
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|| nodep->varp()->isParam())) {
|
2010-01-21 23:20:47 +00:00
|
|
|
|
AstConst* constp = nodep->varp()->valuep()->castConst();
|
2006-08-26 11:35:28 +00:00
|
|
|
|
const V3Number& num = constp->num();
|
|
|
|
|
//UINFO(2,"constVisit "<<(void*)constp<<" "<<num<<endl);
|
|
|
|
|
replaceNum(nodep, num); nodep=NULL;
|
|
|
|
|
did=true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!did && m_required) {
|
|
|
|
|
nodep->v3error("Expecting expression to be constant, but variable isn't const: "<<nodep->varp()->prettyName());
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-12-27 13:29:55 +00:00
|
|
|
|
virtual void visit(AstEnumItemRef* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
if (!nodep->itemp()) nodep->v3fatalSrc("Not linked");
|
|
|
|
|
bool did=false;
|
2010-01-21 23:20:47 +00:00
|
|
|
|
if (nodep->itemp()->valuep()) {
|
|
|
|
|
//if (debug()) nodep->varp()->valuep()->dumpTree(cout," visitvaref: ");
|
|
|
|
|
nodep->itemp()->valuep()->iterateAndNext(*this);
|
|
|
|
|
if (AstConst* valuep = nodep->itemp()->valuep()->castConst()) {
|
|
|
|
|
const V3Number& num = valuep->num();
|
2009-12-27 13:29:55 +00:00
|
|
|
|
replaceNum(nodep, num); nodep=NULL;
|
|
|
|
|
did=true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!did && m_required) {
|
|
|
|
|
nodep->v3error("Expecting expression to be constant, but variable isn't const: "<<nodep->itemp()->prettyName());
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-01-17 20:10:37 +00:00
|
|
|
|
|
|
|
|
|
// virtual void visit(AstCvtPackString* nodep, AstNUser*) {
|
|
|
|
|
// Not constant propagated (for today) because AstMath::isOpaque is set
|
|
|
|
|
// Someday if lower is constant, convert to quoted "string".
|
|
|
|
|
|
2009-01-07 14:37:59 +00:00
|
|
|
|
bool onlySenItemInSenTree(AstNodeSenItem* nodep) {
|
|
|
|
|
// Only one if it's not in a list
|
|
|
|
|
return (!nodep->nextp() && nodep->backp()->nextp() != nodep);
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
virtual void visit(AstSenItem* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
2010-12-29 02:23:16 +00:00
|
|
|
|
if (m_doNConst
|
|
|
|
|
&& (nodep->sensp()->castConst()
|
2013-02-02 19:37:18 +00:00
|
|
|
|
|| nodep->sensp()->castEnumItemRef()
|
2010-12-29 02:23:16 +00:00
|
|
|
|
|| (nodep->varrefp() && nodep->varrefp()->varp()->isParam()))) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Constants in sensitivity lists may be removed (we'll simplify later)
|
2007-06-15 14:39:52 +00:00
|
|
|
|
if (nodep->isClocked()) { // A constant can never get a pos/negexge
|
2009-01-07 14:37:59 +00:00
|
|
|
|
if (onlySenItemInSenTree(nodep)) {
|
|
|
|
|
nodep->replaceWith(new AstSenItem(nodep->fileline(), AstSenItem::Never()));
|
|
|
|
|
nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
} else {
|
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
2007-06-15 14:39:52 +00:00
|
|
|
|
} else { // Otherwise it may compute a result that needs to settle out
|
2009-01-07 14:37:59 +00:00
|
|
|
|
nodep->replaceWith(new AstSenItem(nodep->fileline(), AstSenItem::Combo()));
|
|
|
|
|
nodep->deleteTree(); nodep=NULL;
|
2007-06-15 14:39:52 +00:00
|
|
|
|
}
|
2010-12-29 02:23:16 +00:00
|
|
|
|
} else if (m_doNConst && nodep->sensp()->castNot()) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// V3Gate may propagate NOTs into clocks... Just deal with it
|
|
|
|
|
AstNode* sensp = nodep->sensp();
|
|
|
|
|
AstNode* lastSensp = sensp;
|
|
|
|
|
bool invert = false;
|
|
|
|
|
while (lastSensp->castNot()) {
|
|
|
|
|
lastSensp = lastSensp->castNot()->lhsp();
|
|
|
|
|
invert = !invert;
|
|
|
|
|
}
|
|
|
|
|
UINFO(8,"senItem(NOT...) "<<nodep<<" "<<invert<<endl);
|
|
|
|
|
if (invert) nodep->edgeType( nodep->edgeType().invert() );
|
2006-10-03 13:02:21 +00:00
|
|
|
|
AstNodeVarRef* senvarp = lastSensp->unlinkFrBack()->castNodeVarRef();
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (!senvarp) sensp->v3fatalSrc("Non-varref sensitivity variable");
|
|
|
|
|
sensp->replaceWith(senvarp);
|
|
|
|
|
sensp->deleteTree(); sensp=NULL;
|
2013-02-02 19:37:18 +00:00
|
|
|
|
} else if (!m_doNConst // Deal with later when doNConst missing
|
|
|
|
|
&& (nodep->sensp()->castEnumItemRef()
|
|
|
|
|
|| nodep->sensp()->castConst())) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
} else {
|
|
|
|
|
if (nodep->hasVar() && !nodep->varrefp()) nodep->v3fatalSrc("Null sensitivity variable");
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-01-07 14:37:59 +00:00
|
|
|
|
virtual void visit(AstSenGate* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
if (AstConst* constp = nodep->rhsp()->castConst()) {
|
|
|
|
|
if (constp->isZero()) {
|
|
|
|
|
UINFO(4,"SENGATE(...,0)->NEVER"<<endl);
|
|
|
|
|
if (onlySenItemInSenTree(nodep)) {
|
|
|
|
|
nodep->replaceWith(new AstSenItem(nodep->fileline(), AstSenItem::Never()));
|
|
|
|
|
nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
} else {
|
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
UINFO(4,"SENGATE(SENITEM,0)->ALWAYS SENITEM"<<endl);
|
|
|
|
|
AstNode* senitemp = nodep->sensesp()->unlinkFrBack();
|
|
|
|
|
nodep->replaceWith(senitemp);
|
|
|
|
|
nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct SenItemCmp {
|
|
|
|
|
inline bool operator () (AstNodeSenItem* lhsp, AstNodeSenItem* rhsp) const {
|
|
|
|
|
if (lhsp->type() < rhsp->type()) return true;
|
|
|
|
|
if (lhsp->type() > rhsp->type()) return false;
|
|
|
|
|
const AstSenItem* litemp = lhsp->castSenItem();
|
|
|
|
|
const AstSenItem* ritemp = rhsp->castSenItem();
|
|
|
|
|
if (litemp && ritemp) {
|
|
|
|
|
// Looks visually better if we keep sorted by name
|
|
|
|
|
if (!litemp->varrefp() && ritemp->varrefp()) return true;
|
|
|
|
|
if ( litemp->varrefp() && !ritemp->varrefp()) return false;
|
|
|
|
|
if (litemp->varrefp() && ritemp->varrefp()) {
|
|
|
|
|
if (litemp->varrefp()->name() < ritemp->varrefp()->name()) return true;
|
|
|
|
|
if (litemp->varrefp()->name() > ritemp->varrefp()->name()) return false;
|
|
|
|
|
// But might be same name with different scopes
|
|
|
|
|
if (litemp->varrefp()->varScopep() < ritemp->varrefp()->varScopep()) return true;
|
|
|
|
|
if (litemp->varrefp()->varScopep() > ritemp->varrefp()->varScopep()) return false;
|
|
|
|
|
}
|
2010-01-14 19:17:11 +00:00
|
|
|
|
// Sort by edge, AFTER variable, as we want multiple edges for same var adjacent
|
|
|
|
|
// note the SenTree optimizer requires this order (more general firsst, less general last)
|
|
|
|
|
if (litemp->edgeType() < ritemp->edgeType()) return true;
|
|
|
|
|
if (litemp->edgeType() > ritemp->edgeType()) return false;
|
2009-01-07 14:37:59 +00:00
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
virtual void visit(AstSenTree* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
2010-12-29 01:46:13 +00:00
|
|
|
|
if (m_doExpensive) {
|
2009-01-07 14:37:59 +00:00
|
|
|
|
//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 insure 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 (AstNodeSenItem* senp = nodep->sensesp()->castNodeSenItem();
|
|
|
|
|
senp; senp=senp->nextp()->castNodeSenItem()) {
|
|
|
|
|
if (AstSenItem* itemp = senp->castSenItem()) {
|
|
|
|
|
if (itemp->varrefp() && itemp->varrefp()->varScopep()) {
|
|
|
|
|
itemp->varrefp()->varScopep()->user4(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Find x in SENTREE(SENITEM(x))
|
|
|
|
|
for (AstNodeSenItem* nextp, * senp = nodep->sensesp()->castNodeSenItem();
|
|
|
|
|
senp; senp=nextp) {
|
|
|
|
|
nextp=senp->nextp()->castNodeSenItem();
|
|
|
|
|
if (AstSenGate* gatep = senp->castSenGate()) {
|
|
|
|
|
if (AstSenItem* itemp = gatep->sensesp()->castSenItem()) {
|
|
|
|
|
if (itemp->varrefp() && itemp->varrefp()->varScopep()) {
|
|
|
|
|
if (itemp->varrefp()->varScopep()->user4()) {
|
|
|
|
|
// Found, push this item up to the top
|
|
|
|
|
itemp->unlinkFrBack();
|
|
|
|
|
nodep->addSensesp(itemp);
|
|
|
|
|
gatep->unlinkFrBack()->deleteTree(); gatep=NULL; senp=NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 (AstNodeSenItem* nextp, * senp = nodep->sensesp()->castNodeSenItem();
|
|
|
|
|
senp; senp=nextp) {
|
|
|
|
|
nextp=senp->nextp()->castNodeSenItem();
|
2013-02-03 18:27:37 +00:00
|
|
|
|
// cppcheck-suppress unassignedVariable // cppcheck bug
|
2009-01-07 14:37:59 +00:00
|
|
|
|
SenItemCmp cmp;
|
|
|
|
|
if (nextp && !cmp(senp, nextp)) {
|
|
|
|
|
// Something's out of order, sort it
|
|
|
|
|
senp = NULL;
|
|
|
|
|
vector<AstNodeSenItem*> vec;
|
|
|
|
|
for (AstNodeSenItem* senp = nodep->sensesp()->castNodeSenItem(); senp; senp=senp->nextp()->castNodeSenItem()) {
|
|
|
|
|
vec.push_back(senp);
|
|
|
|
|
}
|
|
|
|
|
sort(vec.begin(), vec.end(), SenItemCmp());
|
|
|
|
|
for (vector<AstNodeSenItem*>::iterator it=vec.begin(); it!=vec.end(); ++it) {
|
|
|
|
|
(*it)->unlinkFrBack();
|
|
|
|
|
}
|
|
|
|
|
for (vector<AstNodeSenItem*>::iterator it=vec.begin(); it!=vec.end(); ++it) {
|
|
|
|
|
nodep->addSensesp(*it);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-03-20 20:01:53 +00:00
|
|
|
|
|
2009-01-07 14:37:59 +00:00
|
|
|
|
// Pass2, remove dup edges
|
|
|
|
|
for (AstNodeSenItem* nextp, * senp = nodep->sensesp()->castNodeSenItem();
|
|
|
|
|
senp; senp=nextp) {
|
|
|
|
|
nextp=senp->nextp()->castNodeSenItem();
|
|
|
|
|
AstNodeSenItem* cmpp = nextp;
|
|
|
|
|
AstSenItem* litemp = senp->castSenItem();
|
|
|
|
|
AstSenItem* ritemp = cmpp->castSenItem();
|
|
|
|
|
if (litemp && ritemp) {
|
2009-04-23 13:16:25 +00:00
|
|
|
|
if ((litemp->varrefp() && ritemp->varrefp() && litemp->varrefp()->sameTree(ritemp->varrefp()))
|
2009-01-07 14:37:59 +00:00
|
|
|
|
|| (!litemp->varrefp() && !ritemp->varrefp())) {
|
|
|
|
|
// We've sorted in the order ANY, BOTH, POS, NEG, so we don't need to try opposite orders
|
2010-02-02 01:15:48 +00:00
|
|
|
|
if (( litemp->edgeType()==AstEdgeType::ET_ANYEDGE) // ANY or {BOTH|POS|NEG} -> ANY
|
|
|
|
|
|| (litemp->edgeType()==AstEdgeType::ET_BOTHEDGE) // BOTH or {POS|NEG} -> BOTH
|
|
|
|
|
|| (litemp->edgeType()==AstEdgeType::ET_POSEDGE // POS or NEG -> BOTH
|
|
|
|
|
&& ritemp->edgeType()==AstEdgeType::ET_NEGEDGE)
|
2009-01-07 14:37:59 +00:00
|
|
|
|
|| (litemp->edgeType()==ritemp->edgeType()) // Identical edges
|
|
|
|
|
) {
|
|
|
|
|
// Fix edge of old node
|
2010-02-02 01:15:48 +00:00
|
|
|
|
if (litemp->edgeType()==AstEdgeType::ET_POSEDGE
|
|
|
|
|
&& ritemp->edgeType()==AstEdgeType::ET_NEGEDGE)
|
|
|
|
|
litemp->edgeType(AstEdgeType::ET_BOTHEDGE);
|
2009-01-07 14:37:59 +00:00
|
|
|
|
// Remove redundant node
|
|
|
|
|
ritemp->unlinkFrBack()->deleteTree(); ritemp=NULL; cmpp=NULL;
|
|
|
|
|
// Try to collapse again
|
|
|
|
|
nextp=litemp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//nodep->dumpTree(cout,"ssou: ");
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
//-----
|
|
|
|
|
// Zero elimination
|
|
|
|
|
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
2010-12-29 02:23:16 +00:00
|
|
|
|
if (m_doNConst && replaceNodeAssign(nodep)) return;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstAssignAlias* nodep, AstNUser*) {
|
|
|
|
|
// Don't perform any optimizations, keep the alias around
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstAssignW* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
2010-12-29 02:23:16 +00:00
|
|
|
|
if (m_doNConst && replaceNodeAssign(nodep)) return;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstNodeVarRef* varrefp = nodep->lhsp()->castVarRef(); // Not VarXRef, as different refs may set different values to each hierarchy
|
2010-12-29 02:23:16 +00:00
|
|
|
|
if (m_wremove && !m_params && m_doNConst
|
2006-08-26 11:35:28 +00:00
|
|
|
|
&& m_modp && operandConst(nodep->rhsp())
|
|
|
|
|
&& !nodep->rhsp()->castConst()->num().isFourState()
|
|
|
|
|
&& varrefp // Don't do messes with BITREFs/ARRAYREFs
|
2010-01-21 23:20:47 +00:00
|
|
|
|
&& !varrefp->varp()->valuep() // Not already constified
|
2010-12-29 01:46:13 +00:00
|
|
|
|
&& !varrefp->varScopep()) { // Not scoped (or each scope may have different initial value)
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// 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);
|
2007-11-02 11:23:03 +00:00
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Set the initial value right in the variable so we can constant propagate
|
|
|
|
|
AstNode* initvaluep = exprp->cloneTree(false);
|
2010-01-21 23:20:47 +00:00
|
|
|
|
varrefp->varp()->valuep(initvaluep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void visit(AstNodeIf* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
2010-12-29 02:23:16 +00:00
|
|
|
|
if (m_doNConst) {
|
|
|
|
|
if (AstConst* constp = nodep->condp()->castConst()) {
|
|
|
|
|
AstNode* keepp = NULL;
|
|
|
|
|
if (constp->isZero()) {
|
|
|
|
|
UINFO(4,"IF(0,{any},{x}) => {x}: "<<nodep<<endl);
|
|
|
|
|
keepp = nodep->elsesp();
|
|
|
|
|
} else {
|
|
|
|
|
UINFO(4,"IF(!0,{x},{any}) => {x}: "<<nodep<<endl);
|
|
|
|
|
keepp = nodep->ifsp();
|
|
|
|
|
}
|
|
|
|
|
if (keepp) {
|
|
|
|
|
keepp->unlinkFrBackWithNext();
|
|
|
|
|
nodep->replaceWith(keepp);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
}
|
|
|
|
|
nodep->deleteTree(); nodep=NULL;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2010-12-29 02:23:16 +00:00
|
|
|
|
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
|
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
else if (!afterComment(nodep->ifsp())) {
|
|
|
|
|
UINFO(4,"IF({x}) NULL {...} => IF(NOT{x}}: "<<nodep<<endl);
|
|
|
|
|
AstNode* condp = nodep->condp();
|
|
|
|
|
AstNode* elsesp = nodep->elsesp();
|
|
|
|
|
condp->unlinkFrBackWithNext();
|
|
|
|
|
elsesp->unlinkFrBackWithNext();
|
|
|
|
|
if (nodep->ifsp()) { nodep->ifsp()->unlinkFrBackWithNext()->deleteTree(); } // Must have been comment
|
|
|
|
|
nodep->condp(new AstLogNot(condp->fileline(), condp)); // LogNot, as C++ optimization also possible
|
|
|
|
|
nodep->addIfsp(elsesp);
|
|
|
|
|
}
|
|
|
|
|
else if (((nodep->condp()->castNot() && nodep->condp()->width()==1)
|
|
|
|
|
|| (nodep->condp()->castLogNot()))
|
|
|
|
|
&& nodep->ifsp() && nodep->elsesp()) {
|
|
|
|
|
UINFO(4,"IF(NOT {x}) => IF(x) swapped if/else"<<nodep<<endl);
|
|
|
|
|
AstNode* condp = nodep->condp()->castNot()->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);
|
|
|
|
|
nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
else if (ifSameAssign(nodep)) {
|
|
|
|
|
UINFO(4,"IF({a}) ASSIGN({b},{c}) else ASSIGN({b},{d}) => ASSIGN({b}, {a}?{c}:{d})"<<endl);
|
|
|
|
|
AstNodeAssign* ifp = nodep->ifsp()->castNodeAssign();
|
|
|
|
|
AstNodeAssign* elsep = nodep->elsesp()->castNodeAssign();
|
|
|
|
|
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);
|
|
|
|
|
nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
else if (0 // Disabled, as vpm assertions are faster without due to short-circuiting
|
|
|
|
|
&& operandIfIf(nodep)) {
|
|
|
|
|
UINFO(0,"IF({a}) IF({b}) => IF({a} && {b})"<<endl);
|
|
|
|
|
AstNodeIf* lowerIfp = nodep->ifsp()->castNodeIf();
|
|
|
|
|
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);
|
|
|
|
|
lowerIfp->deleteTree(); lowerIfp=NULL;
|
|
|
|
|
}
|
|
|
|
|
else if (operandBoolShift(nodep->condp())) {
|
|
|
|
|
replaceBoolShift(nodep->condp());
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-17 20:53:12 +00:00
|
|
|
|
virtual void visit(AstSFormatF* nodep, AstNUser*) {
|
2007-06-14 17:29:37 +00:00
|
|
|
|
// 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.
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
bool anyconst = false;
|
|
|
|
|
for (AstNode* argp = nodep->exprsp(); argp; argp=argp->nextp()) {
|
|
|
|
|
if (argp->castConst()) { anyconst=true; break; }
|
|
|
|
|
}
|
2010-12-29 02:23:16 +00:00
|
|
|
|
if (m_doNConst && anyconst) {
|
2007-06-14 17:29:37 +00:00
|
|
|
|
//UINFO(9," Display in "<<nodep->text()<<endl);
|
|
|
|
|
string dispout = "";
|
|
|
|
|
string fmt = "";
|
|
|
|
|
bool inPct = false;
|
|
|
|
|
AstNode* argp = nodep->exprsp();
|
|
|
|
|
for (const char* inp = nodep->text().c_str(); *inp; inp++) {
|
|
|
|
|
char ch = *inp; // Breaks with iterators...
|
|
|
|
|
if (!inPct && ch=='%') {
|
|
|
|
|
inPct = true;
|
|
|
|
|
fmt = ch;
|
2011-07-06 01:05:35 +00:00
|
|
|
|
} else if (inPct && (isdigit(ch) || ch=='.')) {
|
2007-06-14 17:29:37 +00:00
|
|
|
|
fmt += ch;
|
|
|
|
|
} else if (inPct) {
|
|
|
|
|
inPct = false;
|
|
|
|
|
fmt += ch;
|
|
|
|
|
switch (tolower(ch)) {
|
|
|
|
|
case '%': break; // %% - just output a %
|
|
|
|
|
case 'm': break; // %m - auto insert "name"
|
|
|
|
|
default: // Most operators, just move to next argument
|
|
|
|
|
if (argp) {
|
|
|
|
|
AstNode* nextp=argp->nextp();
|
|
|
|
|
if (argp && argp->castConst()) { // Convert it
|
|
|
|
|
string out = argp->castConst()->num().displayed(fmt);
|
|
|
|
|
UINFO(9," DispConst: "<<fmt<<" -> "<<out<<" for "<<argp<<endl);
|
2007-06-14 18:34:08 +00:00
|
|
|
|
{ // fmt = out w/ replace % with %% as it must be literal.
|
|
|
|
|
fmt = "";
|
2011-08-05 01:58:45 +00:00
|
|
|
|
for (string::iterator pos = out.begin(); pos != out.end(); ++pos) {
|
2007-06-14 18:34:08 +00:00
|
|
|
|
if (*pos == '%') fmt += '%';
|
|
|
|
|
fmt += *pos;
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-06-14 17:29:37 +00:00
|
|
|
|
argp->unlinkFrBack()->deleteTree();
|
|
|
|
|
}
|
|
|
|
|
argp=nextp;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
} // switch
|
|
|
|
|
dispout += fmt;
|
|
|
|
|
} else {
|
|
|
|
|
dispout += ch;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
nodep->text(dispout);
|
|
|
|
|
//UINFO(9," Display out "<<nodep->text()<<endl);
|
|
|
|
|
}
|
2010-01-18 01:06:08 +00:00
|
|
|
|
if (!nodep->exprsp()
|
|
|
|
|
&& nodep->name().find("%") == string::npos
|
|
|
|
|
&& !nodep->hidden()) {
|
|
|
|
|
// Just a simple constant string - the formatting is pointless
|
|
|
|
|
replaceConstString(nodep, nodep->name()); nodep=NULL;
|
|
|
|
|
}
|
2007-06-14 17:29:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-07-17 18:13:11 +00:00
|
|
|
|
virtual void visit(AstFuncRef* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
if (m_params) { // Only parameters force us to do constant function call propagation
|
|
|
|
|
replaceWithSimulation(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
virtual void visit(AstWhile* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
2010-12-29 02:23:16 +00:00
|
|
|
|
if (m_doNConst) {
|
|
|
|
|
if (nodep->condp()->isZero()) {
|
|
|
|
|
UINFO(4,"WHILE(0) => nop "<<nodep<<endl);
|
|
|
|
|
if (nodep->precondsp()) nodep->replaceWith(nodep->precondsp());
|
|
|
|
|
else nodep->unlinkFrBack();
|
|
|
|
|
nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
else if (operandBoolShift(nodep->condp())) {
|
|
|
|
|
replaceBoolShift(nodep->condp());
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-25 20:53:55 +00:00
|
|
|
|
// 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* nodep, AstNUser*) {}
|
|
|
|
|
|
2010-12-30 12:55:31 +00:00
|
|
|
|
// Ignored, can eliminate early
|
|
|
|
|
virtual void visit(AstSysIgnore* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
if (m_doNConst) {
|
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-20 16:48:31 +00:00
|
|
|
|
// Simplify
|
|
|
|
|
virtual void visit(AstBasicDType* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
nodep->cvtRangeConst();
|
|
|
|
|
}
|
|
|
|
|
|
2010-02-14 15:01:21 +00:00
|
|
|
|
//-----
|
|
|
|
|
// Jump elimination
|
|
|
|
|
|
|
|
|
|
virtual void visit(AstJumpGo* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
2010-12-29 01:46:13 +00:00
|
|
|
|
if (m_doExpensive) { nodep->labelp()->user4(true); }
|
2010-02-14 15:01:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void visit(AstJumpLabel* nodep, AstNUser*) {
|
|
|
|
|
// 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
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
// AstJumpGo's below here that point to this node will set user4
|
2010-12-29 01:46:13 +00:00
|
|
|
|
if (m_doExpensive && !nodep->user4()) {
|
2010-02-14 15:01:21 +00:00
|
|
|
|
UINFO(4,"JUMPLABEL => unused "<<nodep<<endl);
|
|
|
|
|
AstNode* underp = NULL;
|
|
|
|
|
if (nodep->stmtsp()) underp = nodep->stmtsp()->unlinkFrBackWithNext();
|
|
|
|
|
if (underp) nodep->replaceWith(underp);
|
|
|
|
|
else nodep->unlinkFrBack();
|
|
|
|
|
nodep->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//-----
|
|
|
|
|
// Below lines are magic expressions processed by astgen
|
|
|
|
|
// "AstNODETYPE { # bracket not paren
|
|
|
|
|
// $accessor_name, ...
|
2007-11-30 22:12:53 +00:00
|
|
|
|
// # ,, gets replaced with a , rather than &&
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// }" # bracket not paren
|
2008-11-22 22:37:20 +00:00
|
|
|
|
// ,"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.
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
// Lint Checks
|
|
|
|
|
// v--- *1* These ops are always first, as we warn before replacing
|
2010-12-29 01:46:13 +00:00
|
|
|
|
// v--- *V* This op is a verilog op, only in m_doV mode
|
2010-12-29 02:23:16 +00:00
|
|
|
|
// v--- *C* This op works on all constant children, allowed in m_doConst mode
|
2012-04-20 02:53:52 +00:00
|
|
|
|
// v--- *S* This op specifies a type should use short-circuting of its lhs op
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
TREEOP1("AstSel{warnSelect(nodep)}", "NEVER");
|
|
|
|
|
// Generic constants on both side. Do this first to avoid other replacements
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOPC("AstNodeBiop {$lhsp.castConst, $rhsp.castConst}", "replaceConst(nodep)");
|
|
|
|
|
TREEOPC("AstNodeUniop{$lhsp.castConst, !nodep->isOpaque()}", "replaceConst(nodep)");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Zero on one side or the other
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstAdd {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
|
2011-12-22 13:33:16 +00:00
|
|
|
|
TREEOP ("AstAnd {$lhsp.isZero, $rhsp, isTPure($rhsp)}", "replaceZero(nodep)"); // Can't use replaceZeroChkPure as we make this pattern in ChkPure
|
2012-04-20 02:53:52 +00:00
|
|
|
|
// This visit function here must allow for short-circuiting.
|
|
|
|
|
TREEOPS("AstLogAnd {$lhsp.isZero}", "replaceZero(nodep)");
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstLogAnd{$lhsp.isZero, $rhsp}", "replaceZero(nodep)");
|
2012-04-20 02:53:52 +00:00
|
|
|
|
// This visit function here must allow for short-circuiting.
|
|
|
|
|
TREEOPS("AstLogOr {$lhsp.isOne}", "replaceNum(nodep, 1)");
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstLogOr {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
|
2011-12-22 13:33:16 +00:00
|
|
|
|
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 {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
|
|
|
|
TREEOP ("AstPowS {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstOr {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
|
2011-12-22 13:33:16 +00:00
|
|
|
|
TREEOP ("AstShiftL{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
|
|
|
|
TREEOP ("AstShiftR{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
|
|
|
|
TREEOP ("AstShiftRS{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstXor {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
|
|
|
|
|
TREEOP ("AstXnor {$lhsp.isZero, $rhsp}", "AstNot{$rhsp}");
|
2011-07-08 10:03:07 +00:00
|
|
|
|
TREEOP ("AstSub {$lhsp.isZero, $rhsp}", "AstNegate{$rhsp}");
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstAdd {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
2011-12-22 13:33:16 +00:00
|
|
|
|
TREEOP ("AstAnd {$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)");
|
|
|
|
|
TREEOP ("AstLogAnd{$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)");
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstLogOr {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
2011-12-22 13:33:16 +00:00
|
|
|
|
TREEOP ("AstMul {$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)");
|
|
|
|
|
TREEOP ("AstMulS {$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)");
|
2010-12-29 02:23:16 +00:00
|
|
|
|
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)");
|
|
|
|
|
TREEOP ("AstXnor {$lhsp, $rhsp.isZero}", "AstNot{$lhsp}");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Non-zero on one side or the other
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstAnd {$lhsp.isAllOnes, $rhsp}", "replaceWRhs(nodep)");
|
|
|
|
|
TREEOP ("AstLogAnd{$lhsp.isNeqZero, $rhsp}", "replaceWRhs(nodep)");
|
2011-12-22 13:33:16 +00:00
|
|
|
|
TREEOP ("AstOr {$lhsp.isAllOnes, $rhsp, isTPure($rhsp)}", "replaceWLhs(nodep)"); //->allOnes
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstLogOr {$lhsp.isNeqZero, $rhsp}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOP ("AstAnd {$lhsp, $rhsp.isAllOnes}", "replaceWLhs(nodep)");
|
|
|
|
|
TREEOP ("AstLogAnd{$lhsp, $rhsp.isNeqZero}", "replaceWLhs(nodep)");
|
2011-12-22 13:33:16 +00:00
|
|
|
|
TREEOP ("AstOr {$lhsp, $rhsp.isAllOnes, isTPure($lhsp)}", "replaceWRhs(nodep)"); //->allOnes
|
|
|
|
|
TREEOP ("AstLogOr {$lhsp, $rhsp.isNeqZero, isTPure($lhsp)}", "replaceNum(nodep,1)");
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstXor {$lhsp.isAllOnes, $rhsp}", "AstNot{$rhsp}");
|
|
|
|
|
TREEOP ("AstXnor {$lhsp.isAllOnes, $rhsp}", "replaceWRhs(nodep)");
|
|
|
|
|
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), $rhsp}", "replaceMulShift(nodep)"); // a*2^n -> a<<n
|
|
|
|
|
TREEOP ("AstDiv {$lhsp, operandIsPowTwo($rhsp)}", "replaceDivShift(nodep)"); // a/2^n -> a>>n
|
|
|
|
|
TREEOP ("AstPow {operandIsTwo($lhsp), $rhsp}", "replacePowShift(nodep)"); // 2**a == 1<<a
|
|
|
|
|
TREEOP ("AstPowS {operandIsTwo($lhsp), $rhsp}", "replacePowShift(nodep)"); // 2**a == 1<<a
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Trinary ops
|
|
|
|
|
// Note V3Case::Sel requires Cond to always be conditionally executed in C to prevent core dump!
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstNodeCond{$condp.isZero, $expr1p, $expr2p}", "replaceWChild(nodep,$expr2p)");
|
|
|
|
|
TREEOP ("AstNodeCond{$condp.isNeqZero, $expr1p, $expr2p}", "replaceWChild(nodep,$expr1p)");
|
2010-12-29 12:56:22 +00:00
|
|
|
|
TREEOPC("AstNodeCond{$condp.isZero, $expr1p.castConst, $expr2p.castConst}", "replaceWChild(nodep,$expr2p)");
|
|
|
|
|
TREEOPC("AstNodeCond{$condp.isNeqZero, $expr1p.castConst, $expr2p.castConst}", "replaceWChild(nodep,$expr1p)");
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstNodeCond{$condp, operandsSame($expr1p,,$expr2p)}","replaceWChild(nodep,$expr1p)");
|
2012-04-20 02:53:52 +00:00
|
|
|
|
// This visit function here must allow for short-circuiting.
|
|
|
|
|
TREEOPS("AstCond {$lhsp.isZero}", "replaceWIteratedThs(nodep)");
|
|
|
|
|
TREEOPS("AstCond {$lhsp.isNeqZero}", "replaceWIteratedRhs(nodep)");
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstCond{$condp->castNot(), $expr1p, $expr2p}", "AstCond{$condp->op1p(), $expr2p, $expr1p}");
|
2011-12-22 13:33:16 +00:00
|
|
|
|
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
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstNodeCond{!$condp.width1, operandBoolShift(nodep->condp())}", "replaceBoolShift(nodep->condp())");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Prefer constants on left, since that often needs a shift, it lets constant red remove the shift
|
2010-12-29 02:23:16 +00:00
|
|
|
|
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)");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// 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()==$lhsp->castNot()->lhsp()->width()}", "replaceWChild(nodep, $lhsp->op1p())"); // NOT(NOT(x))->x
|
|
|
|
|
TREEOP ("AstLogNot{$lhsp.castLogNot}", "replaceWChild(nodep, $lhsp->op1p())"); // NOT(NOT(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()}");
|
2007-07-18 15:01:39 +00:00
|
|
|
|
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()}");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
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)");
|
2011-03-12 12:45:04 +00:00
|
|
|
|
// Compress out EXTENDs to appease loop unroller
|
|
|
|
|
TREEOPV("AstEq {$lhsp.castExtend,operandBiExtendConst(nodep)}", "DONE");
|
|
|
|
|
TREEOPV("AstNeq {$lhsp.castExtend,operandBiExtendConst(nodep)}", "DONE");
|
|
|
|
|
TREEOPV("AstGt {$lhsp.castExtend,operandBiExtendConst(nodep)}", "DONE");
|
|
|
|
|
TREEOPV("AstGte {$lhsp.castExtend,operandBiExtendConst(nodep)}", "DONE");
|
|
|
|
|
TREEOPV("AstLt {$lhsp.castExtend,operandBiExtendConst(nodep)}", "DONE");
|
|
|
|
|
TREEOPV("AstLte {$lhsp.castExtend,operandBiExtendConst(nodep)}", "DONE");
|
2006-09-27 18:00:53 +00:00
|
|
|
|
// Identical operands on both sides
|
|
|
|
|
// AstLogAnd/AstLogOr already converted to AstAnd/AstOr for these rules
|
|
|
|
|
// AstAdd->ShiftL(#,1) but uncommon
|
2010-12-29 02:23:16 +00:00
|
|
|
|
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 ("AstXnor {operandsSame($lhsp,,$rhsp)}", "replaceAllOnes(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.
|
2011-07-24 19:01:51 +00:00
|
|
|
|
TREEOP ("AstEqD {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); // We let X==X -> 1, although in a true 4-state sim it's X.
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstEqCase {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOP ("AstEqWild {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOP ("AstGt {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
2011-07-24 19:01:51 +00:00
|
|
|
|
TREEOP ("AstGtD {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstGtS {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstGte {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
2011-07-24 19:01:51 +00:00
|
|
|
|
TREEOP ("AstGteD {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstGteS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOP ("AstLt {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
2011-07-24 19:01:51 +00:00
|
|
|
|
TREEOP ("AstLtD {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstLtS {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstLte {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
2011-07-24 19:01:51 +00:00
|
|
|
|
TREEOP ("AstLteD {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOP ("AstLteS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOP ("AstNeq {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
2011-07-24 19:01:51 +00:00
|
|
|
|
TREEOP ("AstNeqD {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
2010-12-29 02:23:16 +00:00
|
|
|
|
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)");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
///=== 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()>$lhsp->castExtend()->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)");
|
2007-11-30 22:12:53 +00:00
|
|
|
|
// Binary AND/OR is faster than logical and/or (usually)
|
2011-12-22 13:33:16 +00:00
|
|
|
|
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}");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// 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())");
|
2008-11-17 16:36:01 +00:00
|
|
|
|
// Common two-level operations that can be simplified
|
|
|
|
|
TREEOP ("AstAnd {$lhsp.castOr, $rhsp.castOr, operandAndOrSame(nodep)}", "replaceAndOr(nodep)");
|
|
|
|
|
TREEOP ("AstOr {$lhsp.castAnd,$rhsp.castAnd,operandAndOrSame(nodep)}", "replaceAndOr(nodep)");
|
2009-01-07 14:37:59 +00:00
|
|
|
|
TREEOP ("AstOr {matchOrAndNot(nodep)}", "DONE");
|
2008-11-17 16:36:01 +00:00
|
|
|
|
TREEOP ("AstAnd {operandShiftSame(nodep)}", "replaceShiftSame(nodep)");
|
|
|
|
|
TREEOP ("AstOr {operandShiftSame(nodep)}", "replaceShiftSame(nodep)");
|
|
|
|
|
TREEOP ("AstXor {operandShiftSame(nodep)}", "replaceShiftSame(nodep)");
|
|
|
|
|
TREEOP ("AstXnor{operandShiftSame(nodep)}", "replaceShiftSame(nodep)");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// 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, nodep->lhsp()->castExtend()->lhsp())");
|
|
|
|
|
TREEOPV("AstExtendS{$lhsp.castExtendS}", "replaceExtend(nodep, nodep->lhsp()->castExtendS()->lhsp())");
|
|
|
|
|
TREEOPV("AstReplicate{$lhsp, $rhsp.isOne, $lhsp->width()==nodep->width()}", "replaceWLhs(nodep)"); // {1{lhs}}->lhs
|
|
|
|
|
// Next rule because AUTOINST puts the width of bits in
|
|
|
|
|
// to pins, even when the widths are exactly the same across the hierarchy.
|
2011-03-12 12:45:04 +00:00
|
|
|
|
TREEOPV("AstSel{operandSelExtend(nodep)}", "DONE");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
TREEOPV("AstSel{operandSelFull(nodep)}", "replaceWChild(nodep, nodep->fromp())");
|
|
|
|
|
TREEOPV("AstSel{$fromp.castSel}", "replaceSelSel(nodep)");
|
2011-03-12 12:45:04 +00:00
|
|
|
|
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.castXnor,operandSelBiLower(nodep)}", "DONE");
|
|
|
|
|
TREEOPV("AstSel{$fromp.castXor, operandSelBiLower(nodep)}", "DONE");
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOPC("AstSel{$fromp.castConst, $lsbp.castConst, $widthp.castConst, }", "replaceConst(nodep)");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
TREEOPV("AstSel{$fromp.castConcat, $lsbp.castConst, $widthp.castConst, }", "replaceSelConcat(nodep)");
|
|
|
|
|
TREEOPV("AstSel{$fromp.castReplicate, $lsbp.castConst, $widthp.isOne, }", "replaceSelReplicate(nodep)");
|
2011-02-24 02:21:59 +00:00
|
|
|
|
// V3Tristate requires selects below BufIf1.
|
2011-02-24 02:36:38 +00:00
|
|
|
|
// Also do additional operators that are bit-independent, but only definite
|
2011-02-24 02:21:59 +00:00
|
|
|
|
// win if bit select is a constant (otherwise we may need to compute bit index several times)
|
2011-02-24 02:36:38 +00:00
|
|
|
|
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)");
|
|
|
|
|
TREEOPV("AstSel{$fromp.castXnor,$lhsp.castConst}", "replaceSelIntoUniop(nodep)");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Conversions
|
2006-09-27 18:00:53 +00:00
|
|
|
|
TREEOPV("AstRedXnor{$lhsp}", "AstNot{AstRedXor{$lhsp}}"); // Just eliminate XNOR's
|
2012-04-20 02:53:52 +00:00
|
|
|
|
// This visit function here must allow for short-circuiting.
|
|
|
|
|
TREEOPS("AstLogIf {$lhsp.isZero}", "replaceNum(nodep, 1)");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
TREEOPV("AstLogIf {$lhsp, $rhsp}", "AstLogOr{AstLogNot{$lhsp},$rhsp}");
|
|
|
|
|
TREEOPV("AstLogIff{$lhsp, $rhsp}", "AstLogNot{AstXor{$lhsp,$rhsp}}");
|
2010-01-18 01:06:08 +00:00
|
|
|
|
// Strings
|
2010-12-29 02:23:16 +00:00
|
|
|
|
TREEOPC("AstCvtPackString{$lhsp.castConst}", "replaceConstString(nodep, nodep->lhsp()->castConst()->num().toString())");
|
2010-01-18 01:06:08 +00:00
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
// 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, AstNUser*) {
|
|
|
|
|
// Default: Just iterate
|
|
|
|
|
if (m_required) {
|
|
|
|
|
nodep->v3error("Expecting expression to be constant, but can't convert a "
|
2009-07-09 21:39:24 +00:00
|
|
|
|
<<nodep->prettyTypeName()<<" to constant.");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
} else {
|
|
|
|
|
// Calculate the width of this operation
|
|
|
|
|
if (m_params && !nodep->width()) {
|
2009-10-15 00:13:04 +00:00
|
|
|
|
nodep = V3Width::widthParamsEdit(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2010-12-29 01:46:13 +00:00
|
|
|
|
// Processing Mode Enum
|
|
|
|
|
enum ProcMode {
|
|
|
|
|
PROC_PARAMS,
|
2012-04-20 02:53:52 +00:00
|
|
|
|
PROC_GENERATE,
|
2010-12-29 12:56:22 +00:00
|
|
|
|
PROC_LIVE,
|
2010-12-29 01:46:13 +00:00
|
|
|
|
PROC_V_WARN,
|
|
|
|
|
PROC_V_NOWARN,
|
|
|
|
|
PROC_V_EXPENSIVE,
|
|
|
|
|
PROC_CPP
|
|
|
|
|
};
|
2012-03-20 20:01:53 +00:00
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// CONSTUCTORS
|
2010-12-29 01:46:13 +00:00
|
|
|
|
ConstVisitor(ProcMode pmode) {
|
|
|
|
|
m_params = false;
|
|
|
|
|
m_required = false;
|
|
|
|
|
m_doExpensive = false;
|
2010-12-29 02:23:16 +00:00
|
|
|
|
m_doNConst = false;
|
2011-12-22 13:33:16 +00:00
|
|
|
|
m_doShort = true; // Presently always done
|
2010-12-29 02:23:16 +00:00
|
|
|
|
m_doV = false;
|
2012-04-20 02:53:52 +00:00
|
|
|
|
m_doGenerate = false; // Inside generate conditionals
|
2010-12-29 01:46:13 +00:00
|
|
|
|
m_warn = false;
|
|
|
|
|
m_wremove = true; // Overridden in visitors
|
2006-08-26 11:35:28 +00:00
|
|
|
|
m_modp = NULL;
|
|
|
|
|
m_scopep = NULL;
|
2012-07-28 14:52:29 +00:00
|
|
|
|
m_attrp = NULL;
|
2010-12-29 01:46:13 +00:00
|
|
|
|
//
|
|
|
|
|
switch (pmode) {
|
2010-12-29 02:23:16 +00:00
|
|
|
|
case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true; m_required = true; break;
|
2012-04-20 02:53:52 +00:00
|
|
|
|
case PROC_GENERATE: m_doV = true; m_doNConst = true; m_params = true; m_required = true; m_doGenerate = true; break;
|
2010-12-29 12:56:22 +00:00
|
|
|
|
case PROC_LIVE: break;
|
2010-12-29 02:23:16 +00:00
|
|
|
|
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; break;
|
2010-12-29 01:46:13 +00:00
|
|
|
|
default: v3fatalSrc("Bad case"); break;
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
virtual ~ConstVisitor() {}
|
2009-10-15 00:13:04 +00:00
|
|
|
|
AstNode* mainAcceptEdit(AstNode* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Operate starting at a random place
|
2009-10-15 00:13:04 +00:00
|
|
|
|
return nodep->acceptSubtreeReturnEdits(*this);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Const class functions
|
|
|
|
|
|
2012-04-20 02:53:52 +00:00
|
|
|
|
//! Force this cell node's parameter list to become a constant
|
|
|
|
|
//! @return Pointer to the edited node.
|
2009-10-15 00:13:04 +00:00
|
|
|
|
AstNode* V3Const::constifyParamsEdit(AstNode* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//if (debug()>0) nodep->dumpTree(cout," forceConPRE : ");
|
2012-04-20 02:53:52 +00:00
|
|
|
|
// 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=nodep->castVar()) {
|
|
|
|
|
// 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
|
|
|
|
|
//! someting 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);
|
2009-07-16 18:49:34 +00:00
|
|
|
|
if (AstVar* varp=nodep->castVar()) {
|
|
|
|
|
// 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.
|
2010-01-21 23:20:47 +00:00
|
|
|
|
if (varp->valuep()) visitor.mainAcceptEdit(varp->valuep());
|
2009-07-16 18:49:34 +00:00
|
|
|
|
} else {
|
2009-10-15 00:13:04 +00:00
|
|
|
|
nodep = visitor.mainAcceptEdit(nodep);
|
2009-07-16 18:49:34 +00:00
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Because we do edits, nodep links may get trashed and core dump this.
|
|
|
|
|
//if (debug()>0) nodep->dumpTree(cout," forceConDONE: ");
|
2009-10-15 00:13:04 +00:00
|
|
|
|
return nodep;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-10-05 00:45:39 +00:00
|
|
|
|
void V3Const::constifyAllLint(AstNetlist* nodep) {
|
2008-11-22 22:27:55 +00:00
|
|
|
|
// Only call from Verilator.cpp, as it uses user#'s
|
2006-10-05 00:45:39 +00:00
|
|
|
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
2010-12-29 01:46:13 +00:00
|
|
|
|
ConstVisitor visitor (ConstVisitor::PROC_V_WARN);
|
2009-10-15 00:13:04 +00:00
|
|
|
|
(void)visitor.mainAcceptEdit(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-10-05 00:45:39 +00:00
|
|
|
|
void V3Const::constifyCpp(AstNetlist* nodep) {
|
|
|
|
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
2010-12-29 01:46:13 +00:00
|
|
|
|
ConstVisitor visitor (ConstVisitor::PROC_CPP);
|
2009-10-15 00:13:04 +00:00
|
|
|
|
(void)visitor.mainAcceptEdit(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-10-15 00:13:04 +00:00
|
|
|
|
AstNode* V3Const::constifyEdit(AstNode* nodep) {
|
2010-12-29 01:46:13 +00:00
|
|
|
|
ConstVisitor visitor (ConstVisitor::PROC_V_NOWARN);
|
2009-10-15 00:13:04 +00:00
|
|
|
|
nodep = visitor.mainAcceptEdit(nodep);
|
|
|
|
|
return nodep;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2009-01-07 14:37:59 +00:00
|
|
|
|
|
2010-12-29 02:23:16 +00:00
|
|
|
|
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);
|
2010-12-29 12:56:22 +00:00
|
|
|
|
ConstVisitor visitor (ConstVisitor::PROC_LIVE);
|
2010-12-29 02:23:16 +00:00
|
|
|
|
(void)visitor.mainAcceptEdit(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-29 01:46:13 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-15 00:13:04 +00:00
|
|
|
|
AstNode* V3Const::constifyExpensiveEdit(AstNode* nodep) {
|
2010-12-29 01:46:13 +00:00
|
|
|
|
ConstVisitor visitor (ConstVisitor::PROC_V_EXPENSIVE);
|
2009-10-15 00:13:04 +00:00
|
|
|
|
nodep = visitor.mainAcceptEdit(nodep);
|
|
|
|
|
return nodep;
|
2009-01-07 14:37:59 +00:00
|
|
|
|
}
|