2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Constant folding
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2023-01-01 16:18:39 +01:00
|
|
|
// Copyright 2003-2023 by Wilson Snyder. This program is free software; you
|
2020-03-21 16:24:24 +01:00
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 23:07:57 +02:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// CONST TRANSFORMATIONS:
|
|
|
|
|
// Call on one node for PARAM values, or netlist for overall constant folding:
|
2019-05-19 22:13:13 +02:00
|
|
|
// Bottom up traversal:
|
|
|
|
|
// Attempt to convert operands to constants
|
|
|
|
|
// If operands are constant, replace this node with constant.
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2006-12-18 20:20:45 +01:00
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
#include "V3Const.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Ast.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3Global.h"
|
2009-07-17 20:13:11 +02:00
|
|
|
#include "V3Simulate.h"
|
2021-02-21 10:11:33 +01:00
|
|
|
#include "V3Stats.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3String.h"
|
2021-08-11 15:30:00 +02:00
|
|
|
#include "V3UniqueNames.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3Width.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2018-10-14 19:43:24 +02:00
|
|
|
#include <algorithm>
|
2022-04-23 21:11:46 +02:00
|
|
|
#include <memory>
|
2021-08-18 20:15:02 +02:00
|
|
|
#include <type_traits>
|
2018-10-14 19:43:24 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2008-02-20 17:54:41 +01:00
|
|
|
//######################################################################
|
|
|
|
|
// Utilities
|
|
|
|
|
|
2021-08-18 20:15:02 +02:00
|
|
|
static bool isConst(const AstNode* nodep, uint64_t v) {
|
2021-10-22 16:15:42 +02:00
|
|
|
const AstConst* const constp = VN_CAST(nodep, Const);
|
2021-08-18 20:15:02 +02:00
|
|
|
return constp && constp->toUQuad() == v;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
|
static typename std::enable_if<std::is_integral<T>::value, bool>::type isPow2(T val) {
|
|
|
|
|
return (val & (val - 1)) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int countTrailingZeroes(uint64_t val) {
|
|
|
|
|
UASSERT(val, "countTrailingZeroes argument must be non-zero");
|
|
|
|
|
#if defined(__GNUC__) && !defined(VL_NO_BUILTINS)
|
|
|
|
|
return __builtin_ctzll(val);
|
|
|
|
|
#else
|
|
|
|
|
int bit = 0;
|
|
|
|
|
val = ~val;
|
|
|
|
|
while (val & 1) {
|
|
|
|
|
++bit;
|
|
|
|
|
val >>= 1;
|
|
|
|
|
}
|
|
|
|
|
return bit;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-21 10:11:33 +01:00
|
|
|
// This visitor can be used in the post-expanded Ast from V3Expand, where the Ast satisfies:
|
|
|
|
|
// - Constants are 64 bit at most (because words are accessed via AstWordSel)
|
|
|
|
|
// - Variables are scoped.
|
2023-03-18 17:17:25 +01:00
|
|
|
class ConstBitOpTreeVisitor final : public VNVisitorConst {
|
2021-08-18 20:15:02 +02:00
|
|
|
// NODE STATE
|
|
|
|
|
// AstVarRef::user4u -> Base index of m_varInfos that points VarInfo
|
|
|
|
|
// AstVarScope::user4u -> Same as AstVarRef::user4
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser4InUse m_inuser4;
|
2021-08-18 20:15:02 +02:00
|
|
|
|
2021-02-21 10:11:33 +01:00
|
|
|
// TYPES
|
|
|
|
|
|
2021-08-18 20:15:02 +02:00
|
|
|
// Holds a node to be added as a term in the reduction tree, it's equivalent op count, and a
|
|
|
|
|
// bool indicating if the term is clean (0/1 value, or if the top bits might be dirty)
|
2022-11-13 21:33:11 +01:00
|
|
|
using ResultTerm = std::tuple<AstNodeExpr*, unsigned, bool>;
|
2021-08-18 20:15:02 +02:00
|
|
|
|
2022-06-27 15:41:33 +02:00
|
|
|
class LeafInfo final { // Leaf node (either AstConst or AstVarRef)
|
2022-07-06 01:33:37 +02:00
|
|
|
// MEMBERS
|
2021-02-21 10:11:33 +01:00
|
|
|
bool m_polarity = true;
|
2022-07-06 01:33:37 +02:00
|
|
|
int m_lsb = 0; // LSB of actually used bit of m_refp->varp()
|
|
|
|
|
int m_msb = 0; // MSB of actually used bit of m_refp->varp()
|
2021-02-21 10:11:33 +01:00
|
|
|
int m_wordIdx = -1; // -1 means AstWordSel is not used.
|
|
|
|
|
AstVarRef* m_refp = nullptr;
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstConst* m_constp = nullptr;
|
2021-08-18 20:15:02 +02:00
|
|
|
|
2022-06-27 15:41:33 +02:00
|
|
|
public:
|
2022-07-06 01:33:37 +02:00
|
|
|
// CONSTRUCTORS
|
|
|
|
|
LeafInfo() = default;
|
|
|
|
|
LeafInfo(const LeafInfo& other) = default;
|
|
|
|
|
explicit LeafInfo(int lsb)
|
|
|
|
|
: m_lsb{lsb} {}
|
|
|
|
|
|
|
|
|
|
// METHODS
|
2022-06-27 15:41:33 +02:00
|
|
|
void setLeaf(AstVarRef* refp) {
|
2022-11-30 00:32:28 +01:00
|
|
|
UASSERT_OBJ(!m_refp && !m_constp, refp, "Must be called just once");
|
2022-06-27 15:41:33 +02:00
|
|
|
m_refp = refp;
|
2022-07-06 01:33:37 +02:00
|
|
|
m_msb = refp->varp()->widthMin() - 1;
|
2022-06-27 15:41:33 +02:00
|
|
|
}
|
|
|
|
|
void setLeaf(const AstConst* constp) {
|
2022-11-30 00:32:28 +01:00
|
|
|
UASSERT_OBJ(!m_refp && !m_constp, constp, "Must be called just once");
|
2022-06-27 15:41:33 +02:00
|
|
|
m_constp = constp;
|
2022-07-06 01:33:37 +02:00
|
|
|
m_msb = constp->widthMin() - 1;
|
2022-06-27 15:41:33 +02:00
|
|
|
}
|
2022-07-06 01:33:37 +02:00
|
|
|
void updateBitRange(const AstCCast* castp) {
|
|
|
|
|
m_msb = std::min(m_msb, m_lsb + castp->width() - 1);
|
|
|
|
|
}
|
|
|
|
|
void updateBitRange(const AstShiftR* shiftp) {
|
|
|
|
|
m_lsb += VN_AS(shiftp->rhsp(), Const)->toUInt();
|
|
|
|
|
}
|
|
|
|
|
void wordIdx(int i) { m_wordIdx = i; }
|
|
|
|
|
void polarity(bool p) { m_polarity = p; }
|
|
|
|
|
|
2022-06-27 15:41:33 +02:00
|
|
|
AstVarRef* refp() const { return m_refp; }
|
|
|
|
|
const AstConst* constp() const { return m_constp; }
|
|
|
|
|
int wordIdx() const { return m_wordIdx; }
|
|
|
|
|
bool polarity() const { return m_polarity; }
|
2022-12-01 14:00:24 +01:00
|
|
|
bool missingWordSel() const {
|
|
|
|
|
// When V3Expand is skipped, WordSel is not inserted.
|
|
|
|
|
return m_refp->isWide() && m_wordIdx == -1;
|
|
|
|
|
}
|
2022-06-27 15:41:33 +02:00
|
|
|
int lsb() const { return m_lsb; }
|
|
|
|
|
|
2022-07-06 01:33:37 +02:00
|
|
|
int msb() const { return std::min(m_msb, varWidth() - 1); }
|
2022-06-27 15:41:33 +02:00
|
|
|
int varWidth() const {
|
2021-08-18 20:15:02 +02:00
|
|
|
UASSERT(m_refp, "m_refp should be set");
|
|
|
|
|
const int width = m_refp->varp()->widthMin();
|
|
|
|
|
if (!m_refp->isWide()) {
|
2022-11-30 00:32:28 +01:00
|
|
|
UASSERT_OBJ(m_wordIdx == -1, m_refp, "Bad word index into non-wide");
|
2021-08-18 20:15:02 +02:00
|
|
|
return width;
|
|
|
|
|
} else {
|
2022-12-01 14:00:24 +01:00
|
|
|
if (missingWordSel()) return width;
|
2022-11-30 00:32:28 +01:00
|
|
|
UASSERT_OBJ(m_wordIdx >= 0, m_refp, "Bad word index into wide");
|
2021-08-18 20:15:02 +02:00
|
|
|
const int bitsInMSW = VL_BITBIT_E(width) ? VL_BITBIT_E(width) : VL_EDATASIZE;
|
|
|
|
|
return m_wordIdx == m_refp->widthWords() - 1 ? bitsInMSW : VL_EDATASIZE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-02-21 10:11:33 +01:00
|
|
|
};
|
2021-06-21 00:28:39 +02:00
|
|
|
|
|
|
|
|
struct BitPolarityEntry final { // Found bit polarity during iterate()
|
|
|
|
|
LeafInfo m_info;
|
|
|
|
|
bool m_polarity;
|
|
|
|
|
int m_bit;
|
|
|
|
|
BitPolarityEntry(const LeafInfo& info, bool pol, int bit)
|
2021-07-12 00:42:01 +02:00
|
|
|
: m_info{info}
|
|
|
|
|
, m_polarity{pol}
|
|
|
|
|
, m_bit{bit} {}
|
2021-06-21 00:28:39 +02:00
|
|
|
BitPolarityEntry() = default;
|
|
|
|
|
};
|
|
|
|
|
|
2022-06-01 02:26:16 +02:00
|
|
|
struct FrozenNodeInfo final { // Context when a frozen node is found
|
|
|
|
|
bool m_polarity;
|
|
|
|
|
int m_lsb;
|
|
|
|
|
bool operator<(const FrozenNodeInfo& other) const {
|
|
|
|
|
if (m_lsb != other.m_lsb) return m_lsb < other.m_lsb;
|
|
|
|
|
return m_polarity < other.m_polarity;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-06-21 00:28:39 +02:00
|
|
|
class Restorer final { // Restore the original state unless disableRestore() is called
|
|
|
|
|
ConstBitOpTreeVisitor& m_visitor;
|
|
|
|
|
const size_t m_polaritiesSize;
|
|
|
|
|
const size_t m_frozenSize;
|
2021-08-18 20:15:02 +02:00
|
|
|
const unsigned m_ops;
|
2021-06-21 00:28:39 +02:00
|
|
|
const bool m_polarity;
|
|
|
|
|
bool m_restore;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
explicit Restorer(ConstBitOpTreeVisitor& visitor)
|
2021-07-12 00:42:01 +02:00
|
|
|
: m_visitor{visitor}
|
|
|
|
|
, m_polaritiesSize{visitor.m_bitPolarities.size()}
|
|
|
|
|
, m_frozenSize{visitor.m_frozenNodes.size()}
|
|
|
|
|
, m_ops{visitor.m_ops}
|
|
|
|
|
, m_polarity{visitor.m_polarity}
|
|
|
|
|
, m_restore{true} {}
|
2021-06-21 00:28:39 +02:00
|
|
|
~Restorer() {
|
|
|
|
|
UASSERT(m_visitor.m_bitPolarities.size() >= m_polaritiesSize,
|
|
|
|
|
"m_bitPolarities must grow monotorilaclly");
|
|
|
|
|
UASSERT(m_visitor.m_frozenNodes.size() >= m_frozenSize,
|
|
|
|
|
"m_frozenNodes must grow monotorilaclly");
|
|
|
|
|
if (m_restore) restoreNow();
|
|
|
|
|
}
|
|
|
|
|
void disableRestore() { m_restore = false; }
|
|
|
|
|
void restoreNow() {
|
|
|
|
|
UASSERT(m_restore, "Can be called just once");
|
|
|
|
|
m_visitor.m_bitPolarities.resize(m_polaritiesSize);
|
|
|
|
|
m_visitor.m_frozenNodes.resize(m_frozenSize);
|
|
|
|
|
m_visitor.m_ops = m_ops;
|
|
|
|
|
m_visitor.m_polarity = m_polarity;
|
|
|
|
|
m_restore = false;
|
|
|
|
|
}
|
|
|
|
|
};
|
2021-02-21 10:11:33 +01:00
|
|
|
// Collect information for each Variable to transform as below
|
|
|
|
|
class VarInfo final {
|
|
|
|
|
// MEMBERS
|
2021-08-18 20:15:02 +02:00
|
|
|
int m_knownResult = -1; // -1: result is not known, 0 or 1: result of this tree
|
2021-11-26 23:55:36 +01:00
|
|
|
const ConstBitOpTreeVisitor* const
|
|
|
|
|
m_parentp; // ConstBitOpTreeVisitor holding this VarInfo
|
2021-08-18 20:15:02 +02:00
|
|
|
AstVarRef* const m_refp; // Points the variable that this VarInfo covers
|
|
|
|
|
const int m_width; // Width of term this VarInfo refers to
|
2021-02-21 10:11:33 +01:00
|
|
|
V3Number m_bitPolarity; // Coefficient of each bit
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// METHODS
|
2021-08-18 20:15:02 +02:00
|
|
|
bool hasConstResult() const { return m_knownResult >= 0 || m_bitPolarity.isAllX(); }
|
|
|
|
|
// The constant result. Only valid if hasConstResult() returned true.
|
|
|
|
|
bool getConstResult() const {
|
|
|
|
|
// Note that this condition covers m_knownResult == -1 but m_bitPolarity.isAllX(),
|
|
|
|
|
// in which case the result is 0
|
|
|
|
|
return m_knownResult == 1;
|
|
|
|
|
}
|
|
|
|
|
const AstVarRef* refp() const { return m_refp; }
|
2021-05-04 03:40:16 +02:00
|
|
|
bool sameVarAs(const AstNodeVarRef* otherp) const { return m_refp->same(otherp); }
|
2021-02-21 10:11:33 +01:00
|
|
|
void setPolarity(bool compBit, int bit) {
|
2021-08-18 20:15:02 +02:00
|
|
|
// Ignore if already determined a known reduction
|
|
|
|
|
if (m_knownResult >= 0) return;
|
|
|
|
|
UASSERT_OBJ(bit < m_width, m_refp,
|
|
|
|
|
"Bit index out of range: " << bit << " width: " << m_width);
|
|
|
|
|
if (m_bitPolarity.bitIsX(bit)) { // The bit is not yet marked with either polarity
|
2021-02-21 10:11:33 +01:00
|
|
|
m_bitPolarity.setBit(bit, compBit);
|
2021-08-18 20:15:02 +02:00
|
|
|
} else { // The bit has already been marked with some polarity
|
2021-02-21 10:11:33 +01:00
|
|
|
const bool sameFlag = m_bitPolarity.bitIs1(bit) == compBit;
|
|
|
|
|
if (m_parentp->isXorTree()) {
|
2021-08-18 20:15:02 +02:00
|
|
|
UASSERT_OBJ(compBit && sameFlag, m_refp, "Only true is set in Xor tree");
|
|
|
|
|
// a ^ a ^ b == b so we can ignore a
|
2021-02-21 10:11:33 +01:00
|
|
|
m_bitPolarity.setBit(bit, 'x');
|
|
|
|
|
} else { // And, Or
|
2021-08-18 20:15:02 +02:00
|
|
|
// Can ignore this nodep as the bit is already marked with the same polarity
|
2021-02-21 10:11:33 +01:00
|
|
|
if (sameFlag) return; // a & a == a, b | b == b
|
2021-08-18 20:15:02 +02:00
|
|
|
// Otherwise result is constant (a & ~a == 0) or (a | ~a == 1)
|
|
|
|
|
m_knownResult = m_parentp->isAndTree() ? 0 : 1;
|
2021-02-21 10:11:33 +01:00
|
|
|
m_bitPolarity.setAllBitsX(); // The variable is not referred anymore
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-08-18 20:15:02 +02:00
|
|
|
|
|
|
|
|
// Return reduction term for this VarInfo, together with the number of ops in the term,
|
|
|
|
|
// and a boolean indicating if the term is clean (1-bit vs multi-bit value)
|
|
|
|
|
ResultTerm getResultTerm() const {
|
2022-11-30 00:32:28 +01:00
|
|
|
UASSERT_OBJ(!hasConstResult(), m_refp, "getTerm on reduction that yields constant");
|
2021-08-18 20:15:02 +02:00
|
|
|
FileLine* const fl = m_refp->fileline();
|
|
|
|
|
|
|
|
|
|
// Get the term we are referencing (the WordSel, if wide, otherwise just the VarRef)
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* srcp = VN_CAST(m_refp->backp(), WordSel);
|
2021-02-21 10:11:33 +01:00
|
|
|
if (!srcp) srcp = m_refp;
|
2021-08-18 20:15:02 +02:00
|
|
|
srcp = srcp->cloneTree(false);
|
|
|
|
|
|
|
|
|
|
// Signed variables might have redundant sign bits that need masking.
|
|
|
|
|
const bool hasRedundantSignBits
|
|
|
|
|
= m_refp->varp()->dtypep()->isSigned()
|
|
|
|
|
&& (m_refp->isWide() ? (m_width != VL_EDATASIZE)
|
|
|
|
|
: (m_width < 8 || !isPow2(m_width)));
|
|
|
|
|
|
|
|
|
|
// Get the mask that selects the bits that are relevant in this term
|
|
|
|
|
V3Number maskNum{srcp, m_width, 0};
|
|
|
|
|
maskNum.opBitsNonX(m_bitPolarity); // 'x' -> 0, 0->1, 1->1
|
|
|
|
|
const uint64_t maskVal = maskNum.toUQuad();
|
2022-11-30 00:32:28 +01:00
|
|
|
UASSERT_OBJ(maskVal != 0, m_refp,
|
|
|
|
|
"Should have been recognized as having const 0 result");
|
2021-08-18 20:15:02 +02:00
|
|
|
|
|
|
|
|
// Parts of the return value
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* resultp = srcp; // The tree for this term
|
2021-08-18 20:15:02 +02:00
|
|
|
unsigned ops = 0; // Number of ops in this term
|
|
|
|
|
bool clean = false; // Whether the term is clean (has value 0 or 1)
|
|
|
|
|
|
|
|
|
|
if (isPow2(maskVal)) {
|
|
|
|
|
// If we only want a single bit, shift it out instead of a masked compare. Shifts
|
|
|
|
|
// don't go through the flags register on x86 and are hence faster. This is also
|
|
|
|
|
// always fewer or same ops as mask and compare, but with shorter instructions on
|
|
|
|
|
// x86.
|
|
|
|
|
|
|
|
|
|
// Find the index of the bit we want.
|
|
|
|
|
const int bit = countTrailingZeroes(maskVal);
|
|
|
|
|
// If we want something other than the bottom bit, shift it out
|
|
|
|
|
if (bit != 0) {
|
|
|
|
|
resultp = new AstShiftR{fl, resultp,
|
|
|
|
|
new AstConst{fl, static_cast<uint32_t>(bit)}, m_width};
|
|
|
|
|
++ops;
|
|
|
|
|
}
|
|
|
|
|
// Negate it if necessary
|
|
|
|
|
const bool negate = m_bitPolarity.bitIs0(bit);
|
|
|
|
|
if (negate) {
|
|
|
|
|
resultp = new AstNot{fl, resultp};
|
|
|
|
|
++ops;
|
|
|
|
|
}
|
|
|
|
|
// Clean if MSB of unsigned value, and not negated
|
|
|
|
|
clean = (bit == m_width - 1) && !hasRedundantSignBits && !negate;
|
2021-02-21 10:11:33 +01:00
|
|
|
} else {
|
2021-08-18 20:15:02 +02:00
|
|
|
// We want multiple bits. Go ahead and extract them.
|
|
|
|
|
|
|
|
|
|
// Check if masking is required, and if so apply it
|
|
|
|
|
const bool needsMasking = maskVal != VL_MASK_Q(m_width) || hasRedundantSignBits;
|
|
|
|
|
if (needsMasking) {
|
|
|
|
|
resultp = new AstAnd{fl, new AstConst{fl, maskNum}, resultp};
|
|
|
|
|
++ops;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create the sub-expression for this term
|
|
|
|
|
if (m_parentp->isXorTree()) {
|
|
|
|
|
if (needsMasking) {
|
|
|
|
|
// Reduce the masked term to the minimum known width,
|
|
|
|
|
// to use the smallest RedXor formula
|
|
|
|
|
const int widthMin = maskNum.widthMin();
|
|
|
|
|
resultp->dtypeChgWidth(widthMin, widthMin);
|
|
|
|
|
}
|
|
|
|
|
resultp = new AstRedXor{fl, resultp};
|
|
|
|
|
++ops;
|
|
|
|
|
clean = false;
|
|
|
|
|
// VL_REDXOR_* returns IData, set width accordingly to avoid unnecessary casts
|
|
|
|
|
resultp->dtypeChgWidth(VL_IDATASIZE, 1);
|
|
|
|
|
} else if (m_parentp->isAndTree()) {
|
|
|
|
|
V3Number compNum{srcp, m_width, 0};
|
|
|
|
|
compNum.opBitsOne(m_bitPolarity); // 'x'->0, 0->0, 1->1
|
|
|
|
|
resultp = new AstEq{fl, new AstConst{fl, compNum}, resultp};
|
|
|
|
|
++ops;
|
|
|
|
|
clean = true;
|
2021-02-21 10:11:33 +01:00
|
|
|
} else { // Or
|
2021-08-18 20:15:02 +02:00
|
|
|
V3Number compNum{srcp, m_width, 0};
|
|
|
|
|
compNum.opBitsOne(m_bitPolarity); // 'x'->0, 0->0, 1->1
|
|
|
|
|
compNum.opXor(V3Number{compNum}, maskNum);
|
|
|
|
|
resultp = new AstNeq{fl, new AstConst{fl, compNum}, resultp};
|
|
|
|
|
++ops;
|
|
|
|
|
clean = true;
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
|
|
|
|
}
|
2021-08-18 20:15:02 +02:00
|
|
|
|
|
|
|
|
return ResultTerm{resultp, ops, clean};
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CONSTRUCTORS
|
2021-08-18 20:15:02 +02:00
|
|
|
VarInfo(ConstBitOpTreeVisitor* parent, AstVarRef* refp, int width)
|
2021-07-12 00:42:01 +02:00
|
|
|
: m_parentp{parent}
|
|
|
|
|
, m_refp{refp}
|
2021-08-18 20:15:02 +02:00
|
|
|
, m_width{width}
|
|
|
|
|
, m_bitPolarity{refp, m_width} {
|
2021-02-21 10:11:33 +01:00
|
|
|
m_bitPolarity.setAllBitsX();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// MEMBERS
|
|
|
|
|
bool m_failed = false;
|
|
|
|
|
bool m_polarity = true; // Flip when AstNot comes
|
2021-08-18 20:15:02 +02:00
|
|
|
unsigned m_ops; // Number of operations such as And, Or, Xor, Sel...
|
2021-02-21 10:11:33 +01:00
|
|
|
int m_lsb = 0; // Current LSB
|
|
|
|
|
LeafInfo* m_leafp = nullptr; // AstConst or AstVarRef that currently looking for
|
2022-11-13 21:33:11 +01:00
|
|
|
const AstNodeExpr* const m_rootp; // Root of this AST subtree
|
2021-02-21 10:11:33 +01:00
|
|
|
|
2022-11-13 21:33:11 +01:00
|
|
|
std::vector<std::pair<AstNodeExpr*, FrozenNodeInfo>>
|
2022-06-01 02:26:16 +02:00
|
|
|
m_frozenNodes; // Nodes that cannot be optimized
|
2021-06-21 00:28:39 +02:00
|
|
|
std::vector<BitPolarityEntry> m_bitPolarities; // Polarity of bits found during iterate()
|
|
|
|
|
std::vector<std::unique_ptr<VarInfo>> m_varInfos; // VarInfo for each variable, [0] is nullptr
|
2021-02-21 10:11:33 +01:00
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
|
|
|
|
|
bool isAndTree() const { return VN_IS(m_rootp, And); }
|
|
|
|
|
bool isOrTree() const { return VN_IS(m_rootp, Or); }
|
|
|
|
|
bool isXorTree() const { return VN_IS(m_rootp, Xor) || VN_IS(m_rootp, RedXor); }
|
|
|
|
|
|
|
|
|
|
#define CONST_BITOP_RETURN_IF(cond, nodep) \
|
|
|
|
|
if (setFailed(cond, #cond, nodep, __LINE__)) return
|
|
|
|
|
|
|
|
|
|
#define CONST_BITOP_SET_FAILED(reason, nodep) setFailed(true, reason, nodep, __LINE__)
|
|
|
|
|
|
|
|
|
|
bool setFailed(bool fail, const char* reason, AstNode* nodep, int line) {
|
2021-09-02 02:39:59 +02:00
|
|
|
if (fail && !m_failed) {
|
2021-02-21 10:11:33 +01:00
|
|
|
UINFO(9, "cannot optimize " << m_rootp << " reason:" << reason << " called from line:"
|
|
|
|
|
<< line << " when checking:" << nodep << std::endl);
|
2022-11-27 14:31:22 +01:00
|
|
|
// if (debug() >= 9) m_rootp->dumpTree("- root: ");
|
2021-08-18 20:15:02 +02:00
|
|
|
m_failed = true;
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
|
|
|
|
return m_failed;
|
|
|
|
|
}
|
|
|
|
|
void incrOps(const AstNode* nodep, int line) {
|
|
|
|
|
++m_ops;
|
|
|
|
|
UINFO(9, "Increment to " << m_ops << " " << nodep << " called from line " << line << "\n");
|
|
|
|
|
}
|
|
|
|
|
VarInfo& getVarInfo(const LeafInfo& ref) {
|
2022-06-27 15:41:33 +02:00
|
|
|
UASSERT_OBJ(ref.refp(), m_rootp, "null varref in And/Or/Xor optimization");
|
|
|
|
|
AstNode* nodep = ref.refp()->varScopep();
|
|
|
|
|
if (!nodep) nodep = ref.refp()->varp(); // Not scoped
|
2021-02-21 10:11:33 +01:00
|
|
|
int baseIdx = nodep->user4();
|
|
|
|
|
if (baseIdx == 0) { // Not set yet
|
|
|
|
|
baseIdx = m_varInfos.size();
|
|
|
|
|
const int numWords
|
2022-06-27 15:41:33 +02:00
|
|
|
= ref.refp()->dtypep()->isWide() ? ref.refp()->dtypep()->widthWords() : 1;
|
2021-06-21 00:28:39 +02:00
|
|
|
m_varInfos.resize(m_varInfos.size() + numWords);
|
2021-02-21 10:11:33 +01:00
|
|
|
nodep->user4(baseIdx);
|
|
|
|
|
}
|
2022-06-27 15:41:33 +02:00
|
|
|
const size_t idx = baseIdx + std::max(0, ref.wordIdx());
|
2021-06-21 00:28:39 +02:00
|
|
|
VarInfo* varInfop = m_varInfos[idx].get();
|
2021-02-21 10:11:33 +01:00
|
|
|
if (!varInfop) {
|
2022-06-27 15:41:33 +02:00
|
|
|
varInfop = new VarInfo{this, ref.refp(), ref.varWidth()};
|
2021-06-21 00:28:39 +02:00
|
|
|
m_varInfos[idx].reset(varInfop);
|
2022-12-01 14:00:24 +01:00
|
|
|
if (ref.missingWordSel()) {
|
|
|
|
|
// ConstBitOpTreeVisitor makes some constants for masks and its type is uint64_t.
|
|
|
|
|
// That's why V3Expand, that inserts WordSel, is needed.
|
|
|
|
|
CONST_BITOP_SET_FAILED("V3Expand is skipped", ref.refp());
|
|
|
|
|
}
|
2021-03-11 00:08:11 +01:00
|
|
|
} else {
|
2022-06-27 15:41:33 +02:00
|
|
|
if (!varInfop->sameVarAs(ref.refp()))
|
|
|
|
|
CONST_BITOP_SET_FAILED("different var (scope?)", ref.refp());
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
|
|
|
|
return *varInfop;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Traverse down to see AstConst or AstVarRef
|
|
|
|
|
LeafInfo findLeaf(AstNode* nodep, bool expectConst) {
|
2022-07-06 01:33:37 +02:00
|
|
|
LeafInfo info{m_lsb};
|
2021-02-21 10:11:33 +01:00
|
|
|
{
|
|
|
|
|
VL_RESTORER(m_leafp);
|
|
|
|
|
m_leafp = &info;
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(nodep);
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ok = !m_failed;
|
2021-07-25 19:38:27 +02:00
|
|
|
if (expectConst) {
|
2022-06-27 15:41:33 +02:00
|
|
|
ok &= !info.refp() && info.constp();
|
2021-07-25 19:38:27 +02:00
|
|
|
} else {
|
2022-06-27 15:41:33 +02:00
|
|
|
ok &= info.refp() && !info.constp();
|
2021-07-25 19:38:27 +02:00
|
|
|
}
|
2021-02-21 10:11:33 +01:00
|
|
|
return ok ? info : LeafInfo{};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override { CONST_BITOP_SET_FAILED("Hit unexpected op", nodep); }
|
|
|
|
|
void visit(AstCCast* nodep) override {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2022-07-06 01:33:37 +02:00
|
|
|
if (m_leafp) m_leafp->updateBitRange(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstShiftR* nodep) override {
|
2021-02-21 10:11:33 +01:00
|
|
|
CONST_BITOP_RETURN_IF(!m_leafp, nodep);
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const constp = VN_CAST(nodep->rhsp(), Const);
|
2021-02-21 10:11:33 +01:00
|
|
|
CONST_BITOP_RETURN_IF(!constp, nodep->rhsp());
|
|
|
|
|
m_lsb += constp->toUInt();
|
|
|
|
|
incrOps(nodep, __LINE__);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(nodep->lhsp());
|
2022-07-06 01:33:37 +02:00
|
|
|
m_leafp->updateBitRange(nodep);
|
2021-02-21 10:11:33 +01:00
|
|
|
m_lsb -= constp->toUInt();
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNot* nodep) override {
|
2021-02-21 10:11:33 +01:00
|
|
|
CONST_BITOP_RETURN_IF(nodep->widthMin() != 1, nodep);
|
|
|
|
|
AstNode* lhsp = nodep->lhsp();
|
2022-07-06 01:33:37 +02:00
|
|
|
AstCCast* const castp = VN_CAST(lhsp, CCast);
|
|
|
|
|
if (castp) lhsp = castp->lhsp();
|
2023-03-26 13:36:32 +02:00
|
|
|
CONST_BITOP_RETURN_IF(!isXorTree() && !VN_IS(lhsp, VarRef) && !VN_IS(lhsp, ShiftR), lhsp);
|
2021-02-21 10:11:33 +01:00
|
|
|
incrOps(nodep, __LINE__);
|
|
|
|
|
m_polarity = !m_polarity;
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2021-02-21 10:11:33 +01:00
|
|
|
// Don't restore m_polarity for Xor as it counts parity of the entire tree
|
|
|
|
|
if (!isXorTree()) m_polarity = !m_polarity;
|
2022-07-06 01:33:37 +02:00
|
|
|
if (m_leafp && castp) m_leafp->updateBitRange(castp);
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstWordSel* nodep) override {
|
2021-02-21 10:11:33 +01:00
|
|
|
CONST_BITOP_RETURN_IF(!m_leafp, nodep);
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const constp = VN_CAST(nodep->bitp(), Const);
|
2021-02-21 10:11:33 +01:00
|
|
|
CONST_BITOP_RETURN_IF(!constp, nodep->rhsp());
|
2022-06-27 15:41:33 +02:00
|
|
|
UASSERT_OBJ(m_leafp->wordIdx() == -1, nodep, "Unexpected nested WordSel");
|
|
|
|
|
m_leafp->wordIdx(constp->toSInt());
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(nodep->fromp());
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarRef* nodep) override {
|
2021-02-21 10:11:33 +01:00
|
|
|
CONST_BITOP_RETURN_IF(!m_leafp, nodep);
|
2022-06-27 15:41:33 +02:00
|
|
|
m_leafp->setLeaf(nodep);
|
|
|
|
|
m_leafp->polarity(m_polarity);
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstConst* nodep) override {
|
2021-02-21 10:11:33 +01:00
|
|
|
CONST_BITOP_RETURN_IF(!m_leafp, nodep);
|
2022-06-27 15:41:33 +02:00
|
|
|
m_leafp->setLeaf(nodep);
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstRedXor* nodep) override {
|
2021-06-21 00:28:39 +02:00
|
|
|
Restorer restorer{*this};
|
2021-02-21 10:11:33 +01:00
|
|
|
CONST_BITOP_RETURN_IF(!VN_IS(m_rootp, Xor), nodep);
|
2021-08-18 20:15:02 +02:00
|
|
|
AstNode* lhsp = nodep->lhsp();
|
2022-07-06 01:33:37 +02:00
|
|
|
const AstCCast* const castp = VN_CAST(lhsp, CCast);
|
|
|
|
|
if (castp) lhsp = castp->lhsp();
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstAnd* const andp = VN_CAST(lhsp, And)) { // '^(mask & leaf)'
|
2021-08-18 20:15:02 +02:00
|
|
|
CONST_BITOP_RETURN_IF(!andp, lhsp);
|
2021-02-21 10:11:33 +01:00
|
|
|
|
2021-08-18 20:15:02 +02:00
|
|
|
const LeafInfo& mask = findLeaf(andp->lhsp(), true);
|
2022-06-27 15:41:33 +02:00
|
|
|
CONST_BITOP_RETURN_IF(!mask.constp() || mask.lsb() != 0, andp->lhsp());
|
2021-02-21 10:11:33 +01:00
|
|
|
|
2022-07-06 01:33:37 +02:00
|
|
|
LeafInfo ref = findLeaf(andp->rhsp(), false);
|
2022-06-27 15:41:33 +02:00
|
|
|
CONST_BITOP_RETURN_IF(!ref.refp(), andp->rhsp());
|
2022-07-06 01:33:37 +02:00
|
|
|
if (castp) ref.updateBitRange(castp);
|
2021-02-21 10:11:33 +01:00
|
|
|
|
2021-08-18 20:15:02 +02:00
|
|
|
restorer.disableRestore(); // Now all subtree succeeded
|
2021-06-21 00:28:39 +02:00
|
|
|
|
2022-06-27 15:41:33 +02:00
|
|
|
const V3Number& maskNum = mask.constp()->num();
|
2021-08-18 20:15:02 +02:00
|
|
|
|
|
|
|
|
incrOps(nodep, __LINE__);
|
|
|
|
|
incrOps(andp, __LINE__);
|
|
|
|
|
|
|
|
|
|
// Mark all bits checked in this reduction
|
2022-07-06 01:33:37 +02:00
|
|
|
const int maxBitIdx = std::min(ref.lsb() + maskNum.width(), ref.msb() + 1);
|
2022-06-27 15:41:33 +02:00
|
|
|
for (int bitIdx = ref.lsb(); bitIdx < maxBitIdx; ++bitIdx) {
|
|
|
|
|
const int maskIdx = bitIdx - ref.lsb();
|
2021-08-18 20:15:02 +02:00
|
|
|
if (maskNum.bitIs0(maskIdx)) continue;
|
|
|
|
|
// Set true, m_polarity takes care of the entire parity
|
|
|
|
|
m_bitPolarities.emplace_back(ref, true, bitIdx);
|
|
|
|
|
}
|
|
|
|
|
} else { // '^leaf'
|
2022-07-06 01:33:37 +02:00
|
|
|
LeafInfo ref = findLeaf(lhsp, false);
|
2022-06-27 15:41:33 +02:00
|
|
|
CONST_BITOP_RETURN_IF(!ref.refp(), lhsp);
|
2022-07-06 01:33:37 +02:00
|
|
|
if (castp) ref.updateBitRange(castp);
|
2021-08-18 20:15:02 +02:00
|
|
|
|
|
|
|
|
restorer.disableRestore(); // Now all checks passed
|
|
|
|
|
|
|
|
|
|
incrOps(nodep, __LINE__);
|
|
|
|
|
|
|
|
|
|
// Mark all bits checked by this comparison
|
2022-07-06 01:33:37 +02:00
|
|
|
for (int bitIdx = ref.lsb(); bitIdx <= ref.msb(); ++bitIdx) {
|
2021-08-18 20:15:02 +02:00
|
|
|
m_bitPolarities.emplace_back(ref, true, bitIdx);
|
|
|
|
|
}
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeBiop* nodep) override {
|
2021-08-18 20:15:02 +02:00
|
|
|
if (VN_IS(nodep, And) && isConst(nodep->lhsp(), 1)) { // 1 & _
|
|
|
|
|
// Always reach past a plain making AND
|
|
|
|
|
Restorer restorer{*this};
|
|
|
|
|
incrOps(nodep, __LINE__);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(nodep->rhsp());
|
2021-08-18 20:15:02 +02:00
|
|
|
CONST_BITOP_RETURN_IF(m_failed, nodep->rhsp());
|
|
|
|
|
restorer.disableRestore(); // Now all checks passed
|
|
|
|
|
} else if (nodep->type() == m_rootp->type()) { // And, Or, Xor
|
2021-02-21 10:11:33 +01:00
|
|
|
incrOps(nodep, __LINE__);
|
|
|
|
|
VL_RESTORER(m_leafp);
|
|
|
|
|
|
2021-08-18 20:15:02 +02:00
|
|
|
for (const bool right : {false, true}) {
|
2021-06-21 00:28:39 +02:00
|
|
|
Restorer restorer{*this};
|
2022-07-06 01:33:37 +02:00
|
|
|
LeafInfo leafInfo{m_lsb};
|
2021-02-21 10:11:33 +01:00
|
|
|
m_leafp = &leafInfo;
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* opp = right ? nodep->rhsp() : nodep->lhsp();
|
2021-02-21 10:11:33 +01:00
|
|
|
const bool origFailed = m_failed;
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(opp);
|
2022-06-27 15:41:33 +02:00
|
|
|
if (leafInfo.constp() || m_failed) {
|
2021-08-18 20:15:02 +02:00
|
|
|
// Revert changes in leaf
|
2021-06-21 00:28:39 +02:00
|
|
|
restorer.restoreNow();
|
2021-08-18 20:15:02 +02:00
|
|
|
// Reach past a cast then add to frozen nodes to be added to final reduction
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstCCast* const castp = VN_CAST(opp, CCast)) opp = castp->lhsp();
|
2023-03-26 09:29:10 +02:00
|
|
|
const bool pol = isXorTree() || m_polarity; // Only AND/OR tree needs polarity
|
|
|
|
|
m_frozenNodes.emplace_back(opp, FrozenNodeInfo{pol, m_lsb});
|
2021-02-21 10:11:33 +01:00
|
|
|
m_failed = origFailed;
|
2021-06-21 00:28:39 +02:00
|
|
|
continue;
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
2021-06-21 00:28:39 +02:00
|
|
|
restorer.disableRestore(); // Now all checks passed
|
2022-06-27 15:41:33 +02:00
|
|
|
if (leafInfo.refp()) {
|
2021-08-18 20:15:02 +02:00
|
|
|
// The conditional on the lsb being in range is necessary for some degenerate
|
|
|
|
|
// case, e.g.: (IData)((QData)wide[0] >> 32), or <1-bit-var> >> 1, which is
|
|
|
|
|
// just zero
|
2022-07-06 01:33:37 +02:00
|
|
|
if (leafInfo.lsb() <= leafInfo.msb()) {
|
2022-06-27 15:41:33 +02:00
|
|
|
m_bitPolarities.emplace_back(leafInfo, isXorTree() || leafInfo.polarity(),
|
|
|
|
|
leafInfo.lsb());
|
2023-01-04 15:10:55 +01:00
|
|
|
} else if ((isAndTree() && leafInfo.polarity())
|
|
|
|
|
|| (isOrTree() && !leafInfo.polarity())) {
|
|
|
|
|
// If there is a constant 0 term in an And tree or 1 term in an Or tree, we
|
|
|
|
|
// must include it. Fudge this by adding a bit with both polarities, which
|
|
|
|
|
// will simplify to zero or one respectively.
|
|
|
|
|
// Note that Xor tree does not need this kind of care, polarity of Xor tree
|
|
|
|
|
// is already cared when visitin AstNot. Taking xor with 1'b0 is nop.
|
2021-08-18 20:15:02 +02:00
|
|
|
m_bitPolarities.emplace_back(leafInfo, true, 0);
|
|
|
|
|
m_bitPolarities.emplace_back(leafInfo, false, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
|
|
|
|
} else if ((isAndTree() && VN_IS(nodep, Eq)) || (isOrTree() && VN_IS(nodep, Neq))) {
|
2021-06-21 00:28:39 +02:00
|
|
|
Restorer restorer{*this};
|
2021-02-21 10:11:33 +01:00
|
|
|
CONST_BITOP_RETURN_IF(!m_polarity, nodep);
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNode* lhsp = nodep->lhsp();
|
|
|
|
|
if (const AstCCast* const castp = VN_CAST(lhsp, CCast)) lhsp = castp->lhsp();
|
|
|
|
|
const AstConst* const constp = VN_CAST(lhsp, Const);
|
2021-08-18 20:15:02 +02:00
|
|
|
CONST_BITOP_RETURN_IF(!constp, nodep->lhsp());
|
|
|
|
|
|
|
|
|
|
const V3Number& compNum = constp->num();
|
2021-02-21 10:11:33 +01:00
|
|
|
|
2022-07-24 12:54:37 +02:00
|
|
|
auto setPolarities = [this, &compNum](const LeafInfo& ref, const V3Number* maskp) {
|
|
|
|
|
const bool maskFlip = isOrTree();
|
|
|
|
|
int constantWidth = compNum.width();
|
|
|
|
|
if (maskp) constantWidth = std::max(constantWidth, maskp->width());
|
|
|
|
|
const int maxBitIdx = std::max(ref.lsb() + constantWidth, ref.msb() + 1);
|
|
|
|
|
// Mark all bits checked by this comparison
|
|
|
|
|
for (int bitIdx = ref.lsb(); bitIdx < maxBitIdx; ++bitIdx) {
|
|
|
|
|
const int maskIdx = bitIdx - ref.lsb();
|
|
|
|
|
const bool mask0 = maskp && maskp->bitIs0(maskIdx);
|
|
|
|
|
const bool outOfRange = bitIdx > ref.msb();
|
|
|
|
|
if (mask0 || outOfRange) { // RHS is 0
|
|
|
|
|
if (compNum.bitIs1(maskIdx)) {
|
|
|
|
|
// LHS is 1
|
|
|
|
|
// And tree: 1 == 0 => always false, set v && !v
|
|
|
|
|
// Or tree : 1 != 0 => always true, set v || !v
|
|
|
|
|
m_bitPolarities.emplace_back(ref, true, 0);
|
|
|
|
|
m_bitPolarities.emplace_back(ref, false, 0);
|
|
|
|
|
break;
|
|
|
|
|
} else { // This bitIdx is irrelevant
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const bool polarity = compNum.bitIs1(maskIdx) != maskFlip;
|
|
|
|
|
m_bitPolarities.emplace_back(ref, polarity, bitIdx);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstAnd* const andp = VN_CAST(nodep->rhsp(), And)) { // comp == (mask & v)
|
2021-08-18 20:15:02 +02:00
|
|
|
const LeafInfo& mask = findLeaf(andp->lhsp(), true);
|
2022-06-27 15:41:33 +02:00
|
|
|
CONST_BITOP_RETURN_IF(!mask.constp() || mask.lsb() != 0, andp->lhsp());
|
2021-02-21 10:11:33 +01:00
|
|
|
|
2021-08-18 20:15:02 +02:00
|
|
|
const LeafInfo& ref = findLeaf(andp->rhsp(), false);
|
2022-06-27 15:41:33 +02:00
|
|
|
CONST_BITOP_RETURN_IF(!ref.refp(), andp->rhsp());
|
2021-02-21 10:11:33 +01:00
|
|
|
|
2021-08-18 20:15:02 +02:00
|
|
|
restorer.disableRestore(); // Now all checks passed
|
2021-02-21 10:11:33 +01:00
|
|
|
|
2022-06-27 15:41:33 +02:00
|
|
|
const V3Number& maskNum = mask.constp()->num();
|
2021-08-18 20:15:02 +02:00
|
|
|
|
|
|
|
|
incrOps(nodep, __LINE__);
|
|
|
|
|
incrOps(andp, __LINE__);
|
|
|
|
|
|
2022-07-24 12:54:37 +02:00
|
|
|
setPolarities(ref, &maskNum);
|
2021-08-18 20:15:02 +02:00
|
|
|
} else { // comp == v
|
|
|
|
|
const LeafInfo& ref = findLeaf(nodep->rhsp(), false);
|
2022-06-27 15:41:33 +02:00
|
|
|
CONST_BITOP_RETURN_IF(!ref.refp(), nodep->rhsp());
|
2021-08-18 20:15:02 +02:00
|
|
|
|
|
|
|
|
restorer.disableRestore(); // Now all checks passed
|
2021-02-21 10:11:33 +01:00
|
|
|
|
2021-08-18 20:15:02 +02:00
|
|
|
incrOps(nodep, __LINE__);
|
|
|
|
|
|
2022-07-24 12:54:37 +02:00
|
|
|
setPolarities(ref, nullptr);
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
2021-08-18 20:15:02 +02:00
|
|
|
} else {
|
|
|
|
|
CONST_BITOP_SET_FAILED("Mixture of different ops cannot be optimized", nodep);
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CONSTRUCTORS
|
2022-11-13 21:33:11 +01:00
|
|
|
ConstBitOpTreeVisitor(AstNodeExpr* nodep, unsigned externalOps)
|
2021-08-18 20:15:02 +02:00
|
|
|
: m_ops{externalOps}
|
2021-07-12 00:42:01 +02:00
|
|
|
, m_rootp{nodep} {
|
2021-02-21 10:11:33 +01:00
|
|
|
// Fill nullptr at [0] because AstVarScope::user4 is 0 by default
|
|
|
|
|
m_varInfos.push_back(nullptr);
|
|
|
|
|
CONST_BITOP_RETURN_IF(!isAndTree() && !isOrTree() && !isXorTree(), nodep);
|
2021-08-18 20:15:02 +02:00
|
|
|
if (AstNodeBiop* const biopp = VN_CAST(nodep, NodeBiop)) {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(biopp);
|
2021-02-21 10:11:33 +01:00
|
|
|
} else {
|
2021-08-18 20:15:02 +02:00
|
|
|
UASSERT_OBJ(VN_IS(nodep, RedXor), nodep, "Must be RedXor");
|
2021-02-21 10:11:33 +01:00
|
|
|
incrOps(nodep, __LINE__);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
2021-06-21 00:28:39 +02:00
|
|
|
for (auto&& entry : m_bitPolarities) {
|
2021-08-18 20:15:02 +02:00
|
|
|
getVarInfo(entry.m_info).setPolarity(entry.m_polarity, entry.m_bit);
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
2021-06-21 00:28:39 +02:00
|
|
|
UASSERT_OBJ(isXorTree() || m_polarity, nodep, "must be the original polarity");
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
2021-06-21 00:28:39 +02:00
|
|
|
virtual ~ConstBitOpTreeVisitor() = default;
|
2021-02-21 10:11:33 +01:00
|
|
|
#undef CONST_BITOP_RETURN_IF
|
|
|
|
|
#undef CONST_BITOP_SET_FAILED
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// Transform as below.
|
|
|
|
|
// v[0] & v[1] => 2'b11 == (2'b11 & v)
|
|
|
|
|
// v[0] | v[1] => 2'b00 != (2'b11 & v)
|
|
|
|
|
// v[0] ^ v[1] => ^{2'b11 & v}
|
|
|
|
|
// (3'b011 == (3'b011 & v)) & v[2] => 3'b111 == (3'b111 & v)
|
|
|
|
|
// (3'b000 != (3'b011 & v)) | v[2] => 3'b000 != (3'b111 & v)
|
|
|
|
|
// Reduction ops are transformed in the same way.
|
|
|
|
|
// &{v[0], v[1]} => 2'b11 == (2'b11 & v)
|
2022-11-13 21:33:11 +01:00
|
|
|
static AstNodeExpr* simplify(AstNodeExpr* nodep, int resultWidth, unsigned externalOps,
|
|
|
|
|
VDouble0& reduction) {
|
2021-08-18 20:15:02 +02:00
|
|
|
UASSERT_OBJ(1 <= resultWidth && resultWidth <= 64, nodep, "resultWidth out of range");
|
|
|
|
|
|
|
|
|
|
// Walk tree, gathering all terms referenced in expression
|
2021-11-26 23:55:36 +01:00
|
|
|
const ConstBitOpTreeVisitor visitor{nodep, externalOps};
|
2021-08-18 20:15:02 +02:00
|
|
|
|
|
|
|
|
// If failed on root node is not optimizable, or there are no variable terms, then done
|
2021-02-21 10:11:33 +01:00
|
|
|
if (visitor.m_failed || visitor.m_varInfos.size() == 1) return nullptr;
|
|
|
|
|
|
2021-08-18 20:15:02 +02:00
|
|
|
// FileLine used for constructing all new nodes in this function
|
|
|
|
|
FileLine* const fl = nodep->fileline();
|
|
|
|
|
|
|
|
|
|
// Get partial result each term referenced, count total number of ops and keep track of
|
|
|
|
|
// whether we have clean/dirty terms. visitor.m_varInfos appears in deterministic order,
|
|
|
|
|
// so the optimized tree is deterministic as well.
|
|
|
|
|
|
2022-11-13 21:33:11 +01:00
|
|
|
std::vector<AstNodeExpr*> termps;
|
2021-08-18 20:15:02 +02:00
|
|
|
termps.reserve(visitor.m_varInfos.size() - 1);
|
|
|
|
|
unsigned resultOps = 0;
|
|
|
|
|
bool hasCleanTerm = false;
|
|
|
|
|
bool hasDirtyTerm = false;
|
|
|
|
|
|
2021-06-21 00:28:39 +02:00
|
|
|
for (auto&& v : visitor.m_varInfos) {
|
2021-08-18 20:15:02 +02:00
|
|
|
if (!v) continue; // Skip nullptr at m_varInfos[0]
|
|
|
|
|
if (v->hasConstResult()) {
|
|
|
|
|
// If a constant term is known, we can either drop it or the whole tree is constant
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* resultp = nullptr;
|
2021-08-18 20:15:02 +02:00
|
|
|
if (v->getConstResult()) {
|
|
|
|
|
UASSERT_OBJ(visitor.isOrTree(), nodep,
|
|
|
|
|
"Only OR tree can yield known 1 result");
|
|
|
|
|
UINFO(9, "OR tree with const 1 term: " << v->refp() << endl);
|
|
|
|
|
// Known 1 bit in OR tree, whole result is 1
|
|
|
|
|
resultp = new AstConst{fl, AstConst::BitTrue{}};
|
|
|
|
|
} else if (visitor.isAndTree()) {
|
|
|
|
|
UINFO(9, "AND tree with const 0 term: " << v->refp() << endl);
|
|
|
|
|
// Known 0 bit in AND tree, whole result is 0
|
|
|
|
|
resultp = new AstConst{fl, AstConst::BitFalse{}};
|
|
|
|
|
} else {
|
|
|
|
|
// Known 0 bit in OR or XOR tree. Ignore it.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// Set width and widthMin precisely
|
|
|
|
|
resultp->dtypeChgWidth(resultWidth, 1);
|
|
|
|
|
for (AstNode* const termp : termps) termp->deleteTree();
|
|
|
|
|
return resultp;
|
|
|
|
|
}
|
|
|
|
|
const ResultTerm result = v->getResultTerm();
|
|
|
|
|
termps.push_back(std::get<0>(result));
|
|
|
|
|
resultOps += std::get<1>(result);
|
|
|
|
|
if (std::get<2>(result)) {
|
|
|
|
|
hasCleanTerm = true;
|
|
|
|
|
UINFO(9, "Clean term: " << termps.back() << endl);
|
|
|
|
|
} else {
|
|
|
|
|
hasDirtyTerm = true;
|
|
|
|
|
UINFO(9, "Dirty term: " << termps.back() << endl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-13 21:33:11 +01:00
|
|
|
// Group by FrozenNodeInfo
|
|
|
|
|
std::map<FrozenNodeInfo, std::vector<AstNodeExpr*>> frozenNodes;
|
2021-08-18 20:15:02 +02:00
|
|
|
// Check if frozen terms are clean or not
|
2022-06-01 02:26:16 +02:00
|
|
|
for (const auto& frozenInfo : visitor.m_frozenNodes) {
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const termp = frozenInfo.first;
|
2021-08-18 20:15:02 +02:00
|
|
|
// Comparison operators are clean
|
2022-05-30 12:33:06 +02:00
|
|
|
if ((VN_IS(termp, Eq) || VN_IS(termp, Neq) || VN_IS(termp, Lt) || VN_IS(termp, Lte)
|
|
|
|
|
|| VN_IS(termp, Gt) || VN_IS(termp, Gte))
|
2022-06-01 02:26:16 +02:00
|
|
|
&& frozenInfo.second.m_lsb == 0) {
|
2021-08-18 20:15:02 +02:00
|
|
|
hasCleanTerm = true;
|
|
|
|
|
} else {
|
|
|
|
|
// Otherwise, conservatively assume the frozen term is dirty
|
|
|
|
|
hasDirtyTerm = true;
|
|
|
|
|
UINFO(9, "Dirty frozen term: " << termp << endl);
|
|
|
|
|
}
|
2022-06-01 02:26:16 +02:00
|
|
|
frozenNodes[frozenInfo.second].push_back(termp);
|
2021-08-18 20:15:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Figure out if a final negation is required
|
|
|
|
|
const bool needsFlip = visitor.isXorTree() && !visitor.m_polarity;
|
|
|
|
|
|
|
|
|
|
// Figure out if the final tree needs cleaning
|
|
|
|
|
const bool needsCleaning = visitor.isAndTree() ? !hasCleanTerm : hasDirtyTerm;
|
|
|
|
|
|
|
|
|
|
// Add size of reduction tree to op count
|
2022-05-30 12:33:06 +02:00
|
|
|
resultOps += termps.size() - 1;
|
|
|
|
|
for (const auto& lsbAndNodes : frozenNodes) {
|
2022-06-01 02:26:16 +02:00
|
|
|
if (lsbAndNodes.first.m_lsb > 0) ++resultOps; // Needs AstShiftR
|
|
|
|
|
if (!lsbAndNodes.first.m_polarity) ++resultOps; // Needs AstNot
|
2022-05-30 12:33:06 +02:00
|
|
|
resultOps += lsbAndNodes.second.size();
|
|
|
|
|
}
|
2021-08-18 20:15:02 +02:00
|
|
|
// Add final polarity flip in Xor tree
|
|
|
|
|
if (needsFlip) ++resultOps;
|
|
|
|
|
// Add final cleaning AND
|
|
|
|
|
if (needsCleaning) ++resultOps;
|
|
|
|
|
|
|
|
|
|
if (debug() >= 9) { // LCOV_EXCL_START
|
2022-11-27 14:31:22 +01:00
|
|
|
cout << "- Bitop tree considered: " << endl;
|
|
|
|
|
for (AstNodeExpr* const termp : termps) termp->dumpTree("- Reduced term: ");
|
2022-11-13 21:33:11 +01:00
|
|
|
for (const std::pair<AstNodeExpr*, FrozenNodeInfo>& termp : visitor.m_frozenNodes) {
|
2022-11-27 14:31:22 +01:00
|
|
|
termp.first->dumpTree("- Frozen term with lsb "
|
|
|
|
|
+ std::to_string(termp.second.m_lsb) + " polarity "
|
|
|
|
|
+ std::to_string(termp.second.m_polarity) + ": ");
|
2022-07-30 16:01:25 +02:00
|
|
|
}
|
2022-11-27 14:31:22 +01:00
|
|
|
cout << "- Needs flipping: " << needsFlip << endl;
|
|
|
|
|
cout << "- Needs cleaning: " << needsCleaning << endl;
|
|
|
|
|
cout << "- Size: " << resultOps << " input size: " << visitor.m_ops << endl;
|
2021-08-18 20:15:02 +02:00
|
|
|
} // LCOV_EXCL_END
|
|
|
|
|
|
|
|
|
|
// Sometimes we have no terms left after ignoring redundant terms
|
|
|
|
|
// (all of which were zeroes)
|
|
|
|
|
if (termps.empty() && visitor.m_frozenNodes.empty()) {
|
|
|
|
|
reduction += visitor.m_ops;
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const resultp = needsFlip ? new AstConst{fl, AstConst::BitTrue{}}
|
|
|
|
|
: new AstConst{fl, AstConst::BitFalse{}};
|
2021-08-18 20:15:02 +02:00
|
|
|
resultp->dtypeChgWidth(resultWidth, 1);
|
|
|
|
|
return resultp;
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
2021-08-18 20:15:02 +02:00
|
|
|
|
|
|
|
|
// Only substitute the result if beneficial as determined by operation count
|
|
|
|
|
if (visitor.m_ops <= resultOps) {
|
|
|
|
|
for (AstNode* const termp : termps) termp->deleteTree();
|
|
|
|
|
return nullptr;
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
|
|
|
|
|
2021-08-18 20:15:02 +02:00
|
|
|
// Update statistics
|
|
|
|
|
reduction += visitor.m_ops - resultOps;
|
2021-02-21 10:11:33 +01:00
|
|
|
|
2021-08-18 20:15:02 +02:00
|
|
|
// Reduction op to combine terms
|
2022-11-13 21:33:11 +01:00
|
|
|
const auto reduce = [&visitor, fl](AstNodeExpr* lhsp, AstNodeExpr* rhsp) -> AstNodeExpr* {
|
2021-08-18 20:15:02 +02:00
|
|
|
if (!lhsp) return rhsp;
|
|
|
|
|
if (visitor.isAndTree()) {
|
|
|
|
|
return new AstAnd{fl, lhsp, rhsp};
|
|
|
|
|
} else if (visitor.isOrTree()) {
|
|
|
|
|
return new AstOr{fl, lhsp, rhsp};
|
|
|
|
|
} else {
|
|
|
|
|
return new AstXor{fl, lhsp, rhsp};
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Compute result by reducing all terms
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* resultp = nullptr;
|
|
|
|
|
for (AstNodeExpr* const termp : termps) { //
|
2021-08-18 20:15:02 +02:00
|
|
|
resultp = reduce(resultp, termp);
|
|
|
|
|
}
|
|
|
|
|
// Add any frozen terms to the reduction
|
2022-06-01 02:26:16 +02:00
|
|
|
for (auto&& nodes : frozenNodes) {
|
|
|
|
|
// nodes.second has same lsb and polarity
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* termp = nullptr;
|
|
|
|
|
for (AstNodeExpr* const itemp : nodes.second) {
|
2022-05-30 12:33:06 +02:00
|
|
|
termp = reduce(termp, itemp->unlinkFrBack());
|
|
|
|
|
}
|
2022-06-01 02:26:16 +02:00
|
|
|
if (nodes.first.m_lsb > 0) { // LSB is not 0, so shiftR
|
2022-05-30 12:33:06 +02:00
|
|
|
AstNodeDType* const dtypep = termp->dtypep();
|
|
|
|
|
termp = new AstShiftR{termp->fileline(), termp,
|
|
|
|
|
new AstConst(termp->fileline(), AstConst::WidthedValue{},
|
2022-06-01 02:26:16 +02:00
|
|
|
termp->width(), nodes.first.m_lsb)};
|
|
|
|
|
termp->dtypep(dtypep);
|
|
|
|
|
}
|
|
|
|
|
if (!nodes.first.m_polarity) { // Polarity is inverted, so append Not
|
|
|
|
|
AstNodeDType* const dtypep = termp->dtypep();
|
|
|
|
|
termp = new AstNot{termp->fileline(), termp};
|
2022-05-30 12:33:06 +02:00
|
|
|
termp->dtypep(dtypep);
|
|
|
|
|
}
|
|
|
|
|
resultp = reduce(resultp, termp);
|
2021-08-18 20:15:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set width of masks to expected result width. This is required to prevent later removal
|
|
|
|
|
// of the masking node e.g. by the "AND with all ones" rule. If the result width happens
|
2022-12-03 00:46:38 +01:00
|
|
|
// to be 1, we still need to ensure the AstAnd is not dropped, so use a wider mask in this
|
2021-08-18 20:15:02 +02:00
|
|
|
// special case.
|
|
|
|
|
const int maskWidth = resultWidth == 1 ? VL_IDATASIZE : resultWidth;
|
|
|
|
|
|
|
|
|
|
// Apply final polarity flip
|
|
|
|
|
if (needsFlip) {
|
|
|
|
|
if (needsCleaning) {
|
|
|
|
|
// Cleaning will be added below. Use a NOT which is a byte shorter on x86
|
|
|
|
|
resultp = new AstNot{fl, resultp};
|
|
|
|
|
} else {
|
|
|
|
|
// Keep result clean by using XOR(1, _)
|
|
|
|
|
AstConst* const maskp = new AstConst{fl, AstConst::WidthedValue{}, maskWidth, 1};
|
|
|
|
|
resultp = new AstXor{fl, maskp, resultp};
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
|
|
|
|
}
|
2021-08-18 20:15:02 +02:00
|
|
|
|
|
|
|
|
// Apply final cleaning
|
|
|
|
|
if (needsCleaning) {
|
|
|
|
|
AstConst* const maskp = new AstConst{fl, AstConst::WidthedValue{}, maskWidth, 1};
|
|
|
|
|
resultp = new AstAnd{fl, maskp, resultp};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cast back to original size if required
|
|
|
|
|
if (resultp->width() != resultWidth) {
|
|
|
|
|
resultp = new AstCCast{fl, resultp, resultWidth, 1};
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
2021-08-18 20:15:02 +02:00
|
|
|
|
|
|
|
|
// Set width and widthMin precisely
|
|
|
|
|
resultp->dtypeChgWidth(resultWidth, 1);
|
|
|
|
|
|
2021-02-21 10:11:33 +01:00
|
|
|
return resultp;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Const state, as a visitor of each AstNode
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class ConstVisitor final : public VNVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
private:
|
2022-05-05 13:02:52 +02:00
|
|
|
// CONSTANTS
|
|
|
|
|
static constexpr unsigned CONCAT_MERGABLE_MAX_DEPTH = 10; // Limit alg recursion
|
|
|
|
|
|
2008-02-20 17:54:41 +01:00
|
|
|
// NODE STATE
|
2010-12-29 02:46:13 +01:00
|
|
|
// ** only when m_warn/m_doExpensive is set. If state is needed other times,
|
2008-02-20 17:54:41 +01:00
|
|
|
// ** must track down everywhere V3Const is called and make sure no overlaps.
|
2022-01-09 23:34:10 +01:00
|
|
|
// AstVar::user4p -> Used by variable marking/finding
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstJumpLabel::user4 -> bool. Set when AstJumpGo uses this label
|
2019-11-05 00:48:47 +01:00
|
|
|
// AstEnum::user4 -> bool. Recursing.
|
2008-02-20 17:54:41 +01:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// STATE
|
2021-11-26 23:55:36 +01:00
|
|
|
static constexpr bool m_doShort = true; // Remove expressions that short circuit
|
2020-08-16 15:55:36 +02:00
|
|
|
bool m_params = false; // If true, propagate parameterized and true numbers only
|
|
|
|
|
bool m_required = false; // If true, must become a constant
|
|
|
|
|
bool m_wremove = true; // Inside scope, no assignw removal
|
|
|
|
|
bool m_warn = false; // Output warnings
|
|
|
|
|
bool m_doExpensive = false; // Enable computationally expensive optimizations
|
2020-12-19 17:12:46 +01:00
|
|
|
bool m_doCpp = false; // Enable late-stage C++ optimizations
|
2020-08-16 15:55:36 +02:00
|
|
|
bool m_doNConst = false; // Enable non-constant-child simplifications
|
|
|
|
|
bool m_doV = false; // Verilog, not C++ conversion
|
|
|
|
|
bool m_doGenerate = false; // Postpone width checking inside generate
|
|
|
|
|
bool m_hasJumpDelay = false; // JumpGo or Delay under this while
|
2023-03-02 03:07:37 +01:00
|
|
|
bool m_underRecFunc = false; // Under a recursive function
|
2020-08-16 15:55:36 +02:00
|
|
|
AstNodeModule* m_modp = nullptr; // Current module
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstArraySel* m_selp = nullptr; // Current select
|
|
|
|
|
const AstNode* m_scopep = nullptr; // Current scope
|
|
|
|
|
const AstAttrOf* m_attrp = nullptr; // Current attribute
|
2021-02-21 10:11:33 +01:00
|
|
|
VDouble0 m_statBitOpReduction; // Ops reduced in ConstBitOpTreeVisitor
|
2021-08-11 15:30:00 +02:00
|
|
|
const bool m_globalPass; // ConstVisitor invoked as a global pass
|
|
|
|
|
static uint32_t s_globalPassNum; // Counts number of times ConstVisitor invoked as global pass
|
|
|
|
|
V3UniqueNames m_concswapNames; // For generating unique temporary variable names
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2009-01-21 22:56:50 +01:00
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
bool operandConst(AstNode* nodep) { return VN_IS(nodep, Const); }
|
2018-02-02 03:32:58 +01:00
|
|
|
bool operandAsvConst(const AstNode* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// BIASV(CONST, BIASV(CONST,...)) -> BIASV( BIASV_CONSTED(a,b), ...)
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeBiComAsv* const bnodep = VN_CAST(nodep, NodeBiComAsv);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!bnodep) return false;
|
2018-02-02 03:32:58 +01:00
|
|
|
if (!VN_IS(bnodep->lhsp(), Const)) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeBiComAsv* const rnodep = VN_CAST(bnodep->rhsp(), NodeBiComAsv);
|
2019-05-19 22:13:13 +02:00
|
|
|
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;
|
2018-02-02 03:32:58 +01:00
|
|
|
if (!VN_IS(rnodep->lhsp(), Const)) return false;
|
2019-05-19 22:13:13 +02:00
|
|
|
return true;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
bool operandAsvSame(const AstNode* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// BIASV(SAMEa, BIASV(SAMEb,...)) -> BIASV( BIASV(SAMEa,SAMEb), ...)
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeBiComAsv* const bnodep = VN_CAST(nodep, NodeBiComAsv);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!bnodep) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeBiComAsv* const rnodep = VN_CAST(bnodep->rhsp(), NodeBiComAsv);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!rnodep) return false;
|
|
|
|
|
if (rnodep->type() != bnodep->type()) return false;
|
|
|
|
|
if (rnodep->width() != bnodep->width()) return false;
|
|
|
|
|
return operandsSame(bnodep->lhsp(), rnodep->lhsp());
|
2006-09-28 16:37:28 +02:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
bool operandAsvLUp(const AstNode* nodep) {
|
|
|
|
|
// BIASV(BIASV(CONSTll,lr),r) -> BIASV(CONSTll,BIASV(lr,r)) ?
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
|
|
|
|
// Example of how this is useful:
|
|
|
|
|
// BIASV(BIASV(CONSTa,b...),BIASV(CONSTc,d...)) // hits operandAsvUp
|
|
|
|
|
// BIASV(CONSTa,BIASV(b...,BIASV(CONSTc,d...))) // hits operandAsvUp
|
|
|
|
|
// BIASV(CONSTa,BIASV(CONSTc,BIASV(c...,d...))) // hits operandAsvConst
|
|
|
|
|
// BIASV(BIASV(CONSTa,CONSTc),BIASV(c...,d...))) // hits normal constant propagation
|
|
|
|
|
// BIASV(CONST_a_c,BIASV(c...,d...)))
|
|
|
|
|
//
|
|
|
|
|
// Idea for the future: All BiComAsvs could be lists, sorted by if they're constant
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeBiComAsv* const bnodep = VN_CAST(nodep, NodeBiComAsv);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!bnodep) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeBiComAsv* const lnodep = VN_CAST(bnodep->lhsp(), NodeBiComAsv);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!lnodep) return false;
|
|
|
|
|
if (lnodep->type() != bnodep->type()) return false;
|
|
|
|
|
if (lnodep->width() != bnodep->width()) return false;
|
2018-02-02 03:32:58 +01:00
|
|
|
return VN_IS(lnodep->lhsp(), Const);
|
2008-11-17 16:40:58 +01:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
bool operandAsvRUp(const AstNode* nodep) {
|
|
|
|
|
// BIASV(l,BIASV(CONSTrl,rr)) -> BIASV(CONSTrl,BIASV(l,rr)) ?
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeBiComAsv* const bnodep = VN_CAST(nodep, NodeBiComAsv);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!bnodep) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeBiComAsv* const rnodep = VN_CAST(bnodep->rhsp(), NodeBiComAsv);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!rnodep) return false;
|
|
|
|
|
if (rnodep->type() != bnodep->type()) return false;
|
|
|
|
|
if (rnodep->width() != bnodep->width()) return false;
|
2018-02-02 03:32:58 +01:00
|
|
|
return VN_IS(rnodep->lhsp(), Const);
|
2008-11-17 16:40:58 +01:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
static bool operandSubAdd(const AstNode* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// SUB( ADD(CONSTx,y), CONSTz) -> ADD(SUB(CONSTx,CONSTz), y)
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeBiop* const np = VN_CAST(nodep, NodeBiop);
|
|
|
|
|
const AstNodeBiop* const lp = VN_CAST(np->lhsp(), NodeBiop);
|
2020-04-15 13:58:34 +02:00
|
|
|
return (lp && VN_IS(lp->lhsp(), Const) && VN_IS(np->rhsp(), Const)
|
|
|
|
|
&& lp->width() == np->width());
|
2014-11-10 00:33:54 +01:00
|
|
|
}
|
2021-02-18 11:28:49 +01:00
|
|
|
bool matchRedundantClean(AstAnd* andp) {
|
|
|
|
|
// Remove And with constant one inserted by V3Clean
|
|
|
|
|
// 1 & (a == b) -> (IData)(a == b)
|
|
|
|
|
// When bool is casted to int, the value is either 0 or 1
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const constp = VN_AS(andp->lhsp(), Const);
|
2021-02-18 11:28:49 +01:00
|
|
|
UASSERT_OBJ(constp && constp->isOne(), andp->lhsp(), "TRREEOPC must meet this condition");
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const rhsp = andp->rhsp();
|
2021-02-18 11:28:49 +01:00
|
|
|
AstCCast* ccastp = nullptr;
|
2021-11-26 23:55:36 +01:00
|
|
|
const auto isEqOrNeq
|
2021-02-18 11:28:49 +01:00
|
|
|
= [](AstNode* nodep) -> bool { return VN_IS(nodep, Eq) || VN_IS(nodep, Neq); };
|
|
|
|
|
if (isEqOrNeq(rhsp)) {
|
|
|
|
|
ccastp = new AstCCast{andp->fileline(), rhsp->unlinkFrBack(), andp};
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (AstCCast* const tmpp = VN_CAST(rhsp, CCast)) {
|
2021-02-18 11:28:49 +01:00
|
|
|
if (isEqOrNeq(tmpp->lhsp())) {
|
|
|
|
|
if (tmpp->width() == andp->width()) {
|
|
|
|
|
tmpp->unlinkFrBack();
|
|
|
|
|
ccastp = tmpp;
|
|
|
|
|
} else {
|
|
|
|
|
ccastp = new AstCCast{andp->fileline(), tmpp->lhsp()->unlinkFrBack(), andp};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (ccastp) {
|
|
|
|
|
andp->replaceWith(ccastp);
|
|
|
|
|
VL_DO_DANGLING(andp->deleteTree(), andp);
|
2021-10-06 01:42:31 +02:00
|
|
|
return true;
|
2021-02-18 11:28:49 +01:00
|
|
|
}
|
2021-10-06 01:42:31 +02:00
|
|
|
return false;
|
2021-02-18 11:28:49 +01:00
|
|
|
}
|
2014-11-10 00:33:54 +01:00
|
|
|
|
2018-02-02 03:32:58 +01:00
|
|
|
static bool operandAndOrSame(const AstNode* nodep) {
|
2019-05-19 22:13:13 +02: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)
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeBiop* const np = VN_CAST(nodep, NodeBiop);
|
|
|
|
|
const AstNodeBiop* const lp = VN_CAST(np->lhsp(), NodeBiop);
|
|
|
|
|
const AstNodeBiop* const rp = VN_CAST(np->rhsp(), NodeBiop);
|
2020-04-15 13:58:34 +02:00
|
|
|
return (lp && rp && lp->width() == rp->width() && lp->type() == rp->type()
|
|
|
|
|
&& (operandsSame(lp->lhsp(), rp->lhsp()) || operandsSame(lp->rhsp(), rp->rhsp())));
|
2008-11-17 17:36:01 +01:00
|
|
|
}
|
2009-01-07 15:37:59 +01:00
|
|
|
static bool matchOrAndNot(AstNodeBiop* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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))
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* ap;
|
2019-05-19 22:13:13 +02:00
|
|
|
AstNodeBiop* andp;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (VN_IS(nodep->lhsp(), And)) {
|
2021-10-22 14:56:48 +02:00
|
|
|
andp = VN_AS(nodep->lhsp(), And);
|
2020-04-15 13:58:34 +02:00
|
|
|
ap = nodep->rhsp();
|
|
|
|
|
} else if (VN_IS(nodep->rhsp(), And)) {
|
2021-10-22 14:56:48 +02:00
|
|
|
andp = VN_AS(nodep->rhsp(), And);
|
2020-04-15 13:58:34 +02:00
|
|
|
ap = nodep->lhsp();
|
2022-01-08 18:01:39 +01:00
|
|
|
} else {
|
2020-04-15 13:58:34 +02:00
|
|
|
return false;
|
2022-01-08 18:01:39 +01:00
|
|
|
}
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeUniop* notp;
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* cp;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (VN_IS(andp->lhsp(), Not)) {
|
2021-10-22 14:56:48 +02:00
|
|
|
notp = VN_AS(andp->lhsp(), Not);
|
2020-04-15 13:58:34 +02:00
|
|
|
cp = andp->rhsp();
|
|
|
|
|
} else if (VN_IS(andp->rhsp(), Not)) {
|
2021-10-22 14:56:48 +02:00
|
|
|
notp = VN_AS(andp->rhsp(), Not);
|
2020-04-15 13:58:34 +02:00
|
|
|
cp = andp->lhsp();
|
2021-11-26 23:55:36 +01:00
|
|
|
} else {
|
2020-04-15 13:58:34 +02:00
|
|
|
return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
}
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const bp = notp->lhsp();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!operandsSame(ap, bp)) return false;
|
|
|
|
|
// Do it
|
|
|
|
|
cp->unlinkFrBack();
|
2020-04-15 13:58:34 +02:00
|
|
|
VL_DO_DANGLING(andp->unlinkFrBack()->deleteTree(), andp);
|
|
|
|
|
VL_DANGLING(notp);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Replace whichever branch is now dangling
|
2020-04-15 13:58:34 +02:00
|
|
|
if (nodep->rhsp()) {
|
|
|
|
|
nodep->lhsp(cp);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->rhsp(cp);
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
return true;
|
2009-01-07 15:37:59 +01:00
|
|
|
}
|
2019-11-10 19:17:29 +01:00
|
|
|
bool matchAndCond(AstAnd* nodep) {
|
|
|
|
|
// Push down a AND into conditional, when one side of conditional is constant
|
|
|
|
|
// (otherwise we'd be trading one operation for two operations)
|
|
|
|
|
// V3Clean often makes this pattern, as it postpones the AND until
|
2022-12-03 00:46:38 +01:00
|
|
|
// as high as possible, which is usually the right choice, except for this.
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeCond* const condp = VN_CAST(nodep->rhsp(), NodeCond);
|
2019-11-10 19:17:29 +01:00
|
|
|
if (!condp) return false;
|
2022-09-15 20:43:56 +02:00
|
|
|
if (!VN_IS(condp->thenp(), Const) && !VN_IS(condp->elsep(), Const)) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const maskp = VN_CAST(nodep->lhsp(), Const);
|
2019-11-10 19:17:29 +01:00
|
|
|
if (!maskp) return false;
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, "AND(CONSTm, CONDcond(c, i, e))->CONDcond(c, AND(m,i), AND(m, e)) " << nodep
|
|
|
|
|
<< endl);
|
2022-09-15 20:43:56 +02:00
|
|
|
AstNodeCond* const newp = static_cast<AstNodeCond*>(condp->cloneType(
|
|
|
|
|
condp->condp()->unlinkFrBack(),
|
2022-11-21 02:13:55 +01:00
|
|
|
new AstAnd{nodep->fileline(), maskp->cloneTree(false), condp->thenp()->unlinkFrBack()},
|
|
|
|
|
new AstAnd{nodep->fileline(), maskp->cloneTree(false),
|
|
|
|
|
condp->elsep()->unlinkFrBack()}));
|
2019-11-10 19:17:29 +01:00
|
|
|
newp->dtypeFrom(nodep);
|
2022-09-15 20:43:56 +02:00
|
|
|
newp->thenp()->dtypeFrom(nodep); // As And might have been to change widths
|
|
|
|
|
newp->elsep()->dtypeFrom(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-11-10 19:17:29 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
2021-06-18 17:18:30 +02:00
|
|
|
bool matchMaskedOr(AstAnd* nodep) {
|
|
|
|
|
// Masking an OR with terms that have no bits set under the mask is replaced with masking
|
|
|
|
|
// only the remaining terms. Canonical example as generated by V3Expand is:
|
|
|
|
|
// 0xff & (a << 8 | b >> 24) --> 0xff & (b >> 24)
|
|
|
|
|
|
|
|
|
|
// Compute how many significant bits are in the mask
|
2021-10-22 14:56:48 +02:00
|
|
|
const AstConst* const constp = VN_AS(nodep->lhsp(), Const);
|
2021-06-18 17:18:30 +02:00
|
|
|
const uint32_t significantBits = constp->num().widthMin();
|
|
|
|
|
|
2021-10-22 14:56:48 +02:00
|
|
|
AstOr* const orp = VN_AS(nodep->rhsp(), Or);
|
2021-06-18 17:18:30 +02:00
|
|
|
|
|
|
|
|
// Predicate for checking whether the bottom 'significantBits' bits of the given expression
|
|
|
|
|
// are all zeroes.
|
|
|
|
|
const auto checkBottomClear = [=](const AstNode* nodep) -> bool {
|
2021-10-22 16:15:42 +02:00
|
|
|
if (const AstShiftL* const shiftp = VN_CAST(nodep, ShiftL)) {
|
|
|
|
|
if (const AstConst* const scp = VN_CAST(shiftp->rhsp(), Const)) {
|
2021-06-18 17:18:30 +02:00
|
|
|
return scp->num().toUInt() >= significantBits;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const bool orLIsRedundant = checkBottomClear(orp->lhsp());
|
|
|
|
|
const bool orRIsRedundant = checkBottomClear(orp->rhsp());
|
|
|
|
|
|
|
|
|
|
if (orLIsRedundant && orRIsRedundant) {
|
|
|
|
|
nodep->replaceWith(
|
2022-11-21 02:13:55 +01:00
|
|
|
new AstConst{nodep->fileline(), AstConst::DTyped{}, nodep->dtypep()});
|
2021-06-18 17:18:30 +02:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
return true;
|
|
|
|
|
} else if (orLIsRedundant) {
|
|
|
|
|
orp->replaceWith(orp->rhsp()->unlinkFrBack());
|
|
|
|
|
VL_DO_DANGLING(orp->deleteTree(), orp);
|
|
|
|
|
return false; // input node is still valid, keep going
|
|
|
|
|
} else if (orRIsRedundant) {
|
|
|
|
|
orp->replaceWith(orp->lhsp()->unlinkFrBack());
|
|
|
|
|
VL_DO_DANGLING(orp->deleteTree(), orp);
|
|
|
|
|
return false; // input node is still valid, keep going
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-18 19:52:02 +02:00
|
|
|
bool matchMaskedShift(AstAnd* nodep) {
|
|
|
|
|
// Drop redundant masking of right shift result. E.g: 0xff & ((uint32_t)a >> 24). This
|
|
|
|
|
// commonly appears after V3Expand and the simplification in matchMaskedOr. Similarly,
|
|
|
|
|
// drop redundant masking of left shift result. E.g.: 0xff000000 & ((uint32_t)a << 24).
|
|
|
|
|
|
|
|
|
|
const auto checkMask = [=](const V3Number& mask) -> bool {
|
2021-10-22 14:56:48 +02:00
|
|
|
const AstConst* const constp = VN_AS(nodep->lhsp(), Const);
|
2021-06-18 19:52:02 +02:00
|
|
|
if (constp->num().isCaseEq(mask)) {
|
2021-06-25 19:00:58 +02:00
|
|
|
AstNode* const rhsp = nodep->rhsp();
|
|
|
|
|
rhsp->unlinkFrBack();
|
|
|
|
|
nodep->replaceWith(rhsp);
|
|
|
|
|
rhsp->dtypeFrom(nodep);
|
2021-06-18 19:52:02 +02:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Check if masking is redundant
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstShiftR* const shiftp = VN_CAST(nodep->rhsp(), ShiftR)) {
|
|
|
|
|
if (const AstConst* const scp = VN_CAST(shiftp->rhsp(), Const)) {
|
2021-06-18 19:52:02 +02:00
|
|
|
// Check if mask is full over the non-zero bits
|
2022-11-21 02:13:55 +01:00
|
|
|
V3Number maskLo{nodep, nodep->width()};
|
2021-06-18 19:52:02 +02:00
|
|
|
maskLo.setMask(nodep->width() - scp->num().toUInt());
|
|
|
|
|
return checkMask(maskLo);
|
|
|
|
|
}
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstShiftL* const shiftp = VN_CAST(nodep->rhsp(), ShiftL)) {
|
|
|
|
|
if (const AstConst* const scp = VN_CAST(shiftp->rhsp(), Const)) {
|
2021-06-18 19:52:02 +02:00
|
|
|
// Check if mask is full over the non-zero bits
|
2022-11-21 02:13:55 +01:00
|
|
|
V3Number maskLo{nodep, nodep->width()};
|
|
|
|
|
V3Number maskHi{nodep, nodep->width()};
|
2021-06-18 19:52:02 +02:00
|
|
|
maskLo.setMask(nodep->width() - scp->num().toUInt());
|
|
|
|
|
maskHi.opShiftL(maskLo, scp->num());
|
|
|
|
|
return checkMask(maskHi);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-13 21:33:11 +01:00
|
|
|
bool matchBitOpTree(AstNodeExpr* nodep) {
|
2021-08-14 22:09:01 +02:00
|
|
|
if (nodep->widthMin() != 1) return false;
|
2022-06-04 02:43:16 +02:00
|
|
|
if (!v3Global.opt.fConstBitOpTree()) return false;
|
2021-03-11 00:08:11 +01:00
|
|
|
|
2021-08-14 22:09:01 +02:00
|
|
|
string debugPrefix;
|
|
|
|
|
if (debug() >= 9) { // LCOV_EXCL_START
|
|
|
|
|
static int c = 0;
|
2022-11-27 14:31:22 +01:00
|
|
|
debugPrefix = "- matchBitOpTree[";
|
2021-08-14 22:09:01 +02:00
|
|
|
debugPrefix += cvtToStr(++c);
|
|
|
|
|
debugPrefix += "] ";
|
|
|
|
|
nodep->dumpTree(debugPrefix + "INPUT: ");
|
|
|
|
|
} // LCOV_EXCL_STOP
|
|
|
|
|
|
2021-02-21 10:11:33 +01:00
|
|
|
AstNode* newp = nullptr;
|
2021-08-18 20:15:02 +02:00
|
|
|
const AstAnd* const andp = VN_CAST(nodep, And);
|
|
|
|
|
const int width = nodep->width();
|
|
|
|
|
if (andp && isConst(andp->lhsp(), 1)) { // 1 & BitOpTree
|
|
|
|
|
newp = ConstBitOpTreeVisitor::simplify(andp->rhsp(), width, 1, m_statBitOpReduction);
|
|
|
|
|
} else { // BitOpTree
|
|
|
|
|
newp = ConstBitOpTreeVisitor::simplify(nodep, width, 0, m_statBitOpReduction);
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
2021-08-18 20:15:02 +02:00
|
|
|
|
2021-02-21 10:11:33 +01:00
|
|
|
if (newp) {
|
|
|
|
|
UINFO(4, "Transformed leaf of bit tree to " << newp << std::endl);
|
|
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
}
|
2021-08-18 20:15:02 +02:00
|
|
|
|
2021-08-14 22:09:01 +02:00
|
|
|
if (debug() >= 9) { // LCOV_EXCL_START
|
|
|
|
|
if (newp) {
|
|
|
|
|
newp->dumpTree(debugPrefix + "RESULT: ");
|
|
|
|
|
} else {
|
|
|
|
|
cout << debugPrefix << "not replaced" << endl;
|
|
|
|
|
}
|
|
|
|
|
} // LCOV_EXCL_STOP
|
2021-08-18 20:15:02 +02:00
|
|
|
|
2021-02-21 10:11:33 +01:00
|
|
|
return newp;
|
|
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
static bool operandShiftSame(const AstNode* nodep) {
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeBiop* const np = VN_AS(nodep, NodeBiop);
|
2019-05-19 22:13:13 +02:00
|
|
|
{
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstShiftL* const lp = VN_CAST(np->lhsp(), ShiftL);
|
|
|
|
|
const AstShiftL* const rp = VN_CAST(np->rhsp(), ShiftL);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (lp && rp) {
|
2020-04-15 13:58:34 +02:00
|
|
|
return (lp->width() == rp->width() && lp->lhsp()->width() == rp->lhsp()->width()
|
2019-05-19 22:13:13 +02:00
|
|
|
&& operandsSame(lp->rhsp(), rp->rhsp()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
{
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstShiftR* const lp = VN_CAST(np->lhsp(), ShiftR);
|
|
|
|
|
const AstShiftR* const rp = VN_CAST(np->rhsp(), ShiftR);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (lp && rp) {
|
2020-04-15 13:58:34 +02:00
|
|
|
return (lp->width() == rp->width() && lp->lhsp()->width() == rp->lhsp()->width()
|
2019-05-19 22:13:13 +02:00
|
|
|
&& operandsSame(lp->rhsp(), rp->rhsp()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2008-11-17 17:36:01 +01:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
bool operandHugeShiftL(const AstNodeBiop* nodep) {
|
2021-10-22 16:15:42 +02:00
|
|
|
return (VN_IS(nodep->rhsp(), Const) && !VN_AS(nodep->rhsp(), Const)->num().isFourState()
|
|
|
|
|
&& (VN_AS(nodep->rhsp(), Const)->toUInt() >= static_cast<uint32_t>(nodep->width()))
|
2019-05-19 22:13:13 +02:00
|
|
|
&& isTPure(nodep->lhsp()));
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
bool operandHugeShiftR(const AstNodeBiop* nodep) {
|
2021-10-22 16:15:42 +02:00
|
|
|
return (VN_IS(nodep->rhsp(), Const) && !VN_AS(nodep->rhsp(), Const)->num().isFourState()
|
|
|
|
|
&& (VN_AS(nodep->rhsp(), Const)->toUInt()
|
2018-10-15 00:39:33 +02:00
|
|
|
>= static_cast<uint32_t>(nodep->lhsp()->width()))
|
2019-05-19 22:13:13 +02:00
|
|
|
&& isTPure(nodep->lhsp()));
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
bool operandIsTwo(const AstNode* nodep) {
|
2021-10-22 16:15:42 +02:00
|
|
|
return (VN_IS(nodep, Const) && !VN_AS(nodep, Const)->num().isFourState()
|
|
|
|
|
&& nodep->width() <= VL_QUADSIZE && VN_AS(nodep, Const)->toUQuad() == 2);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
bool operandIsTwostate(const AstNode* nodep) {
|
2021-10-22 16:15:42 +02:00
|
|
|
return (VN_IS(nodep, Const) && !VN_AS(nodep, Const)->num().isFourState());
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
bool operandIsPowTwo(const AstNode* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!operandIsTwostate(nodep)) return false;
|
2021-10-22 16:15:42 +02:00
|
|
|
return (1 == VN_AS(nodep, Const)->num().countOnes());
|
2006-09-01 18:53:14 +02:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
bool operandShiftOp(const AstNodeBiop* nodep) {
|
|
|
|
|
if (!VN_IS(nodep->rhsp(), Const)) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeBiop* const lhsp = VN_CAST(nodep->lhsp(), NodeBiop);
|
2018-02-02 03:32:58 +01:00
|
|
|
if (!lhsp || !(VN_IS(lhsp, And) || VN_IS(lhsp, Or) || VN_IS(lhsp, Xor))) return false;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (nodep->width() != lhsp->width()) return false;
|
|
|
|
|
if (nodep->width() != lhsp->lhsp()->width()) return false;
|
|
|
|
|
if (nodep->width() != lhsp->rhsp()->width()) return false;
|
2019-05-19 22:13:13 +02:00
|
|
|
return true;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
bool operandShiftShift(const AstNodeBiop* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// We could add a AND though.
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeBiop* const lhsp = VN_CAST(nodep->lhsp(), NodeBiop);
|
2018-02-02 03:32:58 +01:00
|
|
|
if (!lhsp || !(VN_IS(lhsp, ShiftL) || VN_IS(lhsp, ShiftR))) return false;
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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.
|
2018-02-02 03:32:58 +01:00
|
|
|
if (!(VN_IS(nodep->rhsp(), Const) && VN_IS(lhsp->rhsp(), Const))) return false;
|
2021-10-22 16:15:42 +02:00
|
|
|
if (VN_AS(nodep->rhsp(), Const)->num().isFourState()
|
|
|
|
|
|| VN_AS(lhsp->rhsp(), Const)->num().isFourState())
|
2020-04-15 13:58:34 +02:00
|
|
|
return false;
|
|
|
|
|
if (nodep->width() != lhsp->width()) return false;
|
|
|
|
|
if (nodep->width() != lhsp->lhsp()->width()) return false;
|
2019-05-19 22:13:13 +02:00
|
|
|
return true;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
bool operandWordOOB(const AstWordSel* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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.
|
2020-04-15 13:58:34 +02:00
|
|
|
return (VN_IS(nodep->rhsp(), Const) && VN_IS(nodep->fromp(), NodeVarRef)
|
2021-10-22 16:15:42 +02:00
|
|
|
&& VN_AS(nodep->fromp(), NodeVarRef)->access().isReadOnly()
|
|
|
|
|
&& (static_cast<int>(VN_AS(nodep->rhsp(), Const)->toUInt())
|
2021-10-22 14:56:48 +02:00
|
|
|
>= VN_AS(nodep->fromp(), NodeVarRef)->varp()->widthWords()));
|
2018-02-02 03:32:58 +01:00
|
|
|
}
|
|
|
|
|
bool operandSelFull(const AstSel* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
return (VN_IS(nodep->lsbp(), Const) && VN_IS(nodep->widthp(), Const)
|
|
|
|
|
&& nodep->lsbConst() == 0
|
2018-10-15 00:39:33 +02:00
|
|
|
&& static_cast<int>(nodep->widthConst()) == nodep->fromp()->width());
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
bool operandSelExtend(AstSel* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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
|
2021-11-26 23:55:36 +01:00
|
|
|
AstExtend* const extendp = VN_CAST(nodep->fromp(), Extend);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!(m_doV && extendp && VN_IS(nodep->lsbp(), Const) && VN_IS(nodep->widthp(), Const)
|
|
|
|
|
&& nodep->lsbConst() == 0
|
|
|
|
|
&& static_cast<int>(nodep->widthConst()) == extendp->lhsp()->width()))
|
|
|
|
|
return false;
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(replaceWChild(nodep, extendp->lhsp()), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
return true;
|
2011-03-12 13:45:04 +01:00
|
|
|
}
|
|
|
|
|
bool operandSelBiLower(AstSel* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeBiop* const bip = VN_CAST(nodep->fromp(), NodeBiop);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!(m_doV && bip && VN_IS(nodep->lsbp(), Const) && VN_IS(nodep->widthp(), Const)
|
|
|
|
|
&& nodep->lsbConst() == 0))
|
|
|
|
|
return false;
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) nodep->dumpTree("- SEL(BI)-in: ");
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const bilhsp = bip->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const birhsp = bip->rhsp()->unlinkFrBack();
|
2022-11-21 02:13:55 +01:00
|
|
|
bip->lhsp(new AstSel{nodep->fileline(), bilhsp, 0, nodep->widthConst()});
|
|
|
|
|
bip->rhsp(new AstSel{nodep->fileline(), birhsp, 0, nodep->widthConst()});
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) bip->dumpTree("- SEL(BI)-ou: ");
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(replaceWChild(nodep, bip), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
return true;
|
2011-03-12 13:45:04 +01:00
|
|
|
}
|
2017-09-20 02:56:17 +02:00
|
|
|
bool operandSelShiftLower(AstSel* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// AND({a}, SHIFTR({b}, {c})) is often shorthand in C for Verilog {b}[{c} :+ {a}]
|
|
|
|
|
// becomes thought other optimizations
|
|
|
|
|
// SEL(SHIFTR({a},{b}),{lsb},{width}) -> SEL({a},{lsb+b},{width})
|
2021-11-26 23:55:36 +01:00
|
|
|
AstShiftR* const shiftp = VN_CAST(nodep->fromp(), ShiftR);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!(m_doV && shiftp && VN_IS(shiftp->rhsp(), Const) && VN_IS(nodep->lsbp(), Const)
|
|
|
|
|
&& VN_IS(nodep->widthp(), Const))) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const ap = shiftp->lhsp();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const bp = VN_AS(shiftp->rhsp(), Const);
|
|
|
|
|
AstConst* const lp = VN_AS(nodep->lsbp(), Const);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (bp->isWide() || bp->num().isFourState() || bp->num().isNegative() || lp->isWide()
|
|
|
|
|
|| lp->num().isFourState() || lp->num().isNegative()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-06-21 00:32:57 +02:00
|
|
|
const int newLsb = lp->toSInt() + bp->toSInt();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (newLsb + nodep->widthConst() > ap->width()) return false;
|
|
|
|
|
//
|
|
|
|
|
UINFO(9, "SEL(SHIFTR(a,b),l,w) -> SEL(a,l+b,w)\n");
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) nodep->dumpTree("- SEL(SH)-in: ");
|
2021-11-26 23:55:36 +01:00
|
|
|
AstSel* const newp
|
2022-11-21 02:13:55 +01:00
|
|
|
= new AstSel{nodep->fileline(), ap->unlinkFrBack(), newLsb, nodep->widthConst()};
|
2019-05-19 22:13:13 +02:00
|
|
|
newp->dtypeFrom(nodep);
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) newp->dumpTree("- SEL(SH)-ou: ");
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->replaceWith(newp), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
return true;
|
2017-09-20 02:56:17 +02:00
|
|
|
}
|
2011-03-12 13:45:04 +01:00
|
|
|
|
2017-10-01 16:21:27 +02:00
|
|
|
bool operandBiExtendConstShrink(AstNodeBiop* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Loop unrolling favors standalone compares
|
|
|
|
|
// EQ(const{width32}, EXTEND(xx{width3})) -> EQ(const{3}, xx{3})
|
|
|
|
|
// The constant must have zero bits (+ 1 if signed) or compare
|
|
|
|
|
// would be incorrect. See also operandBiExtendConst
|
2021-11-26 23:55:36 +01:00
|
|
|
AstExtend* const extendp = VN_CAST(nodep->rhsp(), Extend);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!extendp) return false;
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const smallerp = extendp->lhsp();
|
2021-06-21 00:32:57 +02:00
|
|
|
const int subsize = smallerp->width();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const constp = VN_CAST(nodep->lhsp(), Const);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!constp) return false;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!constp->num().isBitsZero(constp->width() - 1, subsize)) return false;
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) nodep->dumpTree("- BI(EXTEND)-in: ");
|
2019-05-19 22:13:13 +02:00
|
|
|
smallerp->unlinkFrBack();
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(extendp->unlinkFrBack()->deleteTree(), extendp); // aka nodep->lhsp.
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->rhsp(smallerp);
|
2011-03-12 13:45:04 +01:00
|
|
|
|
2019-05-10 02:03:19 +02:00
|
|
|
constp->unlinkFrBack();
|
2022-11-21 02:13:55 +01:00
|
|
|
V3Number num{constp, subsize};
|
2019-05-10 02:03:19 +02:00
|
|
|
num.opAssign(constp->num());
|
2022-11-21 02:13:55 +01:00
|
|
|
nodep->lhsp(new AstConst{constp->fileline(), num});
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(constp->deleteTree(), constp);
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) nodep->dumpTree("- BI(EXTEND)-ou: ");
|
2019-05-19 22:13:13 +02:00
|
|
|
return true;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
bool operandBiExtendConstOver(const AstNodeBiop* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// EQ(const{width32}, EXTEND(xx{width3})) -> constant
|
|
|
|
|
// When the constant has non-zero bits above the extend it's a constant.
|
|
|
|
|
// Avoids compiler warning
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstExtend* const extendp = VN_CAST(nodep->rhsp(), Extend);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!extendp) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const smallerp = extendp->lhsp();
|
2021-06-21 00:32:57 +02:00
|
|
|
const int subsize = smallerp->width();
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstConst* const constp = VN_CAST(nodep->lhsp(), Const);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!constp) return false;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (constp->num().isBitsZero(constp->width() - 1, subsize)) return false;
|
2019-05-19 22:13:13 +02:00
|
|
|
return true;
|
2017-10-01 16:21:27 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2006-08-27 17:20:55 +02:00
|
|
|
AstNode* afterComment(AstNode* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Ignore comments, such as to determine if a AstIf is empty.
|
|
|
|
|
// nodep may be null, if so return null.
|
2018-02-02 03:32:58 +01:00
|
|
|
while (nodep && VN_IS(nodep, Comment)) { nodep = nodep->nextp(); }
|
2019-05-19 22:13:13 +02:00
|
|
|
return nodep;
|
2006-08-27 17:20:55 +02:00
|
|
|
}
|
|
|
|
|
|
2011-12-22 14:33:16 +01:00
|
|
|
bool isTPure(AstNode* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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,
|
2022-12-26 10:30:41 +01:00
|
|
|
// but for now can disable en masse until V3Purify takes effect.
|
2018-02-02 03:32:58 +01:00
|
|
|
return m_doShort || VN_IS(nodep, VarRef) || VN_IS(nodep, Const);
|
2011-12-22 14:33:16 +01:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// Extraction checks
|
|
|
|
|
bool warnSelect(AstSel* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_doGenerate) {
|
|
|
|
|
// Never checked yet
|
|
|
|
|
V3Width::widthParamsEdit(nodep);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep); // May need "constifying"
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
// Find range of dtype we are selecting from
|
|
|
|
|
// Similar code in V3Unknown::AstSel
|
2021-06-21 00:32:57 +02:00
|
|
|
const bool doit = true;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_warn && VN_IS(nodep->lsbp(), Const) && VN_IS(nodep->widthp(), Const) && doit) {
|
2021-06-21 00:32:57 +02:00
|
|
|
const int maxDeclBit = nodep->declRange().hiMaxSelect() * nodep->declElWidth()
|
|
|
|
|
+ (nodep->declElWidth() - 1);
|
2021-10-22 14:56:48 +02:00
|
|
|
if (VN_AS(nodep->lsbp(), Const)->num().isFourState()
|
|
|
|
|
|| VN_AS(nodep->widthp(), Const)->num().isFourState()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->v3error("Selection index is constantly unknown or tristated: "
|
2020-04-15 13:58:34 +02:00
|
|
|
"lsb="
|
|
|
|
|
<< nodep->lsbp()->name() << " width=" << nodep->widthp()->name());
|
2019-05-19 22:13:13 +02:00
|
|
|
// Replacing nodep will make a mess above, so we replace the offender
|
|
|
|
|
replaceZero(nodep->lsbp());
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (nodep->declRange().ranged()
|
|
|
|
|
&& (nodep->msbConst() > maxDeclBit || nodep->lsbConst() > maxDeclBit)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// See also warning in V3Width
|
|
|
|
|
// Must adjust by element width as declRange() is in number of elements
|
2021-09-28 13:28:02 +02:00
|
|
|
string msbLsbProtected;
|
|
|
|
|
if (nodep->declElWidth() == 0) {
|
2021-09-28 13:29:21 +02:00
|
|
|
msbLsbProtected = "(nodep->declElWidth() == 0) "
|
|
|
|
|
+ std::to_string(nodep->msbConst()) + ":"
|
|
|
|
|
+ std::to_string(nodep->lsbConst());
|
2021-09-28 13:28:02 +02:00
|
|
|
} else {
|
2021-09-28 13:29:21 +02:00
|
|
|
msbLsbProtected = std::to_string(nodep->msbConst() / nodep->declElWidth())
|
|
|
|
|
+ ":"
|
|
|
|
|
+ std::to_string(nodep->lsbConst() / nodep->declElWidth());
|
2021-09-28 13:28:02 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->v3warn(SELRANGE,
|
|
|
|
|
"Selection index out of range: "
|
2021-09-28 13:28:02 +02:00
|
|
|
<< msbLsbProtected << " outside "
|
2020-04-15 13:58:34 +02:00
|
|
|
<< nodep->declRange().hiMaxSelect() << ":0"
|
|
|
|
|
<< (nodep->declRange().lo() >= 0
|
|
|
|
|
? ""
|
|
|
|
|
: (" (adjusted +" + cvtToStr(-nodep->declRange().lo())
|
|
|
|
|
+ " to account for negative lsb)")));
|
|
|
|
|
UINFO(1, " Related Raw index is " << nodep->msbConst() << ":"
|
|
|
|
|
<< nodep->lsbConst() << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Don't replace with zero, we'll do it later
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false; // Not a transform, so NOP
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool operandsSame(AstNode* node1p, AstNode* node2p) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// For now we just detect constants & simple vars, though it could be more generic
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(node1p, Const) && VN_IS(node2p, Const)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
return node1p->sameGateTree(node2p);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (VN_IS(node1p, VarRef) && VN_IS(node2p, VarRef)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Avoid comparing widthMin's, which results in lost optimization attempts
|
|
|
|
|
// If cleanup sameGateTree to be smarter, this can be restored.
|
2020-04-15 13:58:34 +02:00
|
|
|
// return node1p->sameGateTree(node2p);
|
2019-05-19 22:13:13 +02:00
|
|
|
return node1p->same(node2p);
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
bool ifSameAssign(const AstNodeIf* nodep) {
|
2022-09-15 20:43:56 +02:00
|
|
|
const AstNodeAssign* const thensp = VN_CAST(nodep->thensp(), NodeAssign);
|
|
|
|
|
const AstNodeAssign* const elsesp = VN_CAST(nodep->elsesp(), NodeAssign);
|
|
|
|
|
if (!thensp || thensp->nextp()) return false; // Must be SINGLE statement
|
|
|
|
|
if (!elsesp || elsesp->nextp()) return false;
|
|
|
|
|
if (thensp->type() != elsesp->type()) return false; // Can't mix an assigndly with assign
|
|
|
|
|
if (!thensp->lhsp()->sameGateTree(elsesp->lhsp())) return false;
|
|
|
|
|
if (!thensp->rhsp()->gateTree()) return false;
|
|
|
|
|
if (!elsesp->rhsp()->gateTree()) return false;
|
2023-03-02 03:07:37 +01:00
|
|
|
if (m_underRecFunc) return false; // This optimization may lead to infinite recursion
|
2019-05-19 22:13:13 +02:00
|
|
|
return true;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
bool operandIfIf(const AstNodeIf* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->elsesp()) return false;
|
2022-09-15 20:43:56 +02:00
|
|
|
const AstNodeIf* const lowerIfp = VN_CAST(nodep->thensp(), NodeIf);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!lowerIfp || lowerIfp->nextp()) return false;
|
|
|
|
|
if (nodep->type() != lowerIfp->type()) return false;
|
|
|
|
|
if (afterComment(lowerIfp->elsesp())) return false;
|
|
|
|
|
return true;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
bool ifConcatMergeableBiop(const AstNode* nodep) {
|
2020-12-10 06:04:10 +01:00
|
|
|
return (VN_IS(nodep, And) || VN_IS(nodep, Or) || VN_IS(nodep, Xor));
|
2014-10-23 03:44:41 +02:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
bool ifAdjacentSel(const AstSel* lhsp, const AstSel* rhsp) {
|
2022-06-04 02:43:16 +02:00
|
|
|
if (!v3Global.opt.fAssemble()) return false; // opt disabled
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!lhsp || !rhsp) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNode* const lfromp = lhsp->fromp();
|
|
|
|
|
const AstNode* const rfromp = rhsp->fromp();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!lfromp || !rfromp || !lfromp->sameGateTree(rfromp)) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstConst* const lstart = VN_CAST(lhsp->lsbp(), Const);
|
|
|
|
|
const AstConst* const rstart = VN_CAST(rhsp->lsbp(), Const);
|
|
|
|
|
const AstConst* const lwidth = VN_CAST(lhsp->widthp(), Const);
|
|
|
|
|
const AstConst* const rwidth = VN_CAST(rhsp->widthp(), Const);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!lstart || !rstart || !lwidth || !rwidth) return false; // too complicated
|
2021-06-21 00:32:57 +02:00
|
|
|
const int rend = (rstart->toSInt() + rwidth->toSInt());
|
2018-10-15 00:39:33 +02:00
|
|
|
return (rend == lstart->toSInt());
|
2014-11-03 01:52:49 +01:00
|
|
|
}
|
2022-11-13 21:33:11 +01:00
|
|
|
bool ifMergeAdjacent(AstNodeExpr* lhsp, AstNodeExpr* rhsp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// called by concatmergeable to determine if {lhsp, rhsp} make sense
|
2022-06-04 02:43:16 +02:00
|
|
|
if (!v3Global.opt.fAssemble()) return false; // opt disabled
|
2019-05-19 22:13:13 +02:00
|
|
|
// two same varref
|
|
|
|
|
if (operandsSame(lhsp, rhsp)) return true;
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstSel* lselp = VN_CAST(lhsp, Sel);
|
|
|
|
|
const AstSel* rselp = VN_CAST(rhsp, Sel);
|
2019-05-19 22:13:13 +02:00
|
|
|
// a[i:0] a
|
|
|
|
|
if (lselp && !rselp && rhsp->sameGateTree(lselp->fromp()))
|
2022-11-21 02:13:55 +01:00
|
|
|
rselp = new AstSel{rhsp->fileline(), rhsp->cloneTree(false), 0, rhsp->width()};
|
2019-05-19 22:13:13 +02:00
|
|
|
// a[i:j] {a[j-1:k], b}
|
2018-02-02 03:32:58 +01:00
|
|
|
if (lselp && !rselp && VN_IS(rhsp, Concat))
|
|
|
|
|
return ifMergeAdjacent(lhsp, VN_CAST(rhsp, Concat)->lhsp());
|
2019-05-19 22:13:13 +02:00
|
|
|
// a a[msb:j]
|
|
|
|
|
if (rselp && !lselp && lhsp->sameGateTree(rselp->fromp()))
|
2022-11-21 02:13:55 +01:00
|
|
|
lselp = new AstSel{lhsp->fileline(), lhsp->cloneTree(false), 0, lhsp->width()};
|
2019-05-19 22:13:13 +02:00
|
|
|
// {b, a[j:k]} a[k-1:i]
|
2018-02-02 03:32:58 +01:00
|
|
|
if (rselp && !lselp && VN_IS(lhsp, Concat))
|
|
|
|
|
return ifMergeAdjacent(VN_CAST(lhsp, Concat)->rhsp(), rhsp);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!lselp || !rselp) return false;
|
2014-11-03 01:52:49 +01:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// a[a:b] a[b-1:c] are adjacent
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const lfromp = lselp->fromp();
|
|
|
|
|
AstNode* const rfromp = rselp->fromp();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!lfromp || !rfromp || !lfromp->sameGateTree(rfromp)) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const lstart = VN_CAST(lselp->lsbp(), Const);
|
|
|
|
|
AstConst* const rstart = VN_CAST(rselp->lsbp(), Const);
|
|
|
|
|
AstConst* const lwidth = VN_CAST(lselp->widthp(), Const);
|
|
|
|
|
AstConst* const rwidth = VN_CAST(rselp->widthp(), Const);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!lstart || !rstart || !lwidth || !rwidth) return false; // too complicated
|
2021-06-21 00:32:57 +02:00
|
|
|
const int rend = (rstart->toSInt() + rwidth->toSInt());
|
2019-05-19 22:13:13 +02:00
|
|
|
// a[i:j] a[j-1:k]
|
|
|
|
|
if (rend == lstart->toSInt()) return true;
|
|
|
|
|
// a[i:0] a[msb:j]
|
|
|
|
|
if (rend == rfromp->width() && lstart->toSInt() == 0) return true;
|
|
|
|
|
return false;
|
2014-10-23 03:44:41 +02:00
|
|
|
}
|
2022-11-13 21:33:11 +01:00
|
|
|
bool concatMergeable(const AstNodeExpr* lhsp, const AstNodeExpr* rhsp, unsigned depth) {
|
2019-09-09 13:50:21 +02:00
|
|
|
// determine if {a OP b, c OP d} => {a, c} OP {b, d} is advantageous
|
2022-06-04 02:43:16 +02:00
|
|
|
if (!v3Global.opt.fAssemble()) return false; // opt disabled
|
2019-05-19 22:13:13 +02:00
|
|
|
if (lhsp->type() != rhsp->type()) return false;
|
|
|
|
|
if (!ifConcatMergeableBiop(lhsp)) return false;
|
2022-05-05 13:02:52 +02:00
|
|
|
if (depth > CONCAT_MERGABLE_MAX_DEPTH) return false; // As worse case O(n^2) algorithm
|
2014-10-23 03:44:41 +02:00
|
|
|
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeBiop* const lp = VN_CAST(lhsp, NodeBiop);
|
|
|
|
|
const AstNodeBiop* const rp = VN_CAST(rhsp, NodeBiop);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!lp || !rp) return false;
|
|
|
|
|
// {a[]&b[], a[]&b[]}
|
2021-06-21 00:32:57 +02:00
|
|
|
const bool lad = ifMergeAdjacent(lp->lhsp(), rp->lhsp());
|
|
|
|
|
const bool rad = ifMergeAdjacent(lp->rhsp(), rp->rhsp());
|
2019-05-19 22:13:13 +02:00
|
|
|
if (lad && rad) return true;
|
|
|
|
|
// {a[] & b[]&c[], a[] & b[]&c[]}
|
2022-05-05 13:02:52 +02:00
|
|
|
if (lad && concatMergeable(lp->rhsp(), rp->rhsp(), depth + 1)) return true;
|
2019-05-19 22:13:13 +02:00
|
|
|
// {a[]&b[] & c[], a[]&b[] & c[]}
|
2022-05-05 13:02:52 +02:00
|
|
|
if (rad && concatMergeable(lp->lhsp(), rp->lhsp(), depth + 1)) return true;
|
2020-04-04 04:31:54 +02:00
|
|
|
// {(a[]&b[])&(c[]&d[]), (a[]&b[])&(c[]&d[])}
|
2022-05-05 13:02:52 +02:00
|
|
|
if (concatMergeable(lp->lhsp(), rp->lhsp(), depth + 1)
|
|
|
|
|
&& concatMergeable(lp->rhsp(), rp->rhsp(), depth + 1)) {
|
2020-04-15 13:58:34 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
return false;
|
2014-10-23 03:44:41 +02:00
|
|
|
}
|
2020-06-11 13:39:37 +02:00
|
|
|
bool operandsSameSize(AstNode* lhsp, AstNode* rhsp) { return lhsp->width() == rhsp->width(); }
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
//----------------------------------------
|
|
|
|
|
// Constant Replacement functions.
|
|
|
|
|
// These all take a node, delete its tree, and replaces it with a constant
|
|
|
|
|
|
2018-08-25 15:52:45 +02:00
|
|
|
void replaceNum(AstNode* oldp, const V3Number& num) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Replace oldp node with a constant set to specified value
|
2018-06-14 00:05:00 +02:00
|
|
|
UASSERT(oldp, "Null old");
|
2021-10-22 14:56:48 +02:00
|
|
|
UASSERT_OBJ(!(VN_IS(oldp, Const) && !VN_AS(oldp, Const)->num().isFourState()), oldp,
|
2020-04-15 13:58:34 +02:00
|
|
|
"Already constant??");
|
2022-11-21 02:13:55 +01:00
|
|
|
AstNode* const newp = new AstConst{oldp->fileline(), num};
|
2019-05-19 22:13:13 +02:00
|
|
|
newp->dtypeFrom(oldp);
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() > 5) oldp->dumpTree("- const_old: ");
|
|
|
|
|
if (debug() > 5) newp->dumpTree("- _new: ");
|
2019-05-19 22:13:13 +02:00
|
|
|
oldp->replaceWith(newp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(oldp->deleteTree(), oldp);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
void replaceNum(AstNode* nodep, uint32_t val) {
|
2022-11-21 02:13:55 +01:00
|
|
|
V3Number num{nodep, nodep->width(), val};
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
void replaceNumSigned(AstNodeBiop* nodep, uint32_t val) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// We allow both sides to be constant, as one may have come from
|
|
|
|
|
// parameter propagation, etc.
|
2018-02-02 03:32:58 +01:00
|
|
|
if (m_warn && !(VN_IS(nodep->lhsp(), Const) && VN_IS(nodep->rhsp(), Const))) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->v3warn(UNSIGNED, "Comparison is constant due to unsigned arithmetic");
|
|
|
|
|
}
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(replaceNum(nodep, val), nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
void replaceNumLimited(AstNodeBiop* nodep, uint32_t val) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Avoids gcc warning about same
|
|
|
|
|
if (m_warn) nodep->v3warn(CMPCONST, "Comparison is constant due to limited range");
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(replaceNum(nodep, val), nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
void replaceZero(AstNode* nodep) { VL_DO_DANGLING(replaceNum(nodep, 0), nodep); }
|
2022-11-13 21:33:11 +01:00
|
|
|
void replaceZeroChkPure(AstNode* nodep, AstNodeExpr* checkp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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)) {
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(replaceNum(nodep, 0), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2022-11-21 02:13:55 +01:00
|
|
|
AstNode* const newp = new AstAnd{nodep->fileline(), new AstConst{nodep->fileline(), 0},
|
|
|
|
|
checkp->unlinkFrBack()};
|
2019-05-19 22:13:13 +02:00
|
|
|
newp->dtypeFrom(nodep);
|
|
|
|
|
nodep->replaceWith(newp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2011-12-22 14:33:16 +01:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
void replaceAllOnes(AstNode* nodep) {
|
2022-11-21 02:13:55 +01:00
|
|
|
V3Number ones{nodep, nodep->width(), 0};
|
2019-05-10 02:03:19 +02:00
|
|
|
ones.setMask(nodep->width());
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(replaceNum(nodep, ones), nodep);
|
2006-09-27 20:00:53 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
void replaceConst(AstNodeUniop* nodep) {
|
2022-11-21 02:13:55 +01:00
|
|
|
V3Number num{nodep, nodep->width()};
|
2021-10-22 14:56:48 +02:00
|
|
|
nodep->numberOperate(num, VN_AS(nodep->lhsp(), Const)->num());
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, "UNICONST -> " << num << endl);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
void replaceConst(AstNodeBiop* nodep) {
|
2022-11-21 02:13:55 +01:00
|
|
|
V3Number num{nodep, nodep->width()};
|
2021-10-22 14:56:48 +02:00
|
|
|
nodep->numberOperate(num, VN_AS(nodep->lhsp(), Const)->num(),
|
|
|
|
|
VN_AS(nodep->rhsp(), Const)->num());
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, "BICONST -> " << num << endl);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
void replaceConst(AstNodeTriop* nodep) {
|
2022-11-21 02:13:55 +01:00
|
|
|
V3Number num{nodep, nodep->width()};
|
2021-10-22 14:56:48 +02:00
|
|
|
nodep->numberOperate(num, VN_AS(nodep->lhsp(), Const)->num(),
|
|
|
|
|
VN_AS(nodep->rhsp(), Const)->num(),
|
|
|
|
|
VN_AS(nodep->thsp(), Const)->num());
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, "TRICONST -> " << num << endl);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-05-10 20:27:22 +02:00
|
|
|
void replaceConst(AstNodeQuadop* nodep) {
|
2022-11-21 02:13:55 +01:00
|
|
|
V3Number num{nodep, nodep->width()};
|
2020-05-10 20:27:22 +02:00
|
|
|
nodep->numberOperate(
|
2021-10-22 14:56:48 +02:00
|
|
|
num, VN_AS(nodep->lhsp(), Const)->num(), VN_AS(nodep->rhsp(), Const)->num(),
|
|
|
|
|
VN_AS(nodep->thsp(), Const)->num(), VN_AS(nodep->fhsp(), Const)->num());
|
2020-05-10 20:27:22 +02:00
|
|
|
UINFO(4, "QUADCONST -> " << num << endl);
|
|
|
|
|
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2018-08-25 15:52:45 +02:00
|
|
|
void replaceConstString(AstNode* oldp, const string& num) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Replace oldp node with a constant set to specified value
|
2018-06-14 00:05:00 +02:00
|
|
|
UASSERT(oldp, "Null old");
|
2022-11-21 02:13:55 +01:00
|
|
|
AstNode* const newp = new AstConst{oldp->fileline(), AstConst::String{}, num};
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() > 5) oldp->dumpTree("- const_old: ");
|
|
|
|
|
if (debug() > 5) newp->dumpTree("- _new: ");
|
2019-05-19 22:13:13 +02:00
|
|
|
oldp->replaceWith(newp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(oldp->deleteTree(), oldp);
|
2010-01-18 02:06:08 +01:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
//----------------------------------------
|
|
|
|
|
// Replacement functions.
|
|
|
|
|
// These all take a node and replace it with something else
|
|
|
|
|
|
2022-11-13 21:33:11 +01:00
|
|
|
void replaceWChild(AstNode* nodep, AstNodeExpr* childp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// NODE(..., CHILD(...)) -> CHILD(...)
|
|
|
|
|
childp->unlinkFrBackWithNext();
|
|
|
|
|
// If replacing a SEL for example, the data type comes from the parent (is less wide).
|
2019-09-09 13:50:21 +02:00
|
|
|
// This may adversely affect the operation of the node being replaced.
|
2019-05-19 22:13:13 +02:00
|
|
|
childp->dtypeFrom(nodep);
|
|
|
|
|
nodep->replaceWith(childp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-11-13 21:33:11 +01:00
|
|
|
void replaceWChildBool(AstNode* nodep, AstNodeExpr* childp) {
|
2022-11-05 12:35:42 +01:00
|
|
|
// NODE(..., CHILD(...)) -> REDOR(CHILD(...))
|
|
|
|
|
childp->unlinkFrBack();
|
|
|
|
|
if (childp->width1()) {
|
|
|
|
|
nodep->replaceWith(childp);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->replaceWith(new AstRedOr{childp->fileline(), childp});
|
|
|
|
|
}
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
}
|
2012-04-20 04:53:52 +02:00
|
|
|
|
|
|
|
|
//! Replace a ternary node with its RHS after iterating
|
2019-09-09 13:50:21 +02:00
|
|
|
//! Used with short-circuiting, where the RHS has not yet been iterated.
|
2012-04-20 04:53:52 +02:00
|
|
|
void replaceWIteratedRhs(AstNodeTriop* nodep) {
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstNode* const rhsp = nodep->rhsp()) iterateAndNextNull(rhsp);
|
2019-05-19 22:13:13 +02:00
|
|
|
replaceWChild(nodep, nodep->rhsp()); // May have changed
|
2012-04-20 04:53:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Replace a ternary node with its THS after iterating
|
2019-09-09 13:50:21 +02:00
|
|
|
//! Used with short-circuiting, where the THS has not yet been iterated.
|
2012-04-20 04:53:52 +02:00
|
|
|
void replaceWIteratedThs(AstNodeTriop* nodep) {
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstNode* const thsp = nodep->thsp()) iterateAndNextNull(thsp);
|
2019-05-19 22:13:13 +02:00
|
|
|
replaceWChild(nodep, nodep->thsp()); // May have changed
|
2012-04-20 04:53:52 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
void replaceWLhs(AstNodeUniop* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Keep LHS, remove RHS
|
|
|
|
|
replaceWChild(nodep, nodep->lhsp());
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
void replaceWLhs(AstNodeBiop* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Keep LHS, remove RHS
|
|
|
|
|
replaceWChild(nodep, nodep->lhsp());
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
void replaceWRhs(AstNodeBiop* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Keep RHS, remove LHS
|
|
|
|
|
replaceWChild(nodep, nodep->rhsp());
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-11-05 12:35:42 +01:00
|
|
|
void replaceWLhsBool(AstNodeBiop* nodep) { replaceWChildBool(nodep, nodep->lhsp()); }
|
|
|
|
|
void replaceWRhsBool(AstNodeBiop* nodep) { replaceWChildBool(nodep, nodep->rhsp()); }
|
2018-08-25 15:52:45 +02:00
|
|
|
void replaceAsv(AstNodeBiop* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// BIASV(CONSTa, BIASV(CONSTb, c)) -> BIASV( BIASV_CONSTED(a,b), c)
|
|
|
|
|
// BIASV(SAMEa, BIASV(SAMEb, c)) -> BIASV( BIASV(SAMEa,SAMEb), c)
|
2022-11-27 14:31:22 +01:00
|
|
|
// nodep->dumpTree("- repAsvConst_old: ");
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const ap = nodep->lhsp();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeBiop* const rp = VN_AS(nodep->rhsp(), NodeBiop);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const bp = rp->lhsp();
|
|
|
|
|
AstNodeExpr* const cp = rp->rhsp();
|
2019-05-19 22:13:13 +02:00
|
|
|
ap->unlinkFrBack();
|
|
|
|
|
bp->unlinkFrBack();
|
|
|
|
|
cp->unlinkFrBack();
|
|
|
|
|
rp->unlinkFrBack();
|
|
|
|
|
nodep->lhsp(rp);
|
|
|
|
|
nodep->rhsp(cp);
|
|
|
|
|
rp->lhsp(ap);
|
|
|
|
|
rp->rhsp(bp);
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(rp->lhsp(), Const) && VN_IS(rp->rhsp(), Const)) replaceConst(rp);
|
2022-11-27 14:31:22 +01:00
|
|
|
// nodep->dumpTree("- repAsvConst_new: ");
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
void replaceAsvLUp(AstNodeBiop* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// BIASV(BIASV(CONSTll,lr),r) -> BIASV(CONSTll,BIASV(lr,r))
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeBiop* const lp = VN_AS(nodep->lhsp()->unlinkFrBack(), NodeBiop);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const llp = lp->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const lrp = lp->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const rp = nodep->rhsp()->unlinkFrBack();
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->lhsp(llp);
|
|
|
|
|
nodep->rhsp(lp);
|
|
|
|
|
lp->lhsp(lrp);
|
|
|
|
|
lp->rhsp(rp);
|
2022-11-27 14:31:22 +01:00
|
|
|
// nodep->dumpTree("- repAsvLUp_new: ");
|
2008-11-17 16:40:58 +01:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
void replaceAsvRUp(AstNodeBiop* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// BIASV(l,BIASV(CONSTrl,rr)) -> BIASV(CONSTrl,BIASV(l,rr))
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const lp = nodep->lhsp()->unlinkFrBack();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeBiop* const rp = VN_AS(nodep->rhsp()->unlinkFrBack(), NodeBiop);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const rlp = rp->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const rrp = rp->rhsp()->unlinkFrBack();
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->lhsp(rlp);
|
|
|
|
|
nodep->rhsp(rp);
|
|
|
|
|
rp->lhsp(lp);
|
|
|
|
|
rp->rhsp(rrp);
|
2022-11-27 14:31:22 +01:00
|
|
|
// nodep->dumpTree("- repAsvRUp_new: ");
|
2008-11-17 16:40:58 +01:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
void replaceAndOr(AstNodeBiop* nodep) {
|
2019-05-19 22:13:13 +02: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
|
|
|
|
|
// (Or/And may also be reversed)
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeBiop* const lp = VN_AS(nodep->lhsp()->unlinkFrBack(), NodeBiop);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const llp = lp->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const lrp = lp->rhsp()->unlinkFrBack();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeBiop* const rp = VN_AS(nodep->rhsp()->unlinkFrBack(), NodeBiop);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const rlp = rp->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const rrp = rp->rhsp()->unlinkFrBack();
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->replaceWith(lp);
|
|
|
|
|
if (operandsSame(llp, rlp)) {
|
|
|
|
|
lp->lhsp(llp);
|
|
|
|
|
lp->rhsp(nodep);
|
2021-11-24 00:15:21 +01:00
|
|
|
lp->dtypeFrom(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->lhsp(lrp);
|
|
|
|
|
nodep->rhsp(rrp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(rp->deleteTree(), rp);
|
|
|
|
|
VL_DO_DANGLING(rlp->deleteTree(), rlp);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else if (operandsSame(lrp, rrp)) {
|
|
|
|
|
lp->lhsp(nodep);
|
|
|
|
|
lp->rhsp(rrp);
|
2021-11-24 00:15:21 +01:00
|
|
|
lp->dtypeFrom(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->lhsp(llp);
|
|
|
|
|
nodep->rhsp(rlp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(rp->deleteTree(), rp);
|
|
|
|
|
VL_DO_DANGLING(lrp->deleteTree(), lrp);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
nodep->v3fatalSrc("replaceAndOr on something operandAndOrSame shouldn't have matched");
|
|
|
|
|
}
|
2022-11-27 14:31:22 +01:00
|
|
|
// nodep->dumpTree("- repAndOr_new: ");
|
2008-11-17 17:36:01 +01:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
void replaceShiftSame(AstNodeBiop* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Or(Shift(ll,CONSTlr),Shift(rl,CONSTrr==lr)) -> Shift(Or(ll,rl),CONSTlr)
|
|
|
|
|
// (Or/And may also be reversed)
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeBiop* const lp = VN_AS(nodep->lhsp()->unlinkFrBack(), NodeBiop);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const llp = lp->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const lrp = lp->rhsp()->unlinkFrBack();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeBiop* const rp = VN_AS(nodep->rhsp()->unlinkFrBack(), NodeBiop);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const rlp = rp->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const rrp = rp->rhsp()->unlinkFrBack();
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->replaceWith(lp);
|
|
|
|
|
lp->lhsp(nodep);
|
|
|
|
|
lp->rhsp(lrp);
|
|
|
|
|
nodep->lhsp(llp);
|
|
|
|
|
nodep->rhsp(rlp);
|
2022-07-29 00:05:04 +02:00
|
|
|
nodep->dtypep(llp->dtypep()); // dtype of Biop is before shift.
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(rp->deleteTree(), rp);
|
|
|
|
|
VL_DO_DANGLING(rrp->deleteTree(), rrp);
|
2022-11-27 14:31:22 +01:00
|
|
|
// nodep->dumpTree("- repShiftSame_new: ");
|
2008-11-17 17:36:01 +01:00
|
|
|
}
|
2014-10-23 03:44:41 +02:00
|
|
|
void replaceConcatSel(AstConcat* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// {a[1], a[0]} -> a[1:0]
|
2021-11-26 23:55:36 +01:00
|
|
|
AstSel* const lselp = VN_AS(nodep->lhsp()->unlinkFrBack(), Sel);
|
|
|
|
|
AstSel* const rselp = VN_AS(nodep->rhsp()->unlinkFrBack(), Sel);
|
2021-06-21 00:32:57 +02:00
|
|
|
const int lstart = lselp->lsbConst();
|
|
|
|
|
const int lwidth = lselp->widthConst();
|
|
|
|
|
const int rstart = rselp->lsbConst();
|
|
|
|
|
const int rwidth = rselp->widthConst();
|
2014-10-23 03:44:41 +02:00
|
|
|
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ((rstart + rwidth) == lstart, nodep,
|
|
|
|
|
"tried to merge two selects which are not adjacent");
|
2022-11-21 02:13:55 +01:00
|
|
|
AstSel* const newselp = new AstSel{
|
|
|
|
|
lselp->fromp()->fileline(), rselp->fromp()->cloneTree(false), rstart, lwidth + rwidth};
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, "merged two adjacent sel " << lselp << " and " << rselp << " to one " << newselp
|
|
|
|
|
<< endl);
|
2014-10-23 03:44:41 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->replaceWith(newselp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(lselp->deleteTree(), lselp);
|
|
|
|
|
VL_DO_DANGLING(rselp->deleteTree(), rselp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2014-10-23 03:44:41 +02:00
|
|
|
}
|
|
|
|
|
void replaceConcatMerge(AstConcat* nodep) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeBiop* const lp = VN_AS(nodep->lhsp(), NodeBiop);
|
|
|
|
|
AstNodeBiop* const rp = VN_AS(nodep->rhsp(), NodeBiop);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const llp = lp->lhsp()->cloneTree(false);
|
|
|
|
|
AstNodeExpr* const lrp = lp->rhsp()->cloneTree(false);
|
|
|
|
|
AstNodeExpr* const rlp = rp->lhsp()->cloneTree(false);
|
|
|
|
|
AstNodeExpr* const rrp = rp->rhsp()->cloneTree(false);
|
2022-05-05 13:02:52 +02:00
|
|
|
if (concatMergeable(lp, rp, 0)) {
|
2022-11-21 02:13:55 +01:00
|
|
|
AstConcat* const newlp = new AstConcat{rlp->fileline(), llp, rlp};
|
|
|
|
|
AstConcat* const newrp = new AstConcat{rrp->fileline(), lrp, rrp};
|
2019-05-19 22:13:13 +02:00
|
|
|
// use the lhs to replace the parent concat
|
|
|
|
|
lp->lhsp()->replaceWith(newlp);
|
|
|
|
|
lp->rhsp()->replaceWith(newrp);
|
2020-04-20 03:19:09 +02:00
|
|
|
lp->dtypeChgWidthSigned(newlp->width(), newlp->width(), VSigning::UNSIGNED);
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, "merged " << nodep << endl);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(rp->unlinkFrBack()->deleteTree(), rp);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->replaceWith(lp->unlinkFrBack());
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(lp->lhsp());
|
|
|
|
|
iterate(lp->rhsp());
|
2020-04-15 13:58:34 +02:00
|
|
|
} else {
|
|
|
|
|
nodep->v3fatalSrc("tried to merge two Concat which are not adjacent");
|
|
|
|
|
}
|
2014-10-23 03:44:41 +02:00
|
|
|
}
|
2022-11-13 21:33:11 +01:00
|
|
|
void replaceExtend(AstNode* nodep, AstNodeExpr* arg0p) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// -> EXTEND(nodep)
|
|
|
|
|
// like a AstExtend{$rhsp}, but we need to set the width correctly from base node
|
|
|
|
|
arg0p->unlinkFrBack();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const newp
|
2021-07-12 00:42:01 +02:00
|
|
|
= (VN_IS(nodep, ExtendS)
|
2022-11-13 21:33:11 +01:00
|
|
|
? static_cast<AstNodeExpr*>(new AstExtendS{nodep->fileline(), arg0p})
|
|
|
|
|
: static_cast<AstNodeExpr*>(new AstExtend{nodep->fileline(), arg0p}));
|
2019-05-19 22:13:13 +02:00
|
|
|
newp->dtypeFrom(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
void replacePowShift(AstNodeBiop* nodep) { // Pow or PowS
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, "POW(2,b)->SHIFTL(1,b) " << nodep << endl);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstShiftL* const newp
|
2022-11-21 02:13:55 +01:00
|
|
|
= new AstShiftL{nodep->fileline(), new AstConst{nodep->fileline(), 1}, rhsp};
|
2019-05-19 22:13:13 +02:00
|
|
|
newp->dtypeFrom(nodep);
|
|
|
|
|
newp->lhsp()->dtypeFrom(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
void replaceMulShift(AstMul* nodep) { // Mul, but not MulS as not simple shift
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, "MUL(2^n,b)->SHIFTL(b,n) " << nodep << endl);
|
2021-10-22 14:56:48 +02:00
|
|
|
const int amount = VN_AS(nodep->lhsp(), Const)->num().mostSetBitP1() - 1; // 2^n->n+1
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const opp = nodep->rhsp()->unlinkFrBack();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstShiftL* const newp
|
2022-11-21 02:13:55 +01:00
|
|
|
= new AstShiftL{nodep->fileline(), opp, new AstConst(nodep->fileline(), amount)};
|
2019-05-19 22:13:13 +02:00
|
|
|
newp->dtypeFrom(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2006-09-01 18:53:14 +02:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
void replaceDivShift(AstDiv* nodep) { // Mul, but not MulS as not simple shift
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, "DIV(b,2^n)->SHIFTR(b,n) " << nodep << endl);
|
2021-10-22 14:56:48 +02:00
|
|
|
const int amount = VN_AS(nodep->rhsp(), Const)->num().mostSetBitP1() - 1; // 2^n->n+1
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const opp = nodep->lhsp()->unlinkFrBack();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstShiftR* const newp
|
2022-11-21 02:13:55 +01:00
|
|
|
= new AstShiftR{nodep->fileline(), opp, new AstConst(nodep->fileline(), amount)};
|
2019-05-19 22:13:13 +02:00
|
|
|
newp->dtypeFrom(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2006-09-01 18:53:14 +02:00
|
|
|
}
|
2019-11-10 18:12:57 +01:00
|
|
|
void replaceModAnd(AstModDiv* nodep) { // Mod, but not ModS as not simple shift
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, "MOD(b,2^n)->AND(b,2^n-1) " << nodep << endl);
|
2021-10-22 14:56:48 +02:00
|
|
|
const int amount = VN_AS(nodep->rhsp(), Const)->num().mostSetBitP1() - 1; // 2^n->n+1
|
2022-11-21 02:13:55 +01:00
|
|
|
V3Number mask{nodep, nodep->width()};
|
2019-11-10 18:12:57 +01:00
|
|
|
mask.setMask(amount);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const opp = nodep->lhsp()->unlinkFrBack();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstAnd* const newp
|
2022-11-21 02:13:55 +01:00
|
|
|
= new AstAnd{nodep->fileline(), opp, new AstConst{nodep->fileline(), mask}};
|
2019-11-10 18:12:57 +01:00
|
|
|
newp->dtypeFrom(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-11-10 18:12:57 +01:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
void replaceShiftOp(AstNodeBiop* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, "SHIFT(AND(a,b),CONST)->AND(SHIFT(a,CONST),SHIFT(b,CONST)) " << nodep << endl);
|
2022-01-02 16:32:35 +01:00
|
|
|
VNRelinker handle;
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->unlinkFrBack(&handle);
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeBiop* const lhsp = VN_AS(nodep->lhsp(), NodeBiop);
|
2020-04-15 13:58:34 +02:00
|
|
|
lhsp->unlinkFrBack();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const shiftp = nodep->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const ap = lhsp->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const bp = lhsp->rhsp()->unlinkFrBack();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeBiop* const shift1p = nodep;
|
|
|
|
|
AstNodeBiop* const shift2p = nodep->cloneTree(true);
|
2020-04-15 13:58:34 +02:00
|
|
|
shift1p->lhsp(ap);
|
|
|
|
|
shift1p->rhsp(shiftp->cloneTree(true));
|
|
|
|
|
shift2p->lhsp(bp);
|
|
|
|
|
shift2p->rhsp(shiftp);
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeBiop* const newp = lhsp;
|
2020-04-15 13:58:34 +02:00
|
|
|
newp->lhsp(shift1p);
|
|
|
|
|
newp->rhsp(shift2p);
|
2019-05-19 22:13:13 +02:00
|
|
|
handle.relink(newp);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(newp); // Further reduce, either node may have more reductions.
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
void replaceShiftShift(AstNodeBiop* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, "SHIFT(SHIFT(a,s1),s2)->SHIFT(a,ADD(s1,s2)) " << nodep << endl);
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) nodep->dumpTree("- repShiftShift_old: ");
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeBiop* const lhsp = VN_AS(nodep->lhsp(), NodeBiop);
|
2020-04-15 13:58:34 +02:00
|
|
|
lhsp->unlinkFrBack();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const ap = lhsp->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const shift1p = lhsp->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const shift2p = nodep->rhsp()->unlinkFrBack();
|
2019-05-19 22:13:13 +02:00
|
|
|
// Shift1p and shift2p may have different sizes, both are
|
|
|
|
|
// self-determined so sum with infinite width
|
2020-04-15 13:58:34 +02:00
|
|
|
if (nodep->type() == lhsp->type()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
const int shift1 = VN_AS(shift1p, Const)->toUInt();
|
|
|
|
|
const int shift2 = VN_AS(shift2p, Const)->toUInt();
|
2021-06-21 00:32:57 +02:00
|
|
|
const int newshift = shift1 + shift2;
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(shift1p->deleteTree(), shift1p);
|
|
|
|
|
VL_DO_DANGLING(shift2p->deleteTree(), shift2p);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->lhsp(ap);
|
|
|
|
|
nodep->rhsp(new AstConst(nodep->fileline(), newshift));
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep); // Further reduce, either node may have more reductions.
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
// We know shift amounts are constant, but might be a mixed left/right shift
|
2021-10-22 14:56:48 +02:00
|
|
|
int shift1 = VN_AS(shift1p, Const)->toUInt();
|
2020-04-15 13:58:34 +02:00
|
|
|
if (VN_IS(lhsp, ShiftR)) shift1 = -shift1;
|
2021-10-22 14:56:48 +02:00
|
|
|
int shift2 = VN_AS(shift2p, Const)->toUInt();
|
2020-04-15 13:58:34 +02:00
|
|
|
if (VN_IS(nodep, ShiftR)) shift2 = -shift2;
|
2021-06-21 00:32:57 +02:00
|
|
|
const int newshift = shift1 + shift2;
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(shift1p->deleteTree(), shift1p);
|
|
|
|
|
VL_DO_DANGLING(shift2p->deleteTree(), shift2p);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* newp;
|
2022-11-21 02:13:55 +01:00
|
|
|
V3Number mask1{nodep, nodep->width()};
|
|
|
|
|
V3Number ones{nodep, nodep->width()};
|
2019-05-10 02:03:19 +02:00
|
|
|
ones.setMask(nodep->width());
|
2020-04-15 13:58:34 +02:00
|
|
|
if (shift1 < 0) {
|
2019-12-09 03:36:38 +01:00
|
|
|
mask1.opShiftR(ones, V3Number(nodep, VL_IDATASIZE, -shift1));
|
2019-05-10 02:03:19 +02:00
|
|
|
} else {
|
2019-12-09 03:36:38 +01:00
|
|
|
mask1.opShiftL(ones, V3Number(nodep, VL_IDATASIZE, shift1));
|
2019-05-10 02:03:19 +02:00
|
|
|
}
|
2022-11-21 02:13:55 +01:00
|
|
|
V3Number mask{nodep, nodep->width()};
|
2020-04-15 13:58:34 +02:00
|
|
|
if (shift2 < 0) {
|
2019-12-09 03:36:38 +01:00
|
|
|
mask.opShiftR(mask1, V3Number(nodep, VL_IDATASIZE, -shift2));
|
2019-05-10 02:03:19 +02:00
|
|
|
} else {
|
2019-12-09 03:36:38 +01:00
|
|
|
mask.opShiftL(mask1, V3Number(nodep, VL_IDATASIZE, shift2));
|
2019-05-10 02:03:19 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
if (newshift < 0) {
|
2022-11-21 02:13:55 +01:00
|
|
|
newp = new AstShiftR{nodep->fileline(), ap,
|
|
|
|
|
new AstConst(nodep->fileline(), -newshift)};
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2022-11-21 02:13:55 +01:00
|
|
|
newp = new AstShiftL{nodep->fileline(), ap,
|
|
|
|
|
new AstConst(nodep->fileline(), newshift)};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
newp->dtypeFrom(nodep);
|
2022-11-21 02:13:55 +01:00
|
|
|
newp = new AstAnd{nodep->fileline(), newp, new AstConst{nodep->fileline(), mask}};
|
2019-05-19 22:13:13 +02:00
|
|
|
newp->dtypeFrom(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2022-11-27 14:31:22 +01:00
|
|
|
// newp->dumpTree("- repShiftShift_new: ");
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(newp); // Further reduce, either node may have more reductions.
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(lhsp->deleteTree(), lhsp);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool replaceAssignMultiSel(AstNodeAssign* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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}
|
2020-04-15 13:58:34 +02:00
|
|
|
|
|
|
|
|
// Skip if we're not const'ing an entire module (IE doing only one assign, etc)
|
|
|
|
|
if (!m_modp) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstSel* const sel1p = VN_CAST(nodep->lhsp(), Sel);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!sel1p) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeAssign* const nextp = VN_CAST(nodep->nextp(), NodeAssign);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!nextp) return false;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->type() != nextp->type()) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstSel* const sel2p = VN_CAST(nextp->lhsp(), Sel);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!sel2p) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstVarRef* const varref1p = VN_CAST(sel1p->fromp(), VarRef);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!varref1p) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstVarRef* const varref2p = VN_CAST(sel2p->fromp(), VarRef);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!varref2p) return false;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!varref1p->sameGateTree(varref2p)) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const con1p = VN_CAST(sel1p->lsbp(), Const);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!con1p) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const con2p = VN_CAST(sel2p->lsbp(), Const);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!con2p) return false;
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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;
|
2021-10-25 13:56:59 +02:00
|
|
|
// If a variable is marked split_var, access to the variable should not be merged.
|
|
|
|
|
if (varref1p->varp()->attrSplitVar() || varref2p->varp()->attrSplitVar()) return false;
|
2019-05-19 22:13:13 +02:00
|
|
|
// Swap?
|
2020-04-15 13:58:34 +02:00
|
|
|
if ((con1p->toSInt() != con2p->toSInt() + sel2p->width())
|
|
|
|
|
&& (con2p->toSInt() != con1p->toSInt() + sel1p->width())) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-06-21 00:32:57 +02:00
|
|
|
const bool lsbFirstAssign = (con1p->toUInt() < con2p->toUInt());
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, "replaceAssignMultiSel " << nodep << endl);
|
|
|
|
|
UINFO(4, " && " << nextp << endl);
|
2022-11-27 14:31:22 +01:00
|
|
|
// nodep->dumpTree("- comb1: ");
|
|
|
|
|
// nextp->dumpTree("- comb2: ");
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const rhs1p = nodep->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const rhs2p = nextp->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeAssign* newp;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (lsbFirstAssign) {
|
2022-11-21 02:13:55 +01:00
|
|
|
newp = nodep->cloneType(new AstSel{sel1p->fileline(), varref1p->unlinkFrBack(),
|
|
|
|
|
sel1p->lsbConst(), sel1p->width() + sel2p->width()},
|
|
|
|
|
new AstConcat{rhs1p->fileline(), rhs2p, rhs1p});
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2022-11-21 02:13:55 +01:00
|
|
|
newp = nodep->cloneType(new AstSel{sel1p->fileline(), varref1p->unlinkFrBack(),
|
|
|
|
|
sel2p->lsbConst(), sel1p->width() + sel2p->width()},
|
|
|
|
|
new AstConcat{rhs1p->fileline(), rhs1p, rhs2p});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-11-27 14:31:22 +01:00
|
|
|
// pnewp->dumpTree("- conew: ");
|
2020-01-17 02:17:11 +01:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
VL_DO_DANGLING(nextp->unlinkFrBack()->deleteTree(), nextp);
|
2019-05-19 22:13:13 +02:00
|
|
|
return true;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
bool varNotReferenced(AstNode* nodep, AstVar* varp, int level = 0) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Return true if varp never referenced under node.
|
|
|
|
|
// Return false if referenced, or tree too deep to be worth it, or side effects
|
|
|
|
|
if (!nodep) return true;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (level > 2) return false;
|
2021-04-03 18:57:06 +02:00
|
|
|
if (!nodep->isPure()) return false; // For example a $fgetc can't be reordered
|
2021-10-22 14:56:48 +02:00
|
|
|
if (VN_IS(nodep, NodeVarRef) && VN_AS(nodep, NodeVarRef)->varp() == varp) return false;
|
2020-04-15 13:58:34 +02:00
|
|
|
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));
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool replaceNodeAssign(AstNodeAssign* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (VN_IS(nodep->lhsp(), VarRef) && VN_IS(nodep->rhsp(), VarRef)
|
2021-10-22 14:56:48 +02:00
|
|
|
&& VN_AS(nodep->lhsp(), VarRef)->sameNoLvalue(VN_AS(nodep->rhsp(), VarRef))
|
2018-02-02 03:32:58 +01:00
|
|
|
&& !VN_IS(nodep, AssignDly)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// X = X. Quite pointless, though X <= X may override another earlier assignment
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(nodep, AssignW)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
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 {
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (m_doV && VN_IS(nodep->lhsp(), Concat)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
bool need_temp = false;
|
2018-02-02 03:32:58 +01:00
|
|
|
if (m_warn && !VN_IS(nodep, AssignDly)) { // Is same var on LHS and RHS?
|
2019-05-19 22:13:13 +02:00
|
|
|
// Note only do this (need user4) when m_warn, which is
|
|
|
|
|
// done as unique visitor
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser4InUse m_inuser4;
|
2022-10-20 14:48:44 +02:00
|
|
|
nodep->lhsp()->foreach([](const AstVarRef* nodep) {
|
2022-01-09 23:34:10 +01:00
|
|
|
if (nodep->varp()) nodep->varp()->user4(1);
|
|
|
|
|
});
|
2022-10-20 14:48:44 +02:00
|
|
|
nodep->rhsp()->foreach([&need_temp](const AstVarRef* nodep) {
|
2022-01-09 23:34:10 +01:00
|
|
|
if (nodep->varp() && nodep->varp()->user4()) need_temp = true;
|
|
|
|
|
});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
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.
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, " ASSITEMP " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
// ASSIGN(CONCAT(lc1,lc2),rhs) -> ASSIGN(temp1,SEL(rhs,{size})),
|
|
|
|
|
// ASSIGN(temp2,SEL(newrhs,{size}))
|
|
|
|
|
// ASSIGN(lc1,temp1),
|
|
|
|
|
// ASSIGN(lc2,temp2)
|
|
|
|
|
} else {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, " ASSI " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
// ASSIGN(CONCAT(lc1,lc2),rhs) -> ASSIGN(lc1,SEL(rhs,{size})),
|
|
|
|
|
// ASSIGN(lc2,SEL(newrhs,{size}))
|
|
|
|
|
}
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) nodep->dumpTree("- Ass_old: ");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Unlink the stuff
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const lc1p = VN_AS(nodep->lhsp(), Concat)->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const lc2p = VN_AS(nodep->lhsp(), Concat)->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const conp = VN_AS(nodep->lhsp(), Concat)->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const rhs2p = rhsp->cloneTree(false);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Calc widths
|
2021-11-26 23:55:36 +01:00
|
|
|
const int lsb2 = 0;
|
|
|
|
|
const int msb2 = lsb2 + lc2p->width() - 1;
|
|
|
|
|
const int lsb1 = msb2 + 1;
|
|
|
|
|
const int msb1 = lsb1 + lc1p->width() - 1;
|
2020-04-15 13:58:34 +02:00
|
|
|
UASSERT_OBJ(msb1 == (conp->width() - 1), nodep, "Width calc mismatch");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Form ranges
|
2022-11-21 02:13:55 +01:00
|
|
|
AstSel* const sel1p = new AstSel{conp->fileline(), rhsp, lsb1, msb1 - lsb1 + 1};
|
|
|
|
|
AstSel* const sel2p = new AstSel{conp->fileline(), rhs2p, lsb2, msb2 - lsb2 + 1};
|
2019-05-19 22:13:13 +02:00
|
|
|
// Make new assigns of same flavor as old one
|
|
|
|
|
//*** Not cloneTree; just one node.
|
2022-08-29 15:26:00 +02:00
|
|
|
AstNodeAssign* newp = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!need_temp) {
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeAssign* const asn1ap = nodep->cloneType(lc1p, sel1p);
|
|
|
|
|
AstNodeAssign* const asn2ap = nodep->cloneType(lc2p, sel2p);
|
2019-05-19 22:13:13 +02:00
|
|
|
asn1ap->dtypeFrom(sel1p);
|
|
|
|
|
asn2ap->dtypeFrom(sel2p);
|
|
|
|
|
newp = AstNode::addNext(newp, asn1ap);
|
|
|
|
|
newp = AstNode::addNext(newp, asn2ap);
|
|
|
|
|
} else {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_modp, nodep, "Not under module");
|
2021-08-11 15:30:00 +02:00
|
|
|
UASSERT_OBJ(m_globalPass, nodep,
|
|
|
|
|
"Should not reach here when not invoked on whole AstNetlist");
|
2019-05-19 22:13:13 +02:00
|
|
|
// We could create just one temp variable, but we'll get better optimization
|
|
|
|
|
// if we make one per term.
|
2021-08-11 15:30:00 +02:00
|
|
|
AstVar* const temp1p
|
2022-11-13 21:33:11 +01:00
|
|
|
= new AstVar{sel1p->fileline(), VVarType::BLOCKTEMP,
|
2022-11-21 02:13:55 +01:00
|
|
|
m_concswapNames.get(sel1p), VFlagLogicPacked{}, msb1 - lsb1 + 1};
|
2021-08-11 15:30:00 +02:00
|
|
|
AstVar* const temp2p
|
2022-11-13 21:33:11 +01:00
|
|
|
= new AstVar{sel2p->fileline(), VVarType::BLOCKTEMP,
|
2022-11-21 02:13:55 +01:00
|
|
|
m_concswapNames.get(sel2p), VFlagLogicPacked{}, msb2 - lsb2 + 1};
|
2022-09-15 20:43:56 +02:00
|
|
|
m_modp->addStmtsp(temp1p);
|
|
|
|
|
m_modp->addStmtsp(temp2p);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeAssign* const asn1ap = nodep->cloneType(
|
|
|
|
|
new AstVarRef{sel1p->fileline(), temp1p, VAccess::WRITE}, sel1p);
|
|
|
|
|
AstNodeAssign* const asn2ap = nodep->cloneType(
|
|
|
|
|
new AstVarRef{sel2p->fileline(), temp2p, VAccess::WRITE}, sel2p);
|
|
|
|
|
AstNodeAssign* const asn1bp = nodep->cloneType(
|
|
|
|
|
lc1p, new AstVarRef{sel1p->fileline(), temp1p, VAccess::READ});
|
|
|
|
|
AstNodeAssign* const asn2bp = nodep->cloneType(
|
|
|
|
|
lc2p, new AstVarRef{sel2p->fileline(), temp2p, VAccess::READ});
|
2019-05-19 22:13:13 +02:00
|
|
|
asn1ap->dtypeFrom(temp1p);
|
|
|
|
|
asn1bp->dtypeFrom(temp1p);
|
|
|
|
|
asn2ap->dtypeFrom(temp2p);
|
|
|
|
|
asn2bp->dtypeFrom(temp2p);
|
|
|
|
|
// This order matters
|
|
|
|
|
newp = AstNode::addNext(newp, asn1ap);
|
|
|
|
|
newp = AstNode::addNext(newp, asn2ap);
|
|
|
|
|
newp = AstNode::addNext(newp, asn1bp);
|
|
|
|
|
newp = AstNode::addNext(newp, asn2bp);
|
|
|
|
|
}
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9 && newp) newp->dumpTreeAndNext(cout, "- _new: ");
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->addNextHere(newp);
|
|
|
|
|
// Cleanup
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
|
|
|
|
VL_DO_DANGLING(conp->deleteTree(), conp);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Further reduce, either node may have more reductions.
|
|
|
|
|
return true;
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (m_doV && VN_IS(nodep->rhsp(), StreamR)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// The right-streaming operator on rhs of assignment does not
|
|
|
|
|
// change the order of bits. Eliminate stream but keep its lhsp
|
|
|
|
|
// Unlink the stuff
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const srcp = VN_AS(nodep->rhsp(), StreamR)->lhsp()->unlinkFrBack();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const sizep = VN_AS(nodep->rhsp(), StreamR)->rhsp()->unlinkFrBack();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const streamp = VN_AS(nodep->rhsp(), StreamR)->unlinkFrBack();
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->rhsp(srcp);
|
|
|
|
|
// Cleanup
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(sizep->deleteTree(), sizep);
|
|
|
|
|
VL_DO_DANGLING(streamp->deleteTree(), streamp);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Further reduce, any of the nodes may have more reductions.
|
|
|
|
|
return true;
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (m_doV && VN_IS(nodep->lhsp(), StreamL)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Push the stream operator to the rhs of the assignment statement
|
2021-10-22 14:56:48 +02:00
|
|
|
const int dWidth = VN_AS(nodep->lhsp(), StreamL)->lhsp()->width();
|
2021-06-21 00:32:57 +02:00
|
|
|
const int sWidth = nodep->rhsp()->width();
|
2019-05-19 22:13:13 +02:00
|
|
|
// Unlink the stuff
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const dstp = VN_AS(nodep->lhsp(), StreamL)->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* streamp = VN_AS(nodep->lhsp(), StreamL)->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const srcp = nodep->rhsp()->unlinkFrBack();
|
2019-05-19 22:13:13 +02:00
|
|
|
// Connect the rhs to the stream operator and update its width
|
2021-10-22 14:56:48 +02:00
|
|
|
VN_AS(streamp, StreamL)->lhsp(srcp);
|
2020-04-20 03:19:09 +02:00
|
|
|
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Shrink the RHS if necessary
|
|
|
|
|
if (sWidth > dWidth) {
|
2022-11-21 02:13:55 +01:00
|
|
|
streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
// Link the nodes back in
|
|
|
|
|
nodep->lhsp(dstp);
|
|
|
|
|
nodep->rhsp(streamp);
|
|
|
|
|
return true;
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (m_doV && VN_IS(nodep->lhsp(), StreamR)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// The right stream operator on lhs of assignment statement does
|
|
|
|
|
// not reorder bits. However, if the rhs is wider than the lhs,
|
|
|
|
|
// then we select bits from the left-most, not the right-most.
|
2021-10-22 14:56:48 +02:00
|
|
|
const int dWidth = VN_AS(nodep->lhsp(), StreamR)->lhsp()->width();
|
2021-06-21 00:32:57 +02:00
|
|
|
const int sWidth = nodep->rhsp()->width();
|
2019-05-19 22:13:13 +02:00
|
|
|
// Unlink the stuff
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const dstp = VN_AS(nodep->lhsp(), StreamR)->lhsp()->unlinkFrBack();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const sizep = VN_AS(nodep->lhsp(), StreamR)->rhsp()->unlinkFrBack();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const streamp = VN_AS(nodep->lhsp(), StreamR)->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* srcp = nodep->rhsp()->unlinkFrBack();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (sWidth > dWidth) {
|
2022-11-21 02:13:55 +01:00
|
|
|
srcp = new AstSel{streamp->fileline(), srcp, sWidth - dWidth, dWidth};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
nodep->lhsp(dstp);
|
|
|
|
|
nodep->rhsp(srcp);
|
|
|
|
|
// Cleanup
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(sizep->deleteTree(), sizep);
|
|
|
|
|
VL_DO_DANGLING(streamp->deleteTree(), streamp);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Further reduce, any of the nodes may have more reductions.
|
|
|
|
|
return true;
|
2021-05-18 20:28:48 +02:00
|
|
|
} else if (m_doV && replaceAssignMultiSel(nodep)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Boolean replacements
|
2018-02-02 03:32:58 +01:00
|
|
|
bool operandBoolShift(const AstNode* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// boolean test of AND(const,SHIFTR(x,const)) -> test of AND(SHIFTL(x,const), x)
|
2018-02-02 03:32:58 +01:00
|
|
|
if (!VN_IS(nodep, And)) return false;
|
2021-10-22 16:15:42 +02:00
|
|
|
if (!VN_IS(VN_AS(nodep, And)->lhsp(), Const)) return false;
|
|
|
|
|
if (!VN_IS(VN_AS(nodep, And)->rhsp(), ShiftR)) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstShiftR* const shiftp = VN_AS(VN_AS(nodep, And)->rhsp(), ShiftR);
|
2018-02-02 03:32:58 +01:00
|
|
|
if (!VN_IS(shiftp->rhsp(), Const)) return false;
|
2021-10-22 16:15:42 +02:00
|
|
|
if (static_cast<uint32_t>(nodep->width()) <= VN_AS(shiftp->rhsp(), Const)->toUInt()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
return true;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
void replaceBoolShift(AstNode* nodep) {
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) nodep->dumpTree("- bshft_old: ");
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const andConstp = VN_AS(VN_AS(nodep, And)->lhsp(), Const);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const fromp
|
|
|
|
|
= VN_AS(VN_AS(nodep, And)->rhsp(), ShiftR)->lhsp()->unlinkFrBack();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const shiftConstp
|
|
|
|
|
= VN_AS(VN_AS(VN_AS(nodep, And)->rhsp(), ShiftR)->rhsp(), Const);
|
2022-11-21 02:13:55 +01:00
|
|
|
V3Number val{andConstp, andConstp->width()};
|
2019-05-10 02:03:19 +02:00
|
|
|
val.opShiftL(andConstp->num(), shiftConstp->num());
|
2021-11-26 23:55:36 +01:00
|
|
|
AstAnd* const newp
|
2022-11-21 02:13:55 +01:00
|
|
|
= new AstAnd{nodep->fileline(), new AstConst{nodep->fileline(), val}, fromp};
|
2018-03-10 22:32:04 +01:00
|
|
|
// widthMin no longer applicable if different C-expanded width
|
2020-04-20 03:19:09 +02:00
|
|
|
newp->dtypeSetLogicSized(nodep->width(), VSigning::UNSIGNED);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->replaceWith(newp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) newp->dumpTree("- _new: ");
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2009-07-17 20:13:11 +02:00
|
|
|
void replaceWithSimulation(AstNode* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
SimulateVisitor simvis;
|
|
|
|
|
// Run it - may be unoptimizable due to large for loop, etc
|
|
|
|
|
simvis.mainParamEmulate(nodep);
|
|
|
|
|
if (!simvis.optimizable()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNode* errorp = simvis.whyNotNodep();
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!errorp) errorp = nodep;
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->v3error("Expecting expression to be constant, but can't determine constant for "
|
2020-11-19 03:03:23 +01:00
|
|
|
<< nodep->prettyTypeName() << '\n'
|
2020-04-15 13:58:34 +02:00
|
|
|
<< errorp->warnOther() << "... Location of non-constant "
|
|
|
|
|
<< errorp->prettyTypeName() << ": " << simvis.whyNotMessage());
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(replaceZero(nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
// Fetch the result
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const valuep = simvis.fetchValueNull(nodep); // valuep is owned by Simulate
|
2019-11-10 00:31:24 +01:00
|
|
|
UASSERT_OBJ(valuep, nodep, "No value returned from simulation");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Replace it
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const newp = valuep->cloneTree(false);
|
2019-11-10 00:31:24 +01:00
|
|
|
newp->dtypeFrom(nodep);
|
|
|
|
|
newp->fileline(nodep->fileline());
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, "Simulate->" << newp << endl);
|
|
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-07-17 20:13:11 +02:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//----------------------------------------
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNetlist* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Iterate modules backwards, in bottom-up order. That's faster
|
2023-03-18 00:58:53 +01:00
|
|
|
iterateChildrenBackwardsConst(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_modp);
|
2020-01-20 19:27:27 +01:00
|
|
|
{
|
|
|
|
|
m_modp = nodep;
|
2021-08-11 15:30:00 +02:00
|
|
|
m_concswapNames.reset();
|
2020-01-20 19:27:27 +01:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCFunc* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// No ASSIGNW removals under funcs, we've long eliminated INITIALs
|
|
|
|
|
// (We should perhaps rename the assignw's to just assigns)
|
2020-10-31 13:59:35 +01:00
|
|
|
VL_RESTORER(m_wremove);
|
|
|
|
|
{
|
|
|
|
|
m_wremove = false;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstScope* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// No ASSIGNW removals under scope, we've long eliminated INITIALs
|
2020-10-31 13:59:35 +01:00
|
|
|
VL_RESTORER(m_wremove);
|
|
|
|
|
VL_RESTORER(m_scopep);
|
|
|
|
|
{
|
|
|
|
|
m_wremove = false;
|
|
|
|
|
m_scopep = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void swapSides(AstNodeBiCom* nodep) {
|
2019-09-12 13:22:22 +02:00
|
|
|
// COMMUTATIVE({a},CONST) -> COMMUTATIVE(CONST,{a})
|
2019-05-19 22:13:13 +02:00
|
|
|
// This simplifies later optimizations
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBackWithNext();
|
|
|
|
|
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBackWithNext();
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->lhsp(rhsp);
|
|
|
|
|
nodep->rhsp(lhsp);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep); // Again?
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2021-11-28 20:17:28 +01:00
|
|
|
bool matchConcatRand(AstConcat* nodep) {
|
|
|
|
|
// CONCAT(RAND, RAND) - created by Chisel code
|
|
|
|
|
AstRand* const aRandp = VN_CAST(nodep->lhsp(), Rand);
|
|
|
|
|
AstRand* const bRandp = VN_CAST(nodep->rhsp(), Rand);
|
|
|
|
|
if (!aRandp || !bRandp) return false;
|
|
|
|
|
if (!aRandp->combinable(bRandp)) return false;
|
|
|
|
|
UINFO(4, "Concat(Rand,Rand) => Rand: " << nodep << endl);
|
|
|
|
|
aRandp->dtypeFrom(nodep); // I.e. the total width
|
|
|
|
|
nodep->replaceWith(aRandp->unlinkFrBack());
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
bool matchSelRand(AstSel* nodep) {
|
|
|
|
|
// SEL(RAND) - created by Chisel code
|
|
|
|
|
AstRand* const aRandp = VN_CAST(nodep->fromp(), Rand);
|
|
|
|
|
if (!aRandp) return false;
|
|
|
|
|
if (aRandp->seedp()) return false;
|
|
|
|
|
UINFO(4, "Sel(Rand) => Rand: " << nodep << endl);
|
|
|
|
|
aRandp->dtypeFrom(nodep); // I.e. the total width
|
|
|
|
|
nodep->replaceWith(aRandp->unlinkFrBack());
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
int operandConcatMove(AstConcat* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// CONCAT under concat (See moveConcat)
|
|
|
|
|
// Return value: true indicates to do it; 2 means move to LHS
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstConcat* const abConcp = VN_CAST(nodep->lhsp(), Concat);
|
|
|
|
|
const AstConcat* const bcConcp = VN_CAST(nodep->rhsp(), Concat);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!abConcp && !bcConcp) return 0;
|
|
|
|
|
if (bcConcp) {
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const ap = nodep->lhsp();
|
|
|
|
|
AstNodeExpr* const bp = bcConcp->lhsp();
|
2019-05-19 22:13:13 +02:00
|
|
|
// If a+b == 32,64,96 etc, then we want to have a+b together on LHS
|
2020-04-15 13:58:34 +02:00
|
|
|
if (VL_BITBIT_I(ap->width() + bp->width()) == 0) return 2; // Transform 2: to abConc
|
|
|
|
|
} else { // abConcp
|
2019-05-19 22:13:13 +02:00
|
|
|
// Unless lhs is already 32 bits due to above, reorder it
|
2020-04-15 13:58:34 +02:00
|
|
|
if (VL_BITBIT_I(nodep->lhsp()->width()) != 0) return 1; // Transform 1: to bcConc
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
return 0; // ok
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
void moveConcat(AstConcat* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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.
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, "Move concat: " << nodep << endl);
|
|
|
|
|
if (operandConcatMove(nodep) > 1) {
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const ap = nodep->lhsp()->unlinkFrBack();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConcat* const bcConcp = VN_AS(nodep->rhsp(), Concat);
|
2020-04-15 13:58:34 +02:00
|
|
|
bcConcp->unlinkFrBack();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const bp = bcConcp->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const cp = bcConcp->rhsp()->unlinkFrBack();
|
2022-11-21 02:13:55 +01:00
|
|
|
AstConcat* const abConcp = new AstConcat{bcConcp->fileline(), ap, bp};
|
2019-05-19 22:13:13 +02:00
|
|
|
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);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(bcConcp->deleteTree(), bcConcp);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConcat* const abConcp = VN_AS(nodep->lhsp(), Concat);
|
2020-04-15 13:58:34 +02:00
|
|
|
abConcp->unlinkFrBack();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const ap = abConcp->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const bp = abConcp->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const cp = nodep->rhsp()->unlinkFrBack();
|
2022-11-21 02:13:55 +01:00
|
|
|
AstConcat* const bcConcp = new AstConcat{abConcp->fileline(), bp, cp};
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->lhsp(ap);
|
|
|
|
|
nodep->rhsp(bcConcp);
|
|
|
|
|
if (operandConcatMove(bcConcp)) moveConcat(bcConcp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(abConcp->deleteTree(), abConcp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Special cases
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstConst*) override {} // Already constant
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCell* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_params) {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(nodep->paramsp());
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstClassOrPackageRef* nodep) override { iterateChildren(nodep); }
|
|
|
|
|
void visit(AstPin* nodep) override { iterateChildren(nodep); }
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2019-06-02 01:40:06 +02:00
|
|
|
void replaceLogEq(AstLogEq* nodep) {
|
|
|
|
|
// LOGEQ(a,b) => AstLogAnd{AstLogOr{AstLogNot{a},b},AstLogOr{AstLogNot{b},a}}
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
|
2019-06-02 01:40:06 +02:00
|
|
|
// Do exactly as IEEE says, might result in extra terms, so in future may do differently
|
2022-11-21 02:13:55 +01:00
|
|
|
AstLogAnd* const newp = new AstLogAnd{
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->fileline(),
|
2022-11-21 02:13:55 +01:00
|
|
|
new AstLogOr{nodep->fileline(), new AstLogNot{nodep->fileline(), lhsp}, rhsp},
|
|
|
|
|
new AstLogOr{nodep->fileline(),
|
|
|
|
|
new AstLogNot{nodep->fileline(), rhsp->cloneTree(false)},
|
|
|
|
|
lhsp->cloneTree(false)}};
|
2019-06-02 01:40:06 +02:00
|
|
|
newp->dtypeFrom(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-06-02 01:40:06 +02:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
void replaceSelSel(AstSel* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// SEL(SEL({x},a,b),c,d) => SEL({x},a+c,d)
|
2021-11-26 23:55:36 +01:00
|
|
|
AstSel* const belowp = VN_AS(nodep->fromp(), Sel);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const fromp = belowp->fromp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const widthp = nodep->widthp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const lsb1p = nodep->lsbp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const lsb2p = belowp->lsbp()->unlinkFrBack();
|
2019-05-19 22:13:13 +02:00
|
|
|
// Eliminate lower range
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, "Elim Lower range: " << nodep << endl);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* newlsbp;
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(lsb1p, Const) && VN_IS(lsb2p, Const)) {
|
2022-11-21 02:13:55 +01:00
|
|
|
newlsbp = new AstConst{lsb1p->fileline(),
|
|
|
|
|
VN_AS(lsb1p, Const)->toUInt() + VN_AS(lsb2p, Const)->toUInt()};
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(lsb1p->deleteTree(), lsb1p);
|
|
|
|
|
VL_DO_DANGLING(lsb2p->deleteTree(), lsb2p);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2022-10-28 14:41:13 +02:00
|
|
|
// Width is important, we need the width of the fromp's expression, not the
|
|
|
|
|
// potentially smaller lsb1p's width, but don't insert a redundant AstExtend.
|
|
|
|
|
// Note that due to some sloppiness in earlier passes, lsb1p might actually be wider,
|
|
|
|
|
// so extend to the wider type.
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const widep = lsb1p->width() > lsb2p->width() ? lsb1p : lsb2p;
|
|
|
|
|
AstNodeExpr* const lhsp = widep->width() > lsb2p->width()
|
|
|
|
|
? new AstExtend{lsb2p->fileline(), lsb2p}
|
|
|
|
|
: lsb2p;
|
|
|
|
|
AstNodeExpr* const rhsp = widep->width() > lsb1p->width()
|
|
|
|
|
? new AstExtend{lsb1p->fileline(), lsb1p}
|
|
|
|
|
: lsb1p;
|
2022-10-28 14:41:13 +02:00
|
|
|
lhsp->dtypeFrom(widep);
|
|
|
|
|
rhsp->dtypeFrom(widep);
|
|
|
|
|
newlsbp = new AstAdd{lsb1p->fileline(), lhsp, rhsp};
|
|
|
|
|
newlsbp->dtypeFrom(widep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-11-21 02:13:55 +01:00
|
|
|
AstSel* const newp = new AstSel{nodep->fileline(), fromp, newlsbp, widthp};
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->replaceWith(newp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void replaceSelConcat(AstSel* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// SEL(CONCAT(a,b),c,d) => SEL(a or b, . .)
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConcat* const conp = VN_AS(nodep->fromp(), Concat);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const conLhsp = conp->lhsp();
|
|
|
|
|
AstNodeExpr* const conRhsp = conp->rhsp();
|
2018-10-15 00:39:33 +02:00
|
|
|
if (static_cast<int>(nodep->lsbConst()) >= conRhsp->width()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
conLhsp->unlinkFrBack();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstSel* const newp
|
2022-11-21 02:13:55 +01:00
|
|
|
= new AstSel{nodep->fileline(), conLhsp, nodep->lsbConst() - conRhsp->width(),
|
|
|
|
|
nodep->widthConst()};
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->replaceWith(newp);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (static_cast<int>(nodep->msbConst()) < conRhsp->width()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
conRhsp->unlinkFrBack();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstSel* const newp
|
2022-11-21 02:13:55 +01:00
|
|
|
= new AstSel{nodep->fileline(), conRhsp, nodep->lsbConst(), nodep->widthConst()};
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->replaceWith(newp);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Yuk, split between the two
|
|
|
|
|
conRhsp->unlinkFrBack();
|
|
|
|
|
conLhsp->unlinkFrBack();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConcat* const newp
|
2022-11-21 02:13:55 +01:00
|
|
|
= 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()}};
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
}
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2017-09-22 03:05:42 +02:00
|
|
|
bool operandSelReplicate(AstSel* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// SEL(REPLICATE(from,rep),lsb,width) => SEL(from,0,width) as long
|
|
|
|
|
// as SEL's width <= b's width
|
2021-11-26 23:55:36 +01:00
|
|
|
AstReplicate* const repp = VN_AS(nodep->fromp(), Replicate);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const fromp = repp->lhsp();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const lsbp = VN_CAST(nodep->lsbp(), Const);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!lsbp) return false;
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const widthp = nodep->widthp();
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!VN_IS(widthp, Const)) return false;
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(fromp->width(), nodep, "Not widthed");
|
2019-05-19 22:13:13 +02:00
|
|
|
if ((lsbp->toUInt() / fromp->width())
|
2020-04-15 13:58:34 +02:00
|
|
|
!= ((lsbp->toUInt() + nodep->width() - 1) / fromp->width())) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
|
|
|
|
fromp->unlinkFrBack();
|
|
|
|
|
widthp->unlinkFrBack();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstSel* const newp
|
2022-11-21 02:13:55 +01:00
|
|
|
= new AstSel{nodep->fileline(), fromp,
|
|
|
|
|
new AstConst{lsbp->fileline(), lsbp->toUInt() % fromp->width()}, widthp};
|
2019-05-19 22:13:13 +02:00
|
|
|
newp->dtypeFrom(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
return true;
|
2017-09-22 03:05:42 +02:00
|
|
|
}
|
|
|
|
|
bool operandRepRep(AstReplicate* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// REPLICATE(REPLICATE2(from2,cnt2),cnt1) => REPLICATE(from2,(cnt1+cnt2))
|
2021-11-26 23:55:36 +01:00
|
|
|
AstReplicate* const rep2p = VN_AS(nodep->lhsp(), Replicate);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const from2p = rep2p->lhsp();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const cnt1p = VN_CAST(nodep->rhsp(), Const);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!cnt1p) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const cnt2p = VN_CAST(rep2p->rhsp(), Const);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!cnt2p) return false;
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
|
|
|
|
from2p->unlinkFrBack();
|
|
|
|
|
cnt1p->unlinkFrBack();
|
|
|
|
|
cnt2p->unlinkFrBack();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstReplicate* const newp
|
2022-11-21 02:13:55 +01:00
|
|
|
= new AstReplicate{nodep->fileline(), from2p, cnt1p->toUInt() * cnt2p->toUInt()};
|
2019-05-19 22:13:13 +02:00
|
|
|
newp->dtypeFrom(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
return true;
|
2017-09-22 03:05:42 +02:00
|
|
|
}
|
|
|
|
|
bool operandConcatSame(AstConcat* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// CONCAT(fromp,fromp) -> REPLICATE(fromp,1+1)
|
|
|
|
|
// CONCAT(REP(fromp,cnt1),fromp) -> REPLICATE(fromp,cnt1+1)
|
|
|
|
|
// CONCAT(fromp,REP(fromp,cnt1)) -> REPLICATE(fromp,1+cnt1)
|
|
|
|
|
// CONCAT(REP(fromp,cnt1),REP(fromp,cnt2)) -> REPLICATE(fromp,cnt1+cnt2)
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* from1p = nodep->lhsp();
|
2020-04-15 13:58:34 +02:00
|
|
|
uint32_t cnt1 = 1;
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* from2p = nodep->rhsp();
|
2020-04-15 13:58:34 +02:00
|
|
|
uint32_t cnt2 = 1;
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(from1p, Replicate)) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const cnt1p = VN_CAST(VN_CAST(from1p, Replicate)->rhsp(), Const);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!cnt1p) return false;
|
2021-10-22 14:56:48 +02:00
|
|
|
from1p = VN_AS(from1p, Replicate)->lhsp();
|
2019-05-19 22:13:13 +02:00
|
|
|
cnt1 = cnt1p->toUInt();
|
|
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(from2p, Replicate)) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const cnt2p = VN_CAST(VN_CAST(from2p, Replicate)->rhsp(), Const);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!cnt2p) return false;
|
2021-10-22 14:56:48 +02:00
|
|
|
from2p = VN_AS(from2p, Replicate)->lhsp();
|
2019-05-19 22:13:13 +02:00
|
|
|
cnt2 = cnt2p->toUInt();
|
|
|
|
|
}
|
|
|
|
|
if (!operandsSame(from1p, from2p)) return false;
|
|
|
|
|
//
|
|
|
|
|
from1p->unlinkFrBack();
|
2022-11-21 02:13:55 +01:00
|
|
|
AstReplicate* const newp = new AstReplicate{nodep->fileline(), from1p, cnt1 + cnt2};
|
2019-05-19 22:13:13 +02:00
|
|
|
newp->dtypeFrom(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
return true;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2011-02-24 03:21:59 +01:00
|
|
|
void replaceSelIntoBiop(AstSel* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// SEL(BUFIF1(a,b),1,bit) => BUFIF1(SEL(a,1,bit),SEL(b,1,bit))
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeBiop* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), NodeBiop);
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(fromp, nodep, "Called on non biop");
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const lsbp = nodep->lsbp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const widthp = nodep->widthp()->unlinkFrBack();
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const bilhsp = fromp->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const birhsp = fromp->rhsp()->unlinkFrBack();
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
2020-04-15 13:58:34 +02:00
|
|
|
fromp->lhsp(
|
2022-11-21 02:13:55 +01:00
|
|
|
new AstSel{nodep->fileline(), bilhsp, lsbp->cloneTree(true), widthp->cloneTree(true)});
|
|
|
|
|
fromp->rhsp(new AstSel{nodep->fileline(), birhsp, lsbp, widthp});
|
2019-05-19 22:13:13 +02:00
|
|
|
fromp->dtypeFrom(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->replaceWith(fromp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2011-02-24 03:21:59 +01:00
|
|
|
}
|
2011-02-24 03:36:38 +01:00
|
|
|
void replaceSelIntoUniop(AstSel* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// SEL(NOT(a),1,bit) => NOT(SEL(a,bit))
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeUniop* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), NodeUniop);
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(fromp, nodep, "Called on non biop");
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const lsbp = nodep->lsbp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const widthp = nodep->widthp()->unlinkFrBack();
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const bilhsp = fromp->lhsp()->unlinkFrBack();
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
2022-11-21 02:13:55 +01:00
|
|
|
fromp->lhsp(new AstSel{nodep->fileline(), bilhsp, lsbp, widthp});
|
2019-05-19 22:13:13 +02:00
|
|
|
fromp->dtypeFrom(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->replaceWith(fromp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2011-02-24 03:36:38 +01:00
|
|
|
}
|
2011-02-24 03:21:59 +01:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAttrOf* nodep) override {
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_attrp);
|
|
|
|
|
{
|
|
|
|
|
m_attrp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2012-07-28 16:52:29 +02:00
|
|
|
}
|
2014-04-02 05:16:16 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstArraySel* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(nodep->bitp());
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(nodep->bitp(), Const)
|
|
|
|
|
&& VN_IS(nodep->fromp(), VarRef)
|
2019-05-19 22:13:13 +02:00
|
|
|
// Need to make sure it's an array object so don't mis-allow a constant (bug509.)
|
2021-10-22 14:56:48 +02:00
|
|
|
&& VN_AS(nodep->fromp(), VarRef)->varp()
|
|
|
|
|
&& VN_IS(VN_AS(nodep->fromp(), VarRef)->varp()->valuep(), InitArray)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_selp = nodep; // Ask visit(AstVarRef) to replace varref with const
|
|
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(nodep->fromp());
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(nodep->fromp(), Const)) { // It did.
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!m_selp) {
|
|
|
|
|
nodep->v3error("Illegal assignment of constant to unpacked array");
|
|
|
|
|
} else {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const fromp = nodep->fromp()->unlinkFrBack();
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->replaceWith(fromp);
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(fromp->dtypep()->skipRefp(), NodeArrayDType)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Strip off array to find what array references
|
2020-04-15 13:58:34 +02:00
|
|
|
fromp->dtypeFrom(
|
2021-10-22 14:56:48 +02:00
|
|
|
VN_AS(fromp->dtypep()->skipRefp(), NodeArrayDType)->subDTypep());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-08-15 16:12:55 +02:00
|
|
|
m_selp = nullptr;
|
2014-04-02 05:16:16 +02:00
|
|
|
}
|
2022-10-12 11:19:21 +02:00
|
|
|
void visit(AstCAwait* nodep) override {
|
|
|
|
|
m_hasJumpDelay = true;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeVarRef* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(nodep->varp(), nodep, "Not linked");
|
2019-05-19 22:13:13 +02:00
|
|
|
bool did = false;
|
|
|
|
|
if (m_doV && nodep->varp()->valuep() && !m_attrp) {
|
2022-11-27 14:31:22 +01:00
|
|
|
// if (debug()) valuep->dumpTree("- visitvaref: ");
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(nodep->varp()->valuep()); // May change nodep->varp()->valuep()
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const valuep = nodep->varp()->valuep();
|
2020-10-31 03:28:51 +01:00
|
|
|
if (nodep->access().isReadOnly()
|
2019-05-19 22:13:13 +02:00
|
|
|
&& ((!m_params // Can reduce constant wires into equations
|
|
|
|
|
&& m_doNConst
|
2022-06-04 02:43:16 +02:00
|
|
|
&& v3Global.opt.fConst()
|
2018-10-27 23:29:00 +02:00
|
|
|
// Default value, not a "known" constant for this usage
|
2022-10-20 12:31:00 +02:00
|
|
|
&& !nodep->varp()->isClassMember() && !nodep->varp()->isUsedVirtIface()
|
2020-04-15 13:58:34 +02:00
|
|
|
&& !(nodep->varp()->isFuncLocal() && nodep->varp()->isNonOutput())
|
|
|
|
|
&& !nodep->varp()->noSubst() && !nodep->varp()->isSigPublic())
|
2019-05-19 22:13:13 +02:00
|
|
|
|| nodep->varp()->isParam())) {
|
|
|
|
|
if (operandConst(valuep)) {
|
2021-10-22 14:56:48 +02:00
|
|
|
const V3Number& num = VN_AS(valuep, Const)->num();
|
2020-04-15 13:58:34 +02:00
|
|
|
// UINFO(2,"constVisit "<<cvtToHex(valuep)<<" "<<num<<endl);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
did = true;
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (m_selp && VN_IS(valuep, InitArray)) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstInitArray* const initarp = VN_AS(valuep, InitArray);
|
|
|
|
|
const uint32_t bit = m_selp->bitConst();
|
|
|
|
|
AstNode* const itemp = initarp->getIndexDefaultedValuep(bit);
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(itemp, Const)) {
|
2021-10-22 14:56:48 +02:00
|
|
|
const V3Number& num = VN_AS(itemp, Const)->num();
|
2020-04-15 13:58:34 +02:00
|
|
|
// UINFO(2,"constVisit "<<cvtToHex(valuep)<<" "<<num<<endl);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
did = true;
|
|
|
|
|
}
|
2021-03-19 23:44:26 +01:00
|
|
|
} else if (m_params && VN_IS(valuep, InitArray)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Allow parameters to pass arrays
|
|
|
|
|
// Earlier recursion of InitArray made sure each array value is constant
|
|
|
|
|
// This exception is fairly fragile, i.e. doesn't
|
|
|
|
|
// support arrays of arrays or other stuff
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const newp = valuep->cloneTree(false);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
did = true;
|
2020-05-08 03:09:14 +02:00
|
|
|
} else if (nodep->varp()->isParam() && VN_IS(valuep, Unbounded)) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const newp = valuep->cloneTree(false);
|
2020-05-08 03:09:14 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
did = true;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!did && m_required) {
|
|
|
|
|
nodep->v3error("Expecting expression to be constant, but variable isn't const: "
|
2020-04-15 13:58:34 +02:00
|
|
|
<< nodep->varp()->prettyNameQ());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstEnumItemRef* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(nodep->itemp(), nodep, "Not linked");
|
2019-05-19 22:13:13 +02:00
|
|
|
bool did = false;
|
|
|
|
|
if (nodep->itemp()->valuep()) {
|
2022-11-27 14:31:22 +01:00
|
|
|
// if (debug()) nodep->itemp()->valuep()->dumpTree("- visitvaref: ");
|
2019-11-05 00:48:47 +01:00
|
|
|
if (nodep->itemp()->user4()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->v3error("Recursive enum value: " << nodep->itemp()->prettyNameQ());
|
2019-11-05 00:48:47 +01:00
|
|
|
} else {
|
|
|
|
|
nodep->itemp()->user4(true);
|
|
|
|
|
iterateAndNextNull(nodep->itemp()->valuep());
|
|
|
|
|
nodep->itemp()->user4(false);
|
|
|
|
|
}
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstConst* const valuep = VN_CAST(nodep->itemp()->valuep(), Const)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
const V3Number& num = valuep->num();
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
did = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!did && m_required) {
|
2021-03-19 23:44:26 +01:00
|
|
|
nodep->v3error("Expecting expression to be constant, but enum value isn't const: "
|
2020-04-15 13:58:34 +02:00
|
|
|
<< nodep->itemp()->prettyNameQ());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-12-27 14:29:55 +01:00
|
|
|
}
|
2010-01-17 21:10:37 +01:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
// void visit(AstCvtPackString* nodep) override {
|
2022-10-12 11:19:21 +02:00
|
|
|
// Not constant propagated (for today) because AstNodeExpr::isOpaque is set
|
2010-01-17 21:10:37 +01:00
|
|
|
// Someday if lower is constant, convert to quoted "string".
|
|
|
|
|
|
2020-07-05 19:13:03 +02:00
|
|
|
bool onlySenItemInSenTree(AstSenItem* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Only one if it's not in a list
|
|
|
|
|
return (!nodep->nextp() && nodep->backp()->nextp() != nodep);
|
2009-01-07 15:37:59 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSenItem* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_doNConst
|
2020-04-15 13:58:34 +02:00
|
|
|
&& (VN_IS(nodep->sensp(), Const) || VN_IS(nodep->sensp(), EnumItemRef)
|
2019-05-19 22:13:13 +02:00
|
|
|
|| (nodep->varrefp() && nodep->varrefp()->varp()->isParam()))) {
|
|
|
|
|
// Constants in sensitivity lists may be removed (we'll simplify later)
|
2019-09-09 13:50:21 +02:00
|
|
|
if (nodep->isClocked()) { // A constant can never get a pos/negedge
|
2019-05-19 22:13:13 +02:00
|
|
|
if (onlySenItemInSenTree(nodep)) {
|
2022-05-15 17:03:32 +02:00
|
|
|
if (nodep->edgeType() == VEdgeType::ET_CHANGED) {
|
2022-12-23 17:32:38 +01:00
|
|
|
// TODO: This really is dodgy, as strictly compliant simulators will not
|
2022-05-15 17:03:32 +02:00
|
|
|
// execute this block, but but t_func_check relies on it
|
|
|
|
|
nodep->replaceWith(
|
2022-09-17 14:56:41 +02:00
|
|
|
new AstSenItem{nodep->fileline(), AstSenItem::Initial{}});
|
2022-05-15 17:03:32 +02:00
|
|
|
} else {
|
2022-09-17 14:56:41 +02:00
|
|
|
nodep->replaceWith(new AstSenItem{nodep->fileline(), AstSenItem::Never{}});
|
2022-05-15 17:03:32 +02:00
|
|
|
}
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
} else { // Otherwise it may compute a result that needs to settle out
|
2022-11-21 02:13:55 +01:00
|
|
|
nodep->replaceWith(new AstSenItem{nodep->fileline(), AstSenItem::Combo{}});
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
} else if (m_doNConst && VN_IS(nodep->sensp(), Not)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// V3Gate may propagate NOTs into clocks... Just deal with it
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const sensp = nodep->sensp();
|
2019-05-19 22:13:13 +02:00
|
|
|
AstNode* lastSensp = sensp;
|
|
|
|
|
bool invert = false;
|
2018-02-02 03:32:58 +01:00
|
|
|
while (VN_IS(lastSensp, Not)) {
|
2021-10-22 14:56:48 +02:00
|
|
|
lastSensp = VN_AS(lastSensp, Not)->lhsp();
|
2019-05-19 22:13:13 +02:00
|
|
|
invert = !invert;
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(8, "senItem(NOT...) " << nodep << " " << invert << endl);
|
|
|
|
|
if (invert) nodep->edgeType(nodep->edgeType().invert());
|
2022-08-19 20:18:38 +02:00
|
|
|
sensp->replaceWith(lastSensp->unlinkFrBack());
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(sensp->deleteTree(), sensp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2009-01-07 15:37:59 +01:00
|
|
|
|
2023-04-13 14:44:54 +02:00
|
|
|
class SenItemCmp final {
|
|
|
|
|
static int cmp(const AstNodeExpr* ap, const AstNodeExpr* bp) {
|
|
|
|
|
const VNType aType = ap->type();
|
|
|
|
|
const VNType bType = bp->type();
|
|
|
|
|
if (aType != bType) return static_cast<int>(bType) - static_cast<int>(aType);
|
|
|
|
|
|
|
|
|
|
if (const AstVarRef* const aRefp = VN_CAST(ap, VarRef)) {
|
|
|
|
|
const AstVarRef* const bRefp = VN_AS(bp, VarRef);
|
|
|
|
|
// Looks visually better if we keep sorted by name
|
|
|
|
|
if (aRefp->name() < bRefp->name()) return -1;
|
|
|
|
|
if (aRefp->name() > bRefp->name()) return 1;
|
2020-07-05 19:13:03 +02:00
|
|
|
// But might be same name with different scopes
|
2023-04-13 14:44:54 +02:00
|
|
|
if (aRefp->varScopep() < bRefp->varScopep()) return -1;
|
|
|
|
|
if (aRefp->varScopep() > bRefp->varScopep()) return 1;
|
2020-07-05 19:13:03 +02:00
|
|
|
// Or rarely, different data types
|
2023-04-13 14:44:54 +02:00
|
|
|
if (aRefp->dtypep() < bRefp->dtypep()) return -1;
|
|
|
|
|
if (aRefp->dtypep() > bRefp->dtypep()) return 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (const AstConst* const aConstp = VN_CAST(ap, Const)) {
|
|
|
|
|
const AstConst* const bConstp = VN_AS(bp, Const);
|
|
|
|
|
if (aConstp->toUQuad() < bConstp->toUQuad()) return -1;
|
|
|
|
|
if (aConstp->toUQuad() > bConstp->toUQuad()) return 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (const AstNodeBiop* const aBiOpp = VN_CAST(ap, NodeBiop)) {
|
|
|
|
|
const AstNodeBiop* const bBiOpp = VN_AS(bp, NodeBiop);
|
|
|
|
|
// Compare RHSs first as LHS might be const, but the variable term should become
|
|
|
|
|
// adjacent for optimization if identical.
|
|
|
|
|
if (const int c = cmp(aBiOpp->rhsp(), bBiOpp->rhsp())) return c;
|
|
|
|
|
return cmp(aBiOpp->lhsp(), bBiOpp->lhsp());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (const AstCMethodHard* const aCallp = VN_CAST(ap, CMethodHard)) {
|
|
|
|
|
const AstCMethodHard* const bCallp = VN_AS(bp, CMethodHard);
|
|
|
|
|
if (aCallp->name() < bCallp->name()) return -1;
|
|
|
|
|
if (aCallp->name() > bCallp->name()) return 1;
|
|
|
|
|
if (const int c = cmp(aCallp->fromp(), bCallp->fromp())) return c;
|
|
|
|
|
AstNodeExpr* aPinsp = aCallp->pinsp();
|
|
|
|
|
AstNodeExpr* bPinsp = bCallp->pinsp();
|
|
|
|
|
while (aPinsp && bPinsp) {
|
|
|
|
|
if (const int c = cmp(aPinsp, bPinsp)) return c;
|
|
|
|
|
aPinsp = VN_AS(aPinsp->nextp(), NodeExpr);
|
|
|
|
|
bPinsp = VN_AS(bPinsp->nextp(), NodeExpr);
|
2022-05-15 17:03:32 +02:00
|
|
|
}
|
2023-04-13 14:44:54 +02:00
|
|
|
return aPinsp ? -1 : bPinsp ? 1 : 0;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2023-04-13 14:44:54 +02:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
bool operator()(const AstSenItem* lhsp, const AstSenItem* rhsp) const {
|
|
|
|
|
AstNodeExpr* const lSensp = lhsp->sensp();
|
|
|
|
|
AstNodeExpr* const rSensp = rhsp->sensp();
|
|
|
|
|
if (lSensp && rSensp) {
|
|
|
|
|
// If both terms have sensitivity expressions, recursively compare them
|
|
|
|
|
if (const int c = cmp(lSensp, rSensp)) return c < 0;
|
|
|
|
|
} else if (lSensp || rSensp) {
|
|
|
|
|
// Terms with sensitivity expressions come after those without
|
|
|
|
|
return rSensp;
|
|
|
|
|
}
|
|
|
|
|
// Finally sort by edge, AFTER variable, as we want multiple edges for same var
|
|
|
|
|
// adjacent. note the SenTree optimizer requires this order (more general first,
|
|
|
|
|
// less general last)
|
|
|
|
|
return lhsp->edgeType() < rhsp->edgeType();
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-01-07 15:37:59 +01:00
|
|
|
};
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSenTree* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_doExpensive) {
|
2022-11-27 14:31:22 +01:00
|
|
|
// cout<<endl; nodep->dumpTree("- ssin: ");
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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
|
2020-01-25 02:10:44 +01:00
|
|
|
// ORing between them; we just need to ensure that the result is at
|
2022-03-31 02:17:59 +02:00
|
|
|
// least as frequently activating. So we
|
2019-05-19 22:13:13 +02:00
|
|
|
// SENGATE(SENITEM(x)) -> SENITEM(x), then let it collapse with the
|
|
|
|
|
// other SENITEM(x).
|
2022-11-21 12:27:55 +01:00
|
|
|
|
|
|
|
|
// Mark x in SENITEM(x)
|
|
|
|
|
for (AstSenItem* senp = nodep->sensesp(); senp; senp = VN_AS(senp->nextp(), SenItem)) {
|
|
|
|
|
if (senp->varrefp() && senp->varrefp()->varScopep()) {
|
|
|
|
|
senp->varrefp()->varScopep()->user4(1);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-13 14:44:54 +02:00
|
|
|
// Pass 1: Sort the sensitivity items so "posedge a or b" and "posedge b or a" and
|
|
|
|
|
// similar, optimizable expressions end up next to each other.
|
2021-10-22 18:36:58 +02:00
|
|
|
for (AstSenItem *nextp, *senp = nodep->sensesp(); senp; senp = nextp) {
|
2021-10-22 14:56:48 +02:00
|
|
|
nextp = VN_AS(senp->nextp(), SenItem);
|
2019-05-19 22:13:13 +02:00
|
|
|
// cppcheck-suppress unassignedVariable // cppcheck bug
|
2021-11-26 23:55:36 +01:00
|
|
|
const SenItemCmp cmp;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nextp && !cmp(senp, nextp)) {
|
|
|
|
|
// Something's out of order, sort it
|
2020-08-15 16:12:55 +02:00
|
|
|
senp = nullptr;
|
2020-07-05 19:13:03 +02:00
|
|
|
std::vector<AstSenItem*> vec;
|
2021-10-22 18:36:58 +02:00
|
|
|
for (AstSenItem* senp = nodep->sensesp(); senp;
|
2021-10-22 14:56:48 +02:00
|
|
|
senp = VN_AS(senp->nextp(), SenItem)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
vec.push_back(senp);
|
|
|
|
|
}
|
|
|
|
|
stable_sort(vec.begin(), vec.end(), SenItemCmp());
|
2020-08-16 17:43:49 +02:00
|
|
|
for (const auto& ip : vec) ip->unlinkFrBack();
|
|
|
|
|
for (const auto& ip : vec) nodep->addSensesp(ip);
|
2019-05-19 22:13:13 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-13 14:44:54 +02:00
|
|
|
// Pass 2, remove duplicates and simplify adjacent terms if possible
|
|
|
|
|
for (AstSenItem *senp = nodep->sensesp(), *nextp; senp; senp = nextp) {
|
2021-10-22 14:56:48 +02:00
|
|
|
nextp = VN_AS(senp->nextp(), SenItem);
|
2023-04-13 14:44:54 +02:00
|
|
|
if (!nextp) break;
|
|
|
|
|
AstSenItem* const lItemp = senp;
|
|
|
|
|
AstSenItem* const rItemp = nextp;
|
|
|
|
|
AstNodeExpr* const lSenp = lItemp->sensp();
|
|
|
|
|
AstNodeExpr* const rSenp = rItemp->sensp();
|
|
|
|
|
if (!lSenp || !rSenp) continue;
|
|
|
|
|
|
|
|
|
|
if (lSenp->sameGateTree(rSenp)) {
|
|
|
|
|
// POSEDGE or NEGEDGE -> BOTHEDGE. (We've sorted POSEDGE, before NEGEDGE, so we
|
|
|
|
|
// do not need to test for the opposite orders.)
|
|
|
|
|
if (lItemp->edgeType() == VEdgeType::ET_POSEDGE
|
|
|
|
|
&& rItemp->edgeType() == VEdgeType::ET_NEGEDGE) {
|
|
|
|
|
// Make both terms BOTHEDGE, the second will be removed below
|
|
|
|
|
lItemp->edgeType(VEdgeType::ET_BOTHEDGE);
|
|
|
|
|
rItemp->edgeType(VEdgeType::ET_BOTHEDGE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove identical expressions
|
|
|
|
|
if (lItemp->edgeType() == rItemp->edgeType()) {
|
|
|
|
|
VL_DO_DANGLING(rItemp->unlinkFrBack()->deleteTree(), rItemp);
|
|
|
|
|
nextp = lItemp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Not identical terms, check if they can be combined
|
|
|
|
|
if (lSenp->width() != rSenp->width()) continue;
|
|
|
|
|
if (AstAnd* const lAndp = VN_CAST(lSenp, And)) {
|
|
|
|
|
if (AstAnd* const rAndp = VN_CAST(rSenp, And)) {
|
|
|
|
|
if (AstConst* const lConstp = VN_CAST(lAndp->lhsp(), Const)) {
|
|
|
|
|
if (AstConst* const rConstp = VN_CAST(rAndp->lhsp(), Const)) {
|
|
|
|
|
if (lAndp->rhsp()->sameTree(rAndp->rhsp())) {
|
|
|
|
|
const V3Number lNum{lConstp->num()};
|
|
|
|
|
lConstp->num().opOr(lNum, rConstp->num());
|
|
|
|
|
// Remove redundant term
|
|
|
|
|
VL_DO_DANGLING(rItemp->unlinkFrBack()->deleteTree(), rItemp);
|
|
|
|
|
nextp = lItemp;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-01-07 15:37:59 +01:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
//-----
|
|
|
|
|
// Zero elimination
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeAssign* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
if (nodep->timingControlp()) m_hasJumpDelay = true;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_doNConst && replaceNodeAssign(nodep)) return;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAssignAlias* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Don't perform any optimizations, keep the alias around
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAssignVarScope* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Don't perform any optimizations, the node won't be linked yet
|
2013-05-28 03:39:19 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAssignW* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_doNConst && replaceNodeAssign(nodep)) return;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeVarRef* const varrefp = VN_CAST(
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->lhsp(),
|
|
|
|
|
VarRef); // Not VarXRef, as different refs may set different values to each hierarchy
|
|
|
|
|
if (m_wremove && !m_params && m_doNConst && m_modp && operandConst(nodep->rhsp())
|
2021-10-22 14:56:48 +02:00
|
|
|
&& !VN_AS(nodep->rhsp(), Const)->num().isFourState()
|
2019-05-19 22:13:13 +02:00
|
|
|
&& varrefp // Don't do messes with BITREFs/ARRAYREFs
|
2022-09-14 13:39:27 +02:00
|
|
|
&& !varrefp->varp()->hasStrengthAssignment() // Strengths are resolved in V3Tristate
|
2019-05-19 22:13:13 +02:00
|
|
|
&& !varrefp->varp()->valuep() // Not already constified
|
2020-04-15 13:58:34 +02:00
|
|
|
&& !varrefp->varScopep()) { // Not scoped (or each scope may have different initial
|
|
|
|
|
// value)
|
2019-05-19 22:13:13 +02:00
|
|
|
// ASSIGNW (VARREF, const) -> INITIAL ( ASSIGN (VARREF, const) )
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, "constAssignW " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Make a initial assignment
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const exprp = nodep->rhsp()->unlinkFrBack();
|
2019-05-19 22:13:13 +02:00
|
|
|
varrefp->unlinkFrBack();
|
2022-11-21 02:13:55 +01:00
|
|
|
AstInitial* const newinitp = new AstInitial{
|
|
|
|
|
nodep->fileline(), new AstAssign{nodep->fileline(), varrefp, exprp}};
|
2022-09-15 20:43:56 +02:00
|
|
|
m_modp->addStmtsp(newinitp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Set the initial value right in the variable so we can constant propagate
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const initvaluep = exprp->cloneTree(false);
|
2019-05-19 22:13:13 +02:00
|
|
|
varrefp->varp()->valuep(initvaluep);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstRelease* nodep) override {
|
2021-12-17 18:56:33 +01:00
|
|
|
if (AstConcat* const concatp = VN_CAST(nodep->lhsp(), Concat)) {
|
|
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
AstRelease* const newLp = new AstRelease{flp, concatp->lhsp()->unlinkFrBack()};
|
|
|
|
|
AstRelease* const newRp = new AstRelease{flp, concatp->rhsp()->unlinkFrBack()};
|
|
|
|
|
nodep->replaceWith(newLp);
|
|
|
|
|
newLp->addNextHere(newRp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
visit(newLp);
|
|
|
|
|
visit(newRp);
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeIf* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_doNConst) {
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstConst* const constp = VN_CAST(nodep->condp(), Const)) {
|
2020-08-15 16:12:55 +02:00
|
|
|
AstNode* keepp = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (constp->isZero()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, "IF(0,{any},{x}) => {x}: " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
keepp = nodep->elsesp();
|
2020-06-20 05:16:07 +02:00
|
|
|
} else if (!m_doV || constp->isNeqZero()) { // Might be X in Verilog
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, "IF(!0,{x},{any}) => {x}: " << nodep << endl);
|
2022-09-15 20:43:56 +02:00
|
|
|
keepp = nodep->thensp();
|
2020-06-20 05:16:07 +02:00
|
|
|
} else {
|
|
|
|
|
UINFO(4, "IF condition is X, retaining: " << nodep << endl);
|
|
|
|
|
return;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
if (keepp) {
|
|
|
|
|
keepp->unlinkFrBackWithNext();
|
|
|
|
|
nodep->replaceWith(keepp);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
}
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2022-09-15 20:43:56 +02:00
|
|
|
} else if (!afterComment(nodep->thensp()) && !afterComment(nodep->elsesp())) {
|
2023-02-28 21:21:58 +01:00
|
|
|
if (!nodep->condp()->isTreePureRecurse()) {
|
2021-09-13 21:52:53 +02:00
|
|
|
// Condition has side effect - leave - perhaps in
|
|
|
|
|
// future simplify to remove all but side effect terms
|
|
|
|
|
} else {
|
|
|
|
|
// Empty block, remove it
|
|
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
|
|
|
|
}
|
2022-09-15 20:43:56 +02:00
|
|
|
} else if (!afterComment(nodep->thensp())) {
|
2020-08-15 16:12:55 +02:00
|
|
|
UINFO(4, "IF({x}) nullptr {...} => IF(NOT{x}}: " << nodep << endl);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const condp = nodep->condp();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const elsesp = nodep->elsesp();
|
2019-05-19 22:13:13 +02:00
|
|
|
condp->unlinkFrBackWithNext();
|
|
|
|
|
elsesp->unlinkFrBackWithNext();
|
2022-09-15 20:43:56 +02:00
|
|
|
if (nodep->thensp()) { // Must have been comment
|
|
|
|
|
nodep->thensp()->unlinkFrBackWithNext()->deleteTree();
|
2020-04-15 13:58:34 +02:00
|
|
|
}
|
2022-11-21 02:13:55 +01:00
|
|
|
nodep->condp(new AstLogNot{condp->fileline(),
|
|
|
|
|
condp}); // LogNot, as C++ optimization also possible
|
2022-09-15 20:43:56 +02:00
|
|
|
nodep->addThensp(elsesp);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (((VN_IS(nodep->condp(), Not) && nodep->condp()->width() == 1)
|
|
|
|
|
|| VN_IS(nodep->condp(), LogNot))
|
2022-09-15 20:43:56 +02:00
|
|
|
&& nodep->thensp() && nodep->elsesp()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, "IF(NOT {x}) => IF(x) swapped if/else" << nodep << endl);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const condp
|
2021-11-26 23:55:36 +01:00
|
|
|
= VN_AS(nodep->condp(), NodeUniop)->lhsp()->unlinkFrBackWithNext();
|
2022-09-15 20:43:56 +02:00
|
|
|
AstNode* const thensp = nodep->thensp()->unlinkFrBackWithNext();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const elsesp = nodep->elsesp()->unlinkFrBackWithNext();
|
2022-11-21 02:13:55 +01:00
|
|
|
AstIf* const ifp = new AstIf{nodep->fileline(), condp, elsesp, thensp};
|
2023-03-21 09:43:31 +01:00
|
|
|
ifp->isBoundsCheck(nodep->isBoundsCheck()); // Copy bounds check info
|
2019-05-19 22:13:13 +02:00
|
|
|
ifp->branchPred(nodep->branchPred().invert());
|
|
|
|
|
nodep->replaceWith(ifp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (ifSameAssign(nodep)) {
|
2020-11-19 03:03:23 +01:00
|
|
|
UINFO(
|
|
|
|
|
4,
|
|
|
|
|
"IF({a}) ASSIGN({b},{c}) else ASSIGN({b},{d}) => ASSIGN({b}, {a}?{c}:{d})\n");
|
2022-09-15 20:43:56 +02:00
|
|
|
AstNodeAssign* const thensp = VN_AS(nodep->thensp(), NodeAssign);
|
|
|
|
|
AstNodeAssign* const elsesp = VN_AS(nodep->elsesp(), NodeAssign);
|
|
|
|
|
thensp->unlinkFrBack();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const condp = nodep->condp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const truep = thensp->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const falsep = elsesp->rhsp()->unlinkFrBack();
|
2022-11-21 02:13:55 +01:00
|
|
|
thensp->rhsp(new AstCond{truep->fileline(), condp, truep, falsep});
|
2022-09-15 20:43:56 +02:00
|
|
|
nodep->replaceWith(thensp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2020-11-11 03:40:14 +01:00
|
|
|
} else if (false // Disabled, as vpm assertions are faster
|
|
|
|
|
// without due to short-circuiting
|
2020-04-15 13:58:34 +02:00
|
|
|
&& operandIfIf(nodep)) {
|
|
|
|
|
UINFO(9, "IF({a}) IF({b}) => IF({a} && {b})" << endl);
|
2022-09-15 20:43:56 +02:00
|
|
|
AstNodeIf* const lowerIfp = VN_AS(nodep->thensp(), NodeIf);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const condp = nodep->condp()->unlinkFrBack();
|
2022-09-15 20:43:56 +02:00
|
|
|
AstNode* const lowerThensp = lowerIfp->thensp()->unlinkFrBackWithNext();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const lowerCondp = lowerIfp->condp()->unlinkFrBackWithNext();
|
2022-11-21 02:13:55 +01:00
|
|
|
nodep->condp(new AstLogAnd{lowerIfp->fileline(), condp, lowerCondp});
|
2022-09-15 20:43:56 +02:00
|
|
|
lowerIfp->replaceWith(lowerThensp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(lowerIfp->deleteTree(), lowerIfp);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (operandBoolShift(nodep->condp())) {
|
2019-05-19 22:13:13 +02:00
|
|
|
replaceBoolShift(nodep->condp());
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstDisplay* nodep) override {
|
2017-10-03 02:25:10 +02:00
|
|
|
// DISPLAY(SFORMAT(text1)),DISPLAY(SFORMAT(text2)) -> DISPLAY(SFORMAT(text1+text2))
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2017-10-03 02:25:10 +02:00
|
|
|
if (stmtDisplayDisplay(nodep)) return;
|
|
|
|
|
}
|
|
|
|
|
bool stmtDisplayDisplay(AstDisplay* nodep) {
|
|
|
|
|
// DISPLAY(SFORMAT(text1)),DISPLAY(SFORMAT(text2)) -> DISPLAY(SFORMAT(text1+text2))
|
|
|
|
|
if (!m_modp) return false; // Don't optimize under single statement
|
2021-11-26 23:55:36 +01:00
|
|
|
AstDisplay* const prevp = VN_CAST(nodep->backp(), Display);
|
2017-10-03 02:25:10 +02:00
|
|
|
if (!prevp) return false;
|
|
|
|
|
if (!((prevp->displayType() == nodep->displayType())
|
2022-01-02 19:56:40 +01:00
|
|
|
|| (prevp->displayType() == VDisplayType::DT_WRITE
|
|
|
|
|
&& nodep->displayType() == VDisplayType::DT_DISPLAY)
|
|
|
|
|
|| (prevp->displayType() == VDisplayType::DT_DISPLAY
|
|
|
|
|
&& nodep->displayType() == VDisplayType::DT_WRITE)))
|
2017-10-03 02:25:10 +02:00
|
|
|
return false;
|
2020-04-15 13:58:34 +02:00
|
|
|
if ((prevp->filep() && !nodep->filep()) || (!prevp->filep() && nodep->filep())
|
2023-05-03 23:04:18 +02:00
|
|
|
|| (prevp->filep() && nodep->filep() && !prevp->filep()->sameTree(nodep->filep())))
|
2020-04-15 13:58:34 +02:00
|
|
|
return false;
|
|
|
|
|
if (!prevp->fmtp() || prevp->fmtp()->nextp() || !nodep->fmtp() || nodep->fmtp()->nextp())
|
|
|
|
|
return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstSFormatF* const pformatp = prevp->fmtp();
|
2020-12-19 17:12:46 +01:00
|
|
|
if (!pformatp) return false;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstSFormatF* const nformatp = nodep->fmtp();
|
2020-12-19 17:12:46 +01:00
|
|
|
if (!nformatp) return false;
|
|
|
|
|
// We don't merge scopeNames as can have only one and might be different scopes (late in
|
|
|
|
|
// process) Also rare for real code to print %m multiple times in same message
|
|
|
|
|
if (nformatp->scopeNamep() && pformatp->scopeNamep()) return false;
|
|
|
|
|
// We don't early merge arguments as might need to later print warnings with
|
|
|
|
|
// right line numbers, nor scopeNames as might be different scopes (late in process)
|
|
|
|
|
if (!m_doCpp && pformatp->exprsp()) return false;
|
|
|
|
|
if (!m_doCpp && nformatp->exprsp()) return false;
|
|
|
|
|
// Avoid huge merges
|
|
|
|
|
static constexpr int DISPLAY_MAX_MERGE_LENGTH = 500;
|
|
|
|
|
if (pformatp->text().length() + nformatp->text().length() > DISPLAY_MAX_MERGE_LENGTH)
|
|
|
|
|
return false;
|
2017-10-03 02:25:10 +02:00
|
|
|
//
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "DISPLAY(SF({a})) DISPLAY(SF({b})) -> DISPLAY(SF({a}+{b}))" << endl);
|
2017-10-03 02:25:10 +02:00
|
|
|
// Convert DT_DISPLAY to DT_WRITE as may allow later optimizations
|
2022-01-02 19:56:40 +01:00
|
|
|
if (prevp->displayType() == VDisplayType::DT_DISPLAY) {
|
|
|
|
|
prevp->displayType(VDisplayType::DT_WRITE);
|
2020-04-15 13:58:34 +02:00
|
|
|
pformatp->text(pformatp->text() + "\n");
|
2017-10-03 02:25:10 +02:00
|
|
|
}
|
|
|
|
|
// We can't replace prev() as the edit tracking iterators will get confused.
|
|
|
|
|
// So instead we edit the prev note itself.
|
2020-04-15 13:58:34 +02:00
|
|
|
if (prevp->addNewline()) pformatp->text(pformatp->text() + "\n");
|
|
|
|
|
pformatp->text(pformatp->text() + nformatp->text());
|
2020-12-19 17:12:46 +01:00
|
|
|
if (!prevp->addNewline() && nodep->addNewline()) pformatp->text(pformatp->text() + "\n");
|
|
|
|
|
if (nformatp->exprsp()) pformatp->addExprsp(nformatp->exprsp()->unlinkFrBackWithNext());
|
2022-09-15 20:43:56 +02:00
|
|
|
if (AstScopeName* const scopeNamep = nformatp->scopeNamep()) {
|
|
|
|
|
scopeNamep->unlinkFrBackWithNext();
|
|
|
|
|
pformatp->scopeNamep(scopeNamep);
|
|
|
|
|
}
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2017-10-03 02:25:10 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSFormatF* nodep) override {
|
2019-05-19 22:13:13 +02: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.
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
bool anyconst = false;
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstNode* argp = nodep->exprsp(); argp; argp = argp->nextp()) {
|
|
|
|
|
if (VN_IS(argp, Const)) {
|
|
|
|
|
anyconst = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
if (m_doNConst && anyconst) {
|
2020-04-15 13:58:34 +02:00
|
|
|
// UINFO(9," Display in "<<nodep->text()<<endl);
|
2018-10-14 05:06:36 +02:00
|
|
|
string newFormat;
|
|
|
|
|
string fmt;
|
2019-05-19 22:13:13 +02:00
|
|
|
bool inPct = false;
|
|
|
|
|
AstNode* argp = nodep->exprsp();
|
2021-06-21 00:32:57 +02:00
|
|
|
const string text = nodep->text();
|
2020-08-16 18:54:32 +02:00
|
|
|
for (const char ch : text) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!inPct && ch == '%') {
|
2019-05-19 22:13:13 +02:00
|
|
|
inPct = true;
|
|
|
|
|
fmt = ch;
|
2023-02-11 02:32:35 +01:00
|
|
|
} else if (inPct && (std::isdigit(ch) || ch == '.' || ch == '-')) {
|
2019-05-19 22:13:13 +02:00
|
|
|
fmt += ch;
|
|
|
|
|
} else if (inPct) {
|
|
|
|
|
inPct = false;
|
|
|
|
|
fmt += ch;
|
2023-02-11 02:32:35 +01:00
|
|
|
switch (std::tolower(ch)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
case '%': break; // %% - just output a %
|
|
|
|
|
case 'm': break; // %m - auto insert "name"
|
|
|
|
|
case 'l': break; // %l - auto insert "library"
|
2020-04-16 01:39:03 +02:00
|
|
|
case 't': // FALLTHRU
|
|
|
|
|
case '^': // %t/%^ - don't know $timeformat so can't constify
|
|
|
|
|
if (argp) argp = argp->nextp();
|
|
|
|
|
break;
|
2019-05-19 22:13:13 +02:00
|
|
|
default: // Most operators, just move to next argument
|
|
|
|
|
if (argp) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const nextp = argp->nextp();
|
2020-02-04 05:21:56 +01:00
|
|
|
if (VN_IS(argp, Const)) { // Convert it
|
2021-10-22 14:56:48 +02:00
|
|
|
const string out = VN_AS(argp, Const)->num().displayed(nodep, fmt);
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, " DispConst: " << fmt << " -> " << out << " for "
|
|
|
|
|
<< argp << endl);
|
2019-05-10 02:03:19 +02:00
|
|
|
// fmt = out w/ replace % with %% as it must be literal.
|
2019-05-19 22:13:13 +02:00
|
|
|
fmt = VString::quotePercent(out);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(argp->unlinkFrBack()->deleteTree(), argp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
argp = nextp;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
} // switch
|
|
|
|
|
newFormat += fmt;
|
|
|
|
|
} else {
|
|
|
|
|
newFormat += ch;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (newFormat != nodep->text()) {
|
|
|
|
|
nodep->text(newFormat);
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, " Display out " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!nodep->exprsp() && nodep->name().find('%') == string::npos && !nodep->hidden()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Just a simple constant string - the formatting is pointless
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(replaceConstString(nodep, nodep->name()), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2007-06-14 19:29:37 +02:00
|
|
|
}
|
2023-03-02 03:07:37 +01:00
|
|
|
void visit(AstNodeFTask* nodep) override {
|
|
|
|
|
VL_RESTORER(m_underRecFunc);
|
|
|
|
|
if (nodep->recursive()) m_underRecFunc = true;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2007-06-14 19:29:37 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFuncRef* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_params) { // Only parameters force us to do constant function call propagation
|
|
|
|
|
replaceWithSimulation(nodep);
|
|
|
|
|
}
|
2009-07-17 20:13:11 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstArg* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// replaceWithSimulation on the Arg's parent FuncRef replaces these
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2013-08-18 02:34:49 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstWhile* nodep) override {
|
2021-06-21 00:32:57 +02:00
|
|
|
const bool oldHasJumpDelay = m_hasJumpDelay;
|
2020-05-07 01:25:13 +02:00
|
|
|
m_hasJumpDelay = false;
|
2020-04-15 13:58:34 +02:00
|
|
|
{ iterateChildren(nodep); }
|
2021-06-21 00:32:57 +02:00
|
|
|
const bool thisWhileHasJumpDelay = m_hasJumpDelay;
|
2020-05-07 01:25:13 +02:00
|
|
|
m_hasJumpDelay = thisWhileHasJumpDelay || oldHasJumpDelay;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_doNConst) {
|
|
|
|
|
if (nodep->condp()->isZero()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, "WHILE(0) => nop " << nodep << endl);
|
|
|
|
|
if (nodep->precondsp()) {
|
|
|
|
|
nodep->replaceWith(nodep->precondsp());
|
|
|
|
|
} else {
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
}
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (nodep->condp()->isNeqZero()) {
|
2020-05-07 01:25:13 +02:00
|
|
|
if (!thisWhileHasJumpDelay) {
|
2017-12-27 03:35:08 +01:00
|
|
|
nodep->v3warn(INFINITELOOP, "Infinite loop (condition always true)");
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->fileline()->modifyWarnOff(V3ErrorCode::INFINITELOOP,
|
|
|
|
|
true); // Complain just once
|
2017-12-27 03:35:08 +01:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (operandBoolShift(nodep->condp())) {
|
2019-05-19 22:13:13 +02:00
|
|
|
replaceBoolShift(nodep->condp());
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstInitArray* nodep) override { iterateChildren(nodep); }
|
|
|
|
|
void visit(AstInitItem* nodep) override { iterateChildren(nodep); }
|
|
|
|
|
void visit(AstUnbounded* nodep) override { iterateChildren(nodep); }
|
2019-05-19 22:13:13 +02:00
|
|
|
// These are converted by V3Param. Don't constify as we don't want the
|
|
|
|
|
// from() VARREF to disappear, if any.
|
2009-10-25 21:53:55 +01:00
|
|
|
// If output of a presel didn't get consted, chances are V3Param didn't visit properly
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodePreSel*) override {}
|
2009-10-25 21:53:55 +01:00
|
|
|
|
2010-12-30 13:55:31 +01:00
|
|
|
// Ignored, can eliminate early
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSysIgnore* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2021-02-22 03:25:21 +01:00
|
|
|
if (m_doNConst) VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2010-12-30 13:55:31 +01:00
|
|
|
}
|
|
|
|
|
|
2022-10-12 11:19:21 +02:00
|
|
|
void visit(AstStmtExpr* nodep) override {
|
|
|
|
|
iterateChildren(nodep);
|
2023-04-06 03:27:37 +02:00
|
|
|
if (!nodep->exprp() || VN_IS(nodep->exprp(), Const)) {
|
2022-12-23 22:17:08 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// TODO if there's an ExprStmt underneath just keep lower statements
|
|
|
|
|
// (No current test case needs this)
|
2022-10-12 11:19:21 +02:00
|
|
|
}
|
|
|
|
|
|
2012-02-20 17:48:31 +01:00
|
|
|
// Simplify
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstBasicDType* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->cvtRangeConst();
|
2012-02-20 17:48:31 +01:00
|
|
|
}
|
|
|
|
|
|
2010-02-14 16:01:21 +01:00
|
|
|
//-----
|
|
|
|
|
// Jump elimination
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstJumpGo* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2023-03-17 03:18:03 +01:00
|
|
|
// Jump to label where label immediately follows this go is not useful
|
2020-05-07 03:33:05 +02:00
|
|
|
if (nodep->labelp() == VN_CAST(nodep->nextp(), JumpLabel)) {
|
|
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
|
|
|
|
// Keep the label, might be other jumps pointing to it, gets cleaned later
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-12-01 23:19:18 +01:00
|
|
|
if (m_doExpensive) {
|
2020-05-09 21:00:36 +02:00
|
|
|
// Any non-label statements (at this statement level) can never execute
|
|
|
|
|
while (nodep->nextp() && !VN_IS(nodep->nextp(), JumpLabel)) {
|
|
|
|
|
nodep->nextp()->unlinkFrBack()->deleteTree();
|
|
|
|
|
}
|
2019-12-01 23:19:18 +01:00
|
|
|
// If last statement in a jump label we have JumpLabel(...., JumpGo)
|
|
|
|
|
// Often caused by "return" in a Verilog function. The Go is pointless, remove.
|
|
|
|
|
if (!nodep->nextp()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstJumpBlock* const aboveBlockp = VN_CAST(nodep->abovep(), JumpBlock)) {
|
2020-05-07 03:33:05 +02:00
|
|
|
if (aboveBlockp == nodep->labelp()->blockp()) {
|
|
|
|
|
if (aboveBlockp->endStmtsp() == nodep->labelp()) {
|
|
|
|
|
UINFO(4, "JUMPGO => last remove " << nodep << endl);
|
|
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-12-01 23:19:18 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-07 03:33:05 +02:00
|
|
|
nodep->labelp()->blockp()->user4(true);
|
2019-12-01 23:19:18 +01:00
|
|
|
}
|
2020-05-07 03:33:05 +02:00
|
|
|
m_hasJumpDelay = true;
|
2010-02-14 16:01:21 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstJumpBlock* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstJumpGo's below here that point to this node will set user4
|
|
|
|
|
if (m_doExpensive && !nodep->user4()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, "JUMPLABEL => unused " << nodep << endl);
|
2020-08-15 16:12:55 +02:00
|
|
|
AstNode* underp = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->stmtsp()) underp = nodep->stmtsp()->unlinkFrBackWithNext();
|
2020-04-15 13:58:34 +02:00
|
|
|
if (underp) {
|
|
|
|
|
nodep->replaceWith(underp);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
}
|
2020-05-07 03:33:05 +02:00
|
|
|
nodep->labelp()->unlinkFrBack()->deleteTree();
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2010-02-14 16:01:21 +01:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//-----
|
|
|
|
|
// Below lines are magic expressions processed by astgen
|
2014-04-02 05:16:16 +02:00
|
|
|
// TREE_SKIP_VISIT("AstNODETYPE") # Rename normal visit to visitGen and don't iterate
|
|
|
|
|
//-----
|
2020-04-15 13:58:34 +02:00
|
|
|
// clang-format off
|
2014-04-02 05:16:16 +02:00
|
|
|
TREE_SKIP_VISIT("ArraySel");
|
2022-10-12 11:19:21 +02:00
|
|
|
TREE_SKIP_VISIT("CAwait");
|
2014-04-02 05:16:16 +02:00
|
|
|
|
|
|
|
|
//-----
|
2006-08-26 13:35:28 +02:00
|
|
|
// "AstNODETYPE { # bracket not paren
|
|
|
|
|
// $accessor_name, ...
|
2018-02-02 03:32:58 +01:00
|
|
|
// # .castFoo is the test VN_IS(object,Foo)
|
2007-11-30 23:12:53 +01:00
|
|
|
// # ,, gets replaced with a , rather than &&
|
2019-05-19 22:13:13 +02:00
|
|
|
// }" # bracket not paren
|
2008-11-22 23:37:20 +01:00
|
|
|
// ,"what to call"
|
|
|
|
|
//
|
|
|
|
|
// Where "what_to_call" is:
|
2019-05-19 22:13:13 +02:00
|
|
|
// "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
|
2008-11-22 23:37:20 +01:00
|
|
|
|
|
|
|
|
// In the future maybe support more complicated match & replace:
|
2019-05-19 22:13:13 +02:00
|
|
|
// ("AstOr {%a, AstAnd{AstNot{%b}, %c}} if %a.width1 if %a==%b", "AstOr{%a,%c}; %b.delete");
|
2008-11-22 23:37:20 +01:00
|
|
|
// Lhs/rhs would be implied; for non math operations you'd need $lhsp etc.
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-01-16 04:12:45 +01:00
|
|
|
// v--- * * This op done on Verilog or C+++ mode, in all non-m_doConst stages
|
2006-08-26 13:35:28 +02:00
|
|
|
// v--- *1* These ops are always first, as we warn before replacing
|
2021-01-17 05:53:49 +01:00
|
|
|
// v--- *C* This op is a (C)++ op, only in m_doCpp mode
|
2021-01-16 04:12:45 +01:00
|
|
|
// v--- *V* This op is a (V)erilog op, only in m_doV mode
|
|
|
|
|
// v--- *A* This op works on (A)ll constant children, allowed in m_doConst mode
|
|
|
|
|
// v--- *S* This op specifies a type should use (S)hort-circuiting of its lhs op
|
2012-04-20 04:53:52 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOP1("AstSel{warnSelect(nodep)}", "NEVER");
|
2006-08-26 13:35:28 +02:00
|
|
|
// Generic constants on both side. Do this first to avoid other replacements
|
2021-01-16 04:12:45 +01:00
|
|
|
TREEOPA("AstNodeBiop {$lhsp.castConst, $rhsp.castConst, nodep->isPredictOptimizable()}", "replaceConst(nodep)");
|
|
|
|
|
TREEOPA("AstNodeUniop{$lhsp.castConst, !nodep->isOpaque(), nodep->isPredictOptimizable()}", "replaceConst(nodep)");
|
|
|
|
|
TREEOPA("AstNodeQuadop{$lhsp.castConst, $rhsp.castConst, $thsp.castConst, $fhsp.castConst}", "replaceConst(nodep)");
|
2006-08-26 13:35:28 +02:00
|
|
|
// Zero on one side or the other
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOP ("AstAdd {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
|
|
|
|
|
TREEOP ("AstAnd {$lhsp.isZero, $rhsp, isTPure($rhsp)}", "replaceZero(nodep)"); // Can't use replaceZeroChkPure as we make this pattern in ChkPure
|
2012-04-20 04:53:52 +02:00
|
|
|
// This visit function here must allow for short-circuiting.
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOPS("AstLogAnd {$lhsp.isZero}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstLogAnd{$lhsp.isZero, $rhsp}", "replaceZero(nodep)");
|
2012-04-20 04:53:52 +02:00
|
|
|
// This visit function here must allow for short-circuiting.
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOPS("AstLogOr {$lhsp.isOne}", "replaceNum(nodep, 1)");
|
2022-11-05 12:35:42 +01:00
|
|
|
TREEOP ("AstLogOr {$lhsp.isZero, $rhsp}", "replaceWRhsBool(nodep)");
|
2019-05-19 22:13:13 +02: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 {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule
|
|
|
|
|
TREEOP ("AstPowSS {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule
|
|
|
|
|
TREEOP ("AstPowSU {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule
|
|
|
|
|
TREEOP ("AstPowUS {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule
|
|
|
|
|
TREEOP ("AstPow {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)");
|
|
|
|
|
TREEOP ("AstPowSU {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)");
|
|
|
|
|
TREEOP ("AstPowUS {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)");
|
|
|
|
|
TREEOP ("AstPowSU {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)");
|
|
|
|
|
TREEOP ("AstOr {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
|
|
|
|
|
TREEOP ("AstShiftL{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
|
|
|
|
TREEOP ("AstShiftR{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
|
|
|
|
TREEOP ("AstShiftRS{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
|
|
|
|
TREEOP ("AstXor {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
|
|
|
|
|
TREEOP ("AstSub {$lhsp.isZero, $rhsp}", "AstNegate{$rhsp}");
|
|
|
|
|
TREEOP ("AstAdd {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
|
|
|
|
TREEOP ("AstAnd {$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)");
|
|
|
|
|
TREEOP ("AstLogAnd{$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)");
|
2022-11-05 12:35:42 +01:00
|
|
|
TREEOP ("AstLogOr {$lhsp, $rhsp.isZero}", "replaceWLhsBool(nodep)");
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOP ("AstMul {$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)");
|
|
|
|
|
TREEOP ("AstMulS {$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)");
|
|
|
|
|
TREEOP ("AstOr {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
|
|
|
|
TREEOP ("AstShiftL{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
|
|
|
|
TREEOP ("AstShiftR{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
|
|
|
|
TREEOP ("AstShiftRS{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
|
|
|
|
TREEOP ("AstSub {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
|
|
|
|
TREEOP ("AstXor {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
2006-08-26 13:35:28 +02:00
|
|
|
// Non-zero on one side or the other
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOP ("AstAnd {$lhsp.isAllOnes, $rhsp}", "replaceWRhs(nodep)");
|
2022-11-05 12:35:42 +01:00
|
|
|
TREEOP ("AstLogAnd{$lhsp.isNeqZero, $rhsp}", "replaceWRhsBool(nodep)");
|
2022-01-08 18:01:39 +01:00
|
|
|
TREEOP ("AstOr {$lhsp.isAllOnes, $rhsp, isTPure($rhsp)}", "replaceWLhs(nodep)"); // ->allOnes
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOP ("AstLogOr {$lhsp.isNeqZero, $rhsp}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOP ("AstAnd {$lhsp, $rhsp.isAllOnes}", "replaceWLhs(nodep)");
|
2022-11-05 12:35:42 +01:00
|
|
|
TREEOP ("AstLogAnd{$lhsp, $rhsp.isNeqZero}", "replaceWLhsBool(nodep)");
|
2022-01-08 18:01:39 +01:00
|
|
|
TREEOP ("AstOr {$lhsp, $rhsp.isAllOnes, isTPure($lhsp)}", "replaceWRhs(nodep)"); // ->allOnes
|
2021-12-12 00:38:23 +01:00
|
|
|
TREEOP ("AstLogOr {$lhsp, $rhsp.isNeqZero, isTPure($lhsp), nodep->isPure()}", "replaceNum(nodep,1)");
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOP ("AstXor {$lhsp.isAllOnes, $rhsp}", "AstNot{$rhsp}");
|
|
|
|
|
TREEOP ("AstMul {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)");
|
|
|
|
|
TREEOP ("AstMulS {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)");
|
|
|
|
|
TREEOP ("AstDiv {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)");
|
|
|
|
|
TREEOP ("AstDivS {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)");
|
2020-06-11 13:39:37 +02:00
|
|
|
TREEOP ("AstMul {operandIsPowTwo($lhsp), operandsSameSize($lhsp,,$rhsp)}", "replaceMulShift(nodep)"); // a*2^n -> a<<n
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOP ("AstDiv {$lhsp, operandIsPowTwo($rhsp)}", "replaceDivShift(nodep)"); // a/2^n -> a>>n
|
2019-11-10 18:12:57 +01:00
|
|
|
TREEOP ("AstModDiv{$lhsp, operandIsPowTwo($rhsp)}", "replaceModAnd(nodep)"); // a % 2^n -> a&(2^n-1)
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOP ("AstPow {operandIsTwo($lhsp), $rhsp}", "replacePowShift(nodep)"); // 2**a == 1<<a
|
|
|
|
|
TREEOP ("AstSub {$lhsp.castAdd, operandSubAdd(nodep)}", "AstAdd{AstSub{$lhsp->castAdd()->lhsp(),$rhsp}, $lhsp->castAdd()->rhsp()}"); // ((a+x)-y) -> (a+(x-y))
|
2021-02-18 11:28:49 +01:00
|
|
|
TREEOPC("AstAnd {$lhsp.isOne, matchRedundantClean(nodep)}", "DONE") // 1 & (a == b) -> (IData)(a == b)
|
2006-08-26 13:35:28 +02:00
|
|
|
// Trinary ops
|
|
|
|
|
// Note V3Case::Sel requires Cond to always be conditionally executed in C to prevent core dump!
|
2022-09-15 20:43:56 +02:00
|
|
|
TREEOP ("AstNodeCond{$condp.isZero, $thenp, $elsep}", "replaceWChild(nodep,$elsep)");
|
|
|
|
|
TREEOP ("AstNodeCond{$condp.isNeqZero, $thenp, $elsep}", "replaceWChild(nodep,$thenp)");
|
|
|
|
|
TREEOPA("AstNodeCond{$condp.isZero, $thenp.castConst, $elsep.castConst}", "replaceWChild(nodep,$elsep)");
|
|
|
|
|
TREEOPA("AstNodeCond{$condp.isNeqZero, $thenp.castConst, $elsep.castConst}", "replaceWChild(nodep,$thenp)");
|
|
|
|
|
TREEOP ("AstNodeCond{$condp, operandsSame($thenp,,$elsep)}","replaceWChild(nodep,$thenp)");
|
2012-04-20 04:53:52 +02:00
|
|
|
// This visit function here must allow for short-circuiting.
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOPS("AstCond {$lhsp.isZero}", "replaceWIteratedThs(nodep)");
|
|
|
|
|
TREEOPS("AstCond {$lhsp.isNeqZero}", "replaceWIteratedRhs(nodep)");
|
2022-11-13 21:33:11 +01:00
|
|
|
TREEOP ("AstCond{$condp.castNot, $thenp, $elsep}", "AstCond{$condp->castNot()->lhsp(), $elsep, $thenp}");
|
2022-09-15 20:43:56 +02:00
|
|
|
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isAllOnes, $elsep}", "AstLogOr {$condp, $elsep}"); // a?1:b == a||b
|
2023-05-30 14:59:00 +02:00
|
|
|
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isZero, !$elsep.isClassHandleValue}", "AstLogAnd{$condp, $thenp}"); // a?b:0 == a&&b
|
2022-09-15 20:43:56 +02:00
|
|
|
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isAllOnes}", "AstLogOr {AstNot{$condp}, $thenp}"); // a?b:1 == ~a||b
|
2023-05-30 14:59:00 +02:00
|
|
|
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isZero, !$thenp.isClassHandleValue, $elsep}", "AstLogAnd{AstNot{$condp}, $elsep}"); // a?0:b == ~a&&b
|
2010-12-29 03:23:16 +01:00
|
|
|
TREEOP ("AstNodeCond{!$condp.width1, operandBoolShift(nodep->condp())}", "replaceBoolShift(nodep->condp())");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Prefer constants on left, since that often needs a shift, it lets
|
|
|
|
|
// constant red remove the shift
|
|
|
|
|
TREEOP ("AstNodeBiCom{!$lhsp.castConst, $rhsp.castConst}", "swapSides(nodep)");
|
|
|
|
|
TREEOP ("AstNodeBiComAsv{operandAsvConst(nodep)}", "replaceAsv(nodep)");
|
|
|
|
|
TREEOP ("AstNodeBiComAsv{operandAsvSame(nodep)}", "replaceAsv(nodep)");
|
|
|
|
|
TREEOP ("AstNodeBiComAsv{operandAsvLUp(nodep)}", "replaceAsvLUp(nodep)");
|
|
|
|
|
TREEOP ("AstNodeBiComAsv{operandAsvRUp(nodep)}", "replaceAsvRUp(nodep)");
|
|
|
|
|
TREEOP ("AstLt {!$lhsp.castConst,$rhsp.castConst}", "AstGt {$rhsp,$lhsp}");
|
|
|
|
|
TREEOP ("AstLtS {!$lhsp.castConst,$rhsp.castConst}", "AstGtS {$rhsp,$lhsp}");
|
|
|
|
|
TREEOP ("AstLte {!$lhsp.castConst,$rhsp.castConst}", "AstGte {$rhsp,$lhsp}");
|
|
|
|
|
TREEOP ("AstLteS {!$lhsp.castConst,$rhsp.castConst}", "AstGteS{$rhsp,$lhsp}");
|
|
|
|
|
TREEOP ("AstGt {!$lhsp.castConst,$rhsp.castConst}", "AstLt {$rhsp,$lhsp}");
|
|
|
|
|
TREEOP ("AstGtS {!$lhsp.castConst,$rhsp.castConst}", "AstLtS {$rhsp,$lhsp}");
|
|
|
|
|
TREEOP ("AstGte {!$lhsp.castConst,$rhsp.castConst}", "AstLte {$rhsp,$lhsp}");
|
|
|
|
|
TREEOP ("AstGteS {!$lhsp.castConst,$rhsp.castConst}", "AstLteS{$rhsp,$lhsp}");
|
2006-08-26 13:35:28 +02:00
|
|
|
// v--- *1* as These ops are always first, as we warn before replacing
|
2019-05-19 22:13:13 +02:00
|
|
|
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)");
|
2006-08-26 13:35:28 +02:00
|
|
|
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
|
2022-11-13 21:33:11 +01:00
|
|
|
TREEOP ("AstNot {$lhsp.castNot, $lhsp->width()==VN_AS($lhsp,,Not)->lhsp()->width()}", "replaceWChild(nodep, $lhsp->castNot()->lhsp())"); // NOT(NOT(x))->x
|
|
|
|
|
TREEOP ("AstLogNot{$lhsp.castLogNot}", "replaceWChild(nodep, $lhsp->castLogNot()->lhsp())"); // LOGNOT(LOGNOT(x))->x
|
|
|
|
|
TREEOPV("AstNot {$lhsp.castEqCase, $lhsp.width1}","AstNeqCase{$lhsp->castEqCase()->lhsp(),$lhsp->castEqCase()->rhsp()}");
|
|
|
|
|
TREEOP ("AstLogNot{$lhsp.castEqCase}", "AstNeqCase{$lhsp->castEqCase()->lhsp(),$lhsp->castEqCase()->rhsp()}");
|
|
|
|
|
TREEOPV("AstNot {$lhsp.castNeqCase, $lhsp.width1}","AstEqCase{$lhsp->castNeqCase()->lhsp(),$lhsp->castNeqCase()->rhsp()}");
|
|
|
|
|
TREEOP ("AstLogNot{$lhsp.castNeqCase}", "AstEqCase {$lhsp->castNeqCase()->lhsp(),$lhsp->castNeqCase()->rhsp()}");
|
|
|
|
|
TREEOPV("AstNot {$lhsp.castEqWild, $lhsp.width1}","AstNeqWild{$lhsp->castEqWild()->lhsp(),$lhsp->castEqWild()->rhsp()}");
|
|
|
|
|
TREEOP ("AstLogNot{$lhsp.castEqWild}", "AstNeqWild{$lhsp->castEqWild()->lhsp(),$lhsp->castEqWild()->rhsp()}");
|
|
|
|
|
TREEOPV("AstNot {$lhsp.castNeqWild, $lhsp.width1}","AstEqWild{$lhsp->castNeqWild()->lhsp(),$lhsp->castNeqWild()->rhsp()}");
|
|
|
|
|
TREEOP ("AstLogNot{$lhsp.castNeqWild}", "AstEqWild {$lhsp->castNeqWild()->lhsp(),$lhsp->castNeqWild()->rhsp()}");
|
|
|
|
|
TREEOPV("AstNot {$lhsp.castEq, $lhsp.width1}", "AstNeq {$lhsp->castEq()->lhsp(),$lhsp->castEq()->rhsp()}");
|
|
|
|
|
TREEOP ("AstLogNot{$lhsp.castEq}", "AstNeq {$lhsp->castEq()->lhsp(),$lhsp->castEq()->rhsp()}");
|
|
|
|
|
TREEOPV("AstNot {$lhsp.castNeq, $lhsp.width1}", "AstEq {$lhsp->castNeq()->lhsp(),$lhsp->castNeq()->rhsp()}");
|
|
|
|
|
TREEOP ("AstLogNot{$lhsp.castNeq}", "AstEq {$lhsp->castNeq()->lhsp(),$lhsp->castNeq()->rhsp()}");
|
|
|
|
|
TREEOPV("AstNot {$lhsp.castLt, $lhsp.width1}", "AstGte {$lhsp->castLt()->lhsp(),$lhsp->castLt()->rhsp()}");
|
|
|
|
|
TREEOP ("AstLogNot{$lhsp.castLt}", "AstGte {$lhsp->castLt()->lhsp(),$lhsp->castLt()->rhsp()}");
|
|
|
|
|
TREEOPV("AstNot {$lhsp.castLtS, $lhsp.width1}", "AstGteS{$lhsp->castLtS()->lhsp(),$lhsp->castLtS()->rhsp()}");
|
|
|
|
|
TREEOP ("AstLogNot{$lhsp.castLtS}", "AstGteS{$lhsp->castLtS()->lhsp(),$lhsp->castLtS()->rhsp()}");
|
|
|
|
|
TREEOPV("AstNot {$lhsp.castLte, $lhsp.width1}", "AstGt {$lhsp->castLte()->lhsp(),$lhsp->castLte()->rhsp()}");
|
|
|
|
|
TREEOP ("AstLogNot{$lhsp.castLte}", "AstGt {$lhsp->castLte()->lhsp(),$lhsp->castLte()->rhsp()}");
|
|
|
|
|
TREEOPV("AstNot {$lhsp.castLteS, $lhsp.width1}", "AstGtS {$lhsp->castLteS()->lhsp(),$lhsp->castLteS()->rhsp()}");
|
|
|
|
|
TREEOP ("AstLogNot{$lhsp.castLteS}", "AstGtS {$lhsp->castLteS()->lhsp(),$lhsp->castLteS()->rhsp()}");
|
|
|
|
|
TREEOPV("AstNot {$lhsp.castGt, $lhsp.width1}", "AstLte {$lhsp->castGt()->lhsp(),$lhsp->castGt()->rhsp()}");
|
|
|
|
|
TREEOP ("AstLogNot{$lhsp.castGt}", "AstLte {$lhsp->castGt()->lhsp(),$lhsp->castGt()->rhsp()}");
|
|
|
|
|
TREEOPV("AstNot {$lhsp.castGtS, $lhsp.width1}", "AstLteS{$lhsp->castGtS()->lhsp(),$lhsp->castGtS()->rhsp()}");
|
|
|
|
|
TREEOP ("AstLogNot{$lhsp.castGtS}", "AstLteS{$lhsp->castGtS()->lhsp(),$lhsp->castGtS()->rhsp()}");
|
|
|
|
|
TREEOPV("AstNot {$lhsp.castGte, $lhsp.width1}", "AstLt {$lhsp->castGte()->lhsp(),$lhsp->castGte()->rhsp()}");
|
|
|
|
|
TREEOP ("AstLogNot{$lhsp.castGte}", "AstLt {$lhsp->castGte()->lhsp(),$lhsp->castGte()->rhsp()}");
|
|
|
|
|
TREEOPV("AstNot {$lhsp.castGteS, $lhsp.width1}", "AstLtS {$lhsp->castGteS()->lhsp(),$lhsp->castGteS()->rhsp()}");
|
|
|
|
|
TREEOP ("AstLogNot{$lhsp.castGteS}", "AstLtS {$lhsp->castGteS()->lhsp(),$lhsp->castGteS()->rhsp()}");
|
2006-08-26 13:35:28 +02:00
|
|
|
// Not common, but avoids compiler warnings about over shifting
|
2019-05-19 22:13:13 +02:00
|
|
|
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 13:45:04 +01:00
|
|
|
// Compress out EXTENDs to appease loop unroller
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOPV("AstEq {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE");
|
|
|
|
|
TREEOPV("AstNeq {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE");
|
|
|
|
|
TREEOPV("AstGt {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE");
|
|
|
|
|
TREEOPV("AstGte {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE");
|
|
|
|
|
TREEOPV("AstLt {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE");
|
|
|
|
|
TREEOPV("AstLte {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE");
|
|
|
|
|
TREEOPV("AstEq {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOPV("AstNeq {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOPV("AstGt {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOPV("AstGte {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOPV("AstLt {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOPV("AstLte {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceZero(nodep)");
|
2006-09-27 20:00:53 +02:00
|
|
|
// Identical operands on both sides
|
|
|
|
|
// AstLogAnd/AstLogOr already converted to AstAnd/AstOr for these rules
|
|
|
|
|
// AstAdd->ShiftL(#,1) but uncommon
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOP ("AstAnd {operandsSame($lhsp,,$rhsp)}", "replaceWLhs(nodep)");
|
|
|
|
|
TREEOP ("AstDiv {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOP ("AstDivS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOP ("AstOr {operandsSame($lhsp,,$rhsp)}", "replaceWLhs(nodep)");
|
|
|
|
|
TREEOP ("AstSub {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstXor {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstEq {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); // We let X==X -> 1, although in a true 4-state sim it's X.
|
|
|
|
|
TREEOP ("AstEqD {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); // We let X==X -> 1, although in a true 4-state sim it's X.
|
|
|
|
|
TREEOP ("AstEqN {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); // We let X==X -> 1, although in a true 4-state sim it's X.
|
|
|
|
|
TREEOP ("AstEqCase {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOP ("AstEqWild {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOP ("AstGt {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstGtD {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstGtN {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstGtS {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstGte {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOP ("AstGteD {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOP ("AstGteN {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOP ("AstGteS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOP ("AstLt {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstLtD {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstLtN {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstLtS {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstLte {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOP ("AstLteD {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOP ("AstLteN {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOP ("AstLteS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)");
|
|
|
|
|
TREEOP ("AstNeq {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstNeqD {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstNeqN {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstNeqCase{operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstNeqWild{operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)");
|
2022-11-05 12:35:42 +01:00
|
|
|
TREEOP ("AstLogAnd {operandsSame($lhsp,,$rhsp)}", "replaceWLhsBool(nodep)");
|
|
|
|
|
TREEOP ("AstLogOr {operandsSame($lhsp,,$rhsp)}", "replaceWLhsBool(nodep)");
|
2006-08-26 13:35:28 +02:00
|
|
|
///=== Verilog operators
|
|
|
|
|
// Comparison against 1'b0/1'b1; must be careful about widths.
|
|
|
|
|
// These use Not, so must be Verilog only
|
2019-05-19 22:13:13 +02:00
|
|
|
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
|
2006-08-26 13:35:28 +02:00
|
|
|
// 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)
|
2019-05-19 22:13:13 +02:00
|
|
|
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}
|
2021-10-22 14:56:48 +02:00
|
|
|
TREEOPV("AstRedAnd{$lhsp.castExtend, $lhsp->width() > VN_AS($lhsp,,Extend)->lhsp()->width()}", "replaceZero(nodep)"); // &{0,...} => 0 Prevents compiler limited range error
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOPV("AstRedOr {$lhsp.castExtend}", "AstRedOr {$lhsp->castExtend()->lhsp()}");
|
|
|
|
|
TREEOPV("AstRedXor{$lhsp.castExtend}", "AstRedXor{$lhsp->castExtend()->lhsp()}");
|
2021-10-22 14:56:48 +02:00
|
|
|
TREEOP ("AstRedXor{$lhsp.castXor, VN_IS(VN_AS($lhsp,,Xor)->lhsp(),,Const)}", "AstXor{AstRedXor{$lhsp->castXor()->lhsp()}, AstRedXor{$lhsp->castXor()->rhsp()}}"); // ^(const ^ a) => (^const)^(^a)
|
2021-08-14 22:09:01 +02:00
|
|
|
TREEOPC("AstAnd {$lhsp.castConst, $rhsp.castRedXor, matchBitOpTree(nodep)}", "DONE");
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOPV("AstOneHot{$lhsp.width1}", "replaceWLhs(nodep)");
|
|
|
|
|
TREEOPV("AstOneHot0{$lhsp.width1}", "replaceNum(nodep,1)");
|
2007-11-30 23:12:53 +01:00
|
|
|
// Binary AND/OR is faster than logical and/or (usually)
|
2011-12-22 14:33:16 +01:00
|
|
|
TREEOPV("AstLogAnd{$lhsp.width1, $rhsp.width1, isTPure($lhsp), isTPure($rhsp)}", "AstAnd{$lhsp,$rhsp}");
|
2021-12-12 00:38:23 +01:00
|
|
|
TREEOPV("AstLogOr {$lhsp.width1, $rhsp.width1, nodep->isPure(), isTPure($lhsp), isTPure($rhsp)}", "AstOr{$lhsp,$rhsp}");
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOPV("AstLogNot{$lhsp.width1, isTPure($lhsp)}", "AstNot{$lhsp}");
|
2006-08-26 13:35:28 +02:00
|
|
|
// CONCAT(CONCAT({a},{b}),{c}) -> CONCAT({a},CONCAT({b},{c}))
|
|
|
|
|
// CONCAT({const},CONCAT({const},{c})) -> CONCAT((constifiedCONC{const|const},{c}))
|
2021-11-28 20:17:28 +01:00
|
|
|
TREEOPV("AstConcat{matchConcatRand(nodep)}", "DONE");
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOPV("AstConcat{operandConcatMove(nodep)}", "moveConcat(nodep)");
|
|
|
|
|
TREEOPV("AstConcat{$lhsp.isZero, $rhsp}", "replaceExtend(nodep, nodep->rhsp())");
|
2014-10-23 03:44:41 +02:00
|
|
|
// CONCAT(a[1],a[0]) -> a[1:0]
|
2021-10-22 14:56:48 +02:00
|
|
|
TREEOPV("AstConcat{$lhsp.castSel, $rhsp.castSel, ifAdjacentSel(VN_AS($lhsp,,Sel),,VN_AS($rhsp,,Sel))}", "replaceConcatSel(nodep)");
|
2022-05-05 13:02:52 +02:00
|
|
|
TREEOPV("AstConcat{ifConcatMergeableBiop($lhsp), concatMergeable($lhsp,,$rhsp,,0)}", "replaceConcatMerge(nodep)");
|
2008-11-17 17:36:01 +01:00
|
|
|
// Common two-level operations that can be simplified
|
2021-06-18 17:18:30 +02:00
|
|
|
TREEOP ("AstAnd {$lhsp.castConst,matchAndCond(nodep)}", "DONE");
|
|
|
|
|
TREEOP ("AstAnd {$lhsp.castConst, $rhsp.castOr, matchMaskedOr(nodep)}", "DONE");
|
2021-06-25 19:00:58 +02:00
|
|
|
TREEOPC("AstAnd {$lhsp.castConst, matchMaskedShift(nodep)}", "DONE");
|
2021-06-18 17:18:30 +02:00
|
|
|
TREEOP ("AstAnd {$lhsp.castOr, $rhsp.castOr, operandAndOrSame(nodep)}", "replaceAndOr(nodep)");
|
|
|
|
|
TREEOP ("AstOr {$lhsp.castAnd,$rhsp.castAnd,operandAndOrSame(nodep)}", "replaceAndOr(nodep)");
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOP ("AstOr {matchOrAndNot(nodep)}", "DONE");
|
|
|
|
|
TREEOP ("AstAnd {operandShiftSame(nodep)}", "replaceShiftSame(nodep)");
|
|
|
|
|
TREEOP ("AstOr {operandShiftSame(nodep)}", "replaceShiftSame(nodep)");
|
|
|
|
|
TREEOP ("AstXor {operandShiftSame(nodep)}", "replaceShiftSame(nodep)");
|
2021-08-14 22:09:01 +02:00
|
|
|
TREEOPC("AstAnd {matchBitOpTree(nodep)}", "DONE");
|
|
|
|
|
TREEOPC("AstOr {matchBitOpTree(nodep)}", "DONE");
|
|
|
|
|
TREEOPC("AstXor {matchBitOpTree(nodep)}", "DONE");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Note can't simplify a extend{extends}, extends{extend}, as the sign
|
|
|
|
|
// bits end up in the wrong places
|
2021-10-22 14:56:48 +02:00
|
|
|
TREEOPV("AstExtend {$lhsp.castExtend}", "replaceExtend(nodep, VN_AS(nodep->lhsp(), Extend)->lhsp())");
|
|
|
|
|
TREEOPV("AstExtendS{$lhsp.castExtendS}", "replaceExtend(nodep, VN_AS(nodep->lhsp(), ExtendS)->lhsp())");
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOPV("AstReplicate{$lhsp, $rhsp.isOne, $lhsp->width()==nodep->width()}", "replaceWLhs(nodep)"); // {1{lhs}}->lhs
|
2014-11-29 02:24:42 +01:00
|
|
|
TREEOPV("AstReplicateN{$lhsp, $rhsp.isOne, $lhsp->width()==nodep->width()}", "replaceWLhs(nodep)"); // {1{lhs}}->lhs
|
2017-09-22 03:05:42 +02:00
|
|
|
TREEOPV("AstReplicate{$lhsp.castReplicate, operandRepRep(nodep)}", "DONE"); // {2{3{lhs}}}->{6{lhs}}
|
|
|
|
|
TREEOPV("AstConcat{operandConcatSame(nodep)}", "DONE"); // {a,a}->{2{a}}, {a,2{a}}->{3{a}, etc
|
2006-08-26 13:35:28 +02:00
|
|
|
// Next rule because AUTOINST puts the width of bits in
|
|
|
|
|
// to pins, even when the widths are exactly the same across the hierarchy.
|
2021-11-28 20:17:28 +01:00
|
|
|
TREEOPV("AstSel{matchSelRand(nodep)}", "DONE");
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOPV("AstSel{operandSelExtend(nodep)}", "DONE");
|
|
|
|
|
TREEOPV("AstSel{operandSelFull(nodep)}", "replaceWChild(nodep, nodep->fromp())");
|
|
|
|
|
TREEOPV("AstSel{$fromp.castSel}", "replaceSelSel(nodep)");
|
|
|
|
|
TREEOPV("AstSel{$fromp.castAdd, operandSelBiLower(nodep)}", "DONE");
|
|
|
|
|
TREEOPV("AstSel{$fromp.castAnd, operandSelBiLower(nodep)}", "DONE");
|
|
|
|
|
TREEOPV("AstSel{$fromp.castOr, operandSelBiLower(nodep)}", "DONE");
|
|
|
|
|
TREEOPV("AstSel{$fromp.castSub, operandSelBiLower(nodep)}", "DONE");
|
|
|
|
|
TREEOPV("AstSel{$fromp.castXor, operandSelBiLower(nodep)}", "DONE");
|
|
|
|
|
TREEOPV("AstSel{$fromp.castShiftR, operandSelShiftLower(nodep)}", "DONE");
|
2021-01-16 04:12:45 +01:00
|
|
|
TREEOPA("AstSel{$fromp.castConst, $lsbp.castConst, $widthp.castConst, }", "replaceConst(nodep)");
|
2019-05-19 22:13:13 +02:00
|
|
|
TREEOPV("AstSel{$fromp.castConcat, $lsbp.castConst, $widthp.castConst, }", "replaceSelConcat(nodep)");
|
|
|
|
|
TREEOPV("AstSel{$fromp.castReplicate, $lsbp.castConst, $widthp.castConst, operandSelReplicate(nodep) }", "DONE");
|
2011-02-24 03:21:59 +01:00
|
|
|
// V3Tristate requires selects below BufIf1.
|
2011-02-24 03:36:38 +01:00
|
|
|
// Also do additional operators that are bit-independent, but only definite
|
2011-02-24 03:21:59 +01:00
|
|
|
// win if bit select is a constant (otherwise we may need to compute bit index several times)
|
2019-05-19 22:13:13 +02: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)");
|
2012-04-20 04:53:52 +02:00
|
|
|
// This visit function here must allow for short-circuiting.
|
2019-06-02 01:40:06 +02:00
|
|
|
TREEOPS("AstLogIf{$lhsp.isZero}", "replaceNum(nodep, 1)");
|
|
|
|
|
TREEOPV("AstLogIf{$lhsp, $rhsp}", "AstLogOr{AstLogNot{$lhsp},$rhsp}");
|
|
|
|
|
TREEOPV("AstLogEq{$lhsp, $rhsp}", "replaceLogEq(nodep)");
|
2010-01-18 02:06:08 +01:00
|
|
|
// Strings
|
2021-01-16 04:12:45 +01:00
|
|
|
TREEOPA("AstPutcN{$lhsp.castConst, $rhsp.castConst, $thsp.castConst}", "replaceConst(nodep)");
|
|
|
|
|
TREEOPA("AstSubstrN{$lhsp.castConst, $rhsp.castConst, $thsp.castConst}", "replaceConst(nodep)");
|
2021-10-22 14:56:48 +02:00
|
|
|
TREEOPA("AstCvtPackString{$lhsp.castConst}", "replaceConstString(nodep, VN_AS(nodep->lhsp(), Const)->num().toString())");
|
2020-05-08 03:09:14 +02:00
|
|
|
// Custom
|
|
|
|
|
// Implied by AstIsUnbounded::numberOperate: V("AstIsUnbounded{$lhsp.castConst}", "replaceNum(nodep, 0)");
|
|
|
|
|
TREEOPV("AstIsUnbounded{$lhsp.castUnbounded}", "replaceNum(nodep, 1)");
|
2020-04-15 13:58:34 +02:00
|
|
|
// clang-format on
|
2006-08-26 13:35:28 +02: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
|
|
|
|
|
|
|
|
|
|
//-----
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Default: Just iterate
|
|
|
|
|
if (m_required) {
|
2021-11-26 16:51:11 +01:00
|
|
|
if (VN_IS(nodep, NodeDType) || VN_IS(nodep, Range) || VN_IS(nodep, SliceSel)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Ignore dtypes for parameter type pins
|
|
|
|
|
} else {
|
|
|
|
|
nodep->v3error("Expecting expression to be constant, but can't convert a "
|
2020-04-15 13:58:34 +02:00
|
|
|
<< nodep->prettyTypeName() << " to constant.");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
if (nodep->isTimingControl()) m_hasJumpDelay = true;
|
2019-05-19 22:13:13 +02:00
|
|
|
// Calculate the width of this operation
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_params && !nodep->width()) nodep = V3Width::widthParamsEdit(nodep);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2010-12-29 02:46:13 +01:00
|
|
|
// Processing Mode Enum
|
2020-08-16 18:05:35 +02:00
|
|
|
enum ProcMode : uint8_t {
|
2019-05-19 22:13:13 +02:00
|
|
|
PROC_PARAMS,
|
|
|
|
|
PROC_GENERATE,
|
|
|
|
|
PROC_LIVE,
|
|
|
|
|
PROC_V_WARN,
|
|
|
|
|
PROC_V_NOWARN,
|
|
|
|
|
PROC_V_EXPENSIVE,
|
|
|
|
|
PROC_CPP
|
2010-12-29 02:46:13 +01:00
|
|
|
};
|
2012-03-20 21:01:53 +01:00
|
|
|
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2021-08-11 15:30:00 +02:00
|
|
|
ConstVisitor(ProcMode pmode, bool globalPass)
|
|
|
|
|
: m_globalPass{globalPass}
|
|
|
|
|
, m_concswapNames{globalPass ? ("__Vconcswap_" + cvtToStr(s_globalPassNum++)) : ""} {
|
2020-04-15 13:58:34 +02:00
|
|
|
// clang-format off
|
2019-05-19 22:13:13 +02:00
|
|
|
switch (pmode) {
|
|
|
|
|
case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true;
|
|
|
|
|
m_required = true; break;
|
|
|
|
|
case PROC_GENERATE: m_doV = true; m_doNConst = true; m_params = true;
|
|
|
|
|
m_required = true; m_doGenerate = true; break;
|
|
|
|
|
case PROC_LIVE: break;
|
|
|
|
|
case PROC_V_WARN: m_doV = true; m_doNConst = true; m_warn = true; break;
|
|
|
|
|
case PROC_V_NOWARN: m_doV = true; m_doNConst = true; break;
|
|
|
|
|
case PROC_V_EXPENSIVE: m_doV = true; m_doNConst = true; m_doExpensive = true; break;
|
2020-12-19 17:12:46 +01:00
|
|
|
case PROC_CPP: m_doV = false; m_doNConst = true; m_doCpp = true; break;
|
2019-05-19 22:13:13 +02:00
|
|
|
default: v3fatalSrc("Bad case"); break;
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
// clang-format on
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~ConstVisitor() override {
|
2021-02-21 10:11:33 +01:00
|
|
|
if (m_doCpp) {
|
2022-08-02 14:36:14 +02:00
|
|
|
if (m_globalPass) {
|
|
|
|
|
V3Stats::addStat("Optimizations, Const bit op reduction", m_statBitOpReduction);
|
|
|
|
|
} else {
|
|
|
|
|
V3Stats::addStatSum("Optimizations, Const bit op reduction", m_statBitOpReduction);
|
|
|
|
|
}
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-15 02:13:04 +02:00
|
|
|
AstNode* mainAcceptEdit(AstNode* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Operate starting at a random place
|
2018-05-11 02:55:37 +02:00
|
|
|
return iterateSubtreeReturnEdits(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-08-11 15:30:00 +02:00
|
|
|
uint32_t ConstVisitor::s_globalPassNum = 0;
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Const class functions
|
|
|
|
|
|
2012-04-20 04:53:52 +02:00
|
|
|
//! Force this cell node's parameter list to become a constant
|
|
|
|
|
//! @return Pointer to the edited node.
|
2009-10-15 02:13:04 +02:00
|
|
|
AstNode* V3Const::constifyParamsEdit(AstNode* nodep) {
|
2022-11-27 14:31:22 +01:00
|
|
|
// if (debug() > 0) nodep->dumpTree("- forceConPRE : ");
|
2012-04-20 04:53:52 +02: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);
|
2021-08-11 15:30:00 +02:00
|
|
|
ConstVisitor visitor{ConstVisitor::PROC_PARAMS, /* globalPass: */ false};
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstVar* const varp = VN_CAST(nodep, Var)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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());
|
2012-04-20 04:53:52 +02:00
|
|
|
} else {
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep = visitor.mainAcceptEdit(nodep);
|
2012-04-20 04:53:52 +02:00
|
|
|
}
|
|
|
|
|
// Because we do edits, nodep links may get trashed and core dump this.
|
2022-11-27 14:31:22 +01:00
|
|
|
// if (debug() > 0) nodep->dumpTree("- forceConDONE: ");
|
2012-04-20 04:53:52 +02:00
|
|
|
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
|
2019-09-09 13:50:21 +02:00
|
|
|
//! something a generate block can depend on, we can wait until later to do the
|
2012-04-20 04:53:52 +02:00
|
|
|
//! width check.
|
|
|
|
|
//! @return Pointer to the edited node.
|
|
|
|
|
AstNode* V3Const::constifyGenerateParamsEdit(AstNode* nodep) {
|
2022-11-27 14:31:22 +01:00
|
|
|
// if (debug() > 0) nodep->dumpTree("- forceConPRE:: ");
|
2012-04-20 04:53:52 +02: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::widthGenerateParamsEdit(nodep);
|
2021-08-11 15:30:00 +02:00
|
|
|
ConstVisitor visitor{ConstVisitor::PROC_GENERATE, /* globalPass: */ false};
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstVar* const varp = VN_CAST(nodep, Var)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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());
|
2009-07-16 20:49:34 +02:00
|
|
|
} else {
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep = visitor.mainAcceptEdit(nodep);
|
2009-07-16 20:49:34 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
// Because we do edits, nodep links may get trashed and core dump this.
|
2022-11-27 14:31:22 +01:00
|
|
|
// if (debug() > 0) nodep->dumpTree("- forceConDONE: ");
|
2009-10-15 02:13:04 +02:00
|
|
|
return nodep;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2006-10-05 02:45:39 +02:00
|
|
|
void V3Const::constifyAllLint(AstNetlist* nodep) {
|
2008-11-22 23:27:55 +01:00
|
|
|
// Only call from Verilator.cpp, as it uses user#'s
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2018-03-10 18:57:50 +01:00
|
|
|
{
|
2021-08-11 15:30:00 +02:00
|
|
|
ConstVisitor visitor{ConstVisitor::PROC_V_WARN, /* globalPass: */ true};
|
2018-03-10 18:57:50 +01:00
|
|
|
(void)visitor.mainAcceptEdit(nodep);
|
|
|
|
|
} // Destruct before checking
|
2023-05-04 00:04:10 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("const", 0, dumpTreeLevel() >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2006-10-05 02:45:39 +02:00
|
|
|
void V3Const::constifyCpp(AstNetlist* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2018-03-10 18:57:50 +01:00
|
|
|
{
|
2021-08-11 15:30:00 +02:00
|
|
|
ConstVisitor visitor{ConstVisitor::PROC_CPP, /* globalPass: */ true};
|
2018-03-10 18:57:50 +01:00
|
|
|
(void)visitor.mainAcceptEdit(nodep);
|
|
|
|
|
} // Destruct before checking
|
2023-05-04 00:04:10 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("const_cpp", 0, dumpTreeLevel() >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2009-10-15 02:13:04 +02:00
|
|
|
AstNode* V3Const::constifyEdit(AstNode* nodep) {
|
2021-08-11 15:30:00 +02:00
|
|
|
ConstVisitor visitor{ConstVisitor::PROC_V_NOWARN, /* globalPass: */ false};
|
2009-10-15 02:13:04 +02:00
|
|
|
nodep = visitor.mainAcceptEdit(nodep);
|
|
|
|
|
return nodep;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2009-01-07 15:37:59 +01:00
|
|
|
|
2022-08-02 14:36:14 +02:00
|
|
|
AstNode* V3Const::constifyEditCpp(AstNode* nodep) {
|
|
|
|
|
ConstVisitor visitor{ConstVisitor::PROC_CPP, /* globalPass: */ false};
|
|
|
|
|
nodep = visitor.mainAcceptEdit(nodep);
|
|
|
|
|
return nodep;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-29 03:23:16 +01: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
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2018-03-10 18:57:50 +01:00
|
|
|
{
|
2021-08-11 15:30:00 +02:00
|
|
|
ConstVisitor visitor{ConstVisitor::PROC_LIVE, /* globalPass: */ true};
|
2018-03-10 18:57:50 +01:00
|
|
|
(void)visitor.mainAcceptEdit(nodep);
|
|
|
|
|
} // Destruct before checking
|
2023-05-04 00:04:10 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("const", 0, dumpTreeLevel() >= 3);
|
2010-12-29 03:23:16 +01:00
|
|
|
}
|
|
|
|
|
|
2010-12-29 02:46:13 +01:00
|
|
|
void V3Const::constifyAll(AstNetlist* nodep) {
|
|
|
|
|
// Only call from Verilator.cpp, as it uses user#'s
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2018-03-10 18:57:50 +01:00
|
|
|
{
|
2021-08-11 15:30:00 +02:00
|
|
|
ConstVisitor visitor{ConstVisitor::PROC_V_EXPENSIVE, /* globalPass: */ true};
|
2018-03-10 18:57:50 +01:00
|
|
|
(void)visitor.mainAcceptEdit(nodep);
|
|
|
|
|
} // Destruct before checking
|
2023-05-04 00:04:10 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("const", 0, dumpTreeLevel() >= 3);
|
2010-12-29 02:46:13 +01:00
|
|
|
}
|
|
|
|
|
|
2009-10-15 02:13:04 +02:00
|
|
|
AstNode* V3Const::constifyExpensiveEdit(AstNode* nodep) {
|
2021-08-11 15:30:00 +02:00
|
|
|
ConstVisitor visitor{ConstVisitor::PROC_V_EXPENSIVE, /* globalPass: */ false};
|
2009-10-15 02:13:04 +02:00
|
|
|
nodep = visitor.mainAcceptEdit(nodep);
|
|
|
|
|
return nodep;
|
2009-01-07 15:37:59 +01:00
|
|
|
}
|