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
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2025-01-01 14:30:25 +01:00
|
|
|
// Copyright 2003-2025 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
|
|
|
|
2024-10-01 03:34:34 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
2023-09-25 04:12:23 +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>
|
2025-07-23 18:51:16 +02:00
|
|
|
#include <unordered_set>
|
2018-10-14 19:43:24 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2024-01-02 20:51:40 +01:00
|
|
|
#define TREE_SKIP_VISIT(...)
|
|
|
|
|
#define TREEOP1(...)
|
|
|
|
|
#define TREEOPA(...)
|
|
|
|
|
#define TREEOP(...)
|
|
|
|
|
#define TREEOPS(...)
|
|
|
|
|
#define TREEOPC(...)
|
|
|
|
|
#define TREEOPV(...)
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-30 00:01:50 +01:00
|
|
|
template <typename T>
|
2021-08-18 20:15:02 +02:00
|
|
|
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
|
|
|
}
|
2025-05-18 13:18:37 +02:00
|
|
|
// updateBitRange(), limitBitRangeToLsb(), and polarity() must be called during ascending
|
|
|
|
|
// back to the root.
|
|
|
|
|
void updateBitRange(int newLsb, int newMsb) {
|
|
|
|
|
if ((m_lsb <= m_msb && newLsb > newMsb) || (m_lsb > m_msb && m_lsb < newLsb)) {
|
|
|
|
|
// When the new bit range is out of m_refp, clear polarity because nodes below is
|
|
|
|
|
// shifted out to zero.
|
|
|
|
|
// This kind of clear may happen several times. e.g. (!(1'b1 >> 1)) >> 1
|
|
|
|
|
polarity(true);
|
|
|
|
|
}
|
|
|
|
|
m_lsb = newLsb;
|
|
|
|
|
m_msb = newMsb;
|
|
|
|
|
}
|
2022-07-06 01:33:37 +02:00
|
|
|
void updateBitRange(const AstCCast* castp) {
|
2025-05-18 13:18:37 +02:00
|
|
|
updateBitRange(m_lsb, std::min(m_msb, m_lsb + castp->width() - 1));
|
2022-07-06 01:33:37 +02:00
|
|
|
}
|
|
|
|
|
void updateBitRange(const AstShiftR* shiftp) {
|
2025-05-18 13:18:37 +02:00
|
|
|
updateBitRange(m_lsb + VN_AS(shiftp->rhsp(), Const)->toUInt(), m_msb);
|
2022-07-06 01:33:37 +02:00
|
|
|
}
|
2025-05-18 13:18:37 +02:00
|
|
|
void limitBitRangeToLsb() { updateBitRange(m_lsb, std::min(m_msb, m_lsb)); }
|
2024-06-23 01:50:46 +02:00
|
|
|
int wordIdx() const { return m_wordIdx; }
|
2022-07-06 01:33:37 +02:00
|
|
|
void wordIdx(int i) { m_wordIdx = i; }
|
2024-06-23 01:50:46 +02:00
|
|
|
bool polarity() const { return m_polarity; }
|
2022-07-06 01:33:37 +02:00
|
|
|
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; }
|
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;
|
2025-08-19 23:02:10 +02:00
|
|
|
bool m_polarity = false;
|
|
|
|
|
int m_bit = 0;
|
2021-06-21 00:28:39 +02:00
|
|
|
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,
|
2025-05-17 01:02:19 +02:00
|
|
|
"m_bitPolarities must grow monotonically");
|
2021-06-21 00:28:39 +02:00
|
|
|
UASSERT(m_visitor.m_frozenNodes.size() >= m_frozenSize,
|
2025-05-17 01:02:19 +02:00
|
|
|
"m_frozenNodes must grow monotonically");
|
2021-06-21 00:28:39 +02:00
|
|
|
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; }
|
2024-11-28 20:37:11 +01:00
|
|
|
bool sameVarAs(const AstNodeVarRef* otherp) const { return m_refp->sameNode(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
|
2025-04-29 03:54:58 +02:00
|
|
|
const int widthMin = maskNum.widthToFit();
|
2021-08-18 20:15:02 +02:00
|
|
|
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__)
|
|
|
|
|
|
2025-08-19 23:02:10 +02:00
|
|
|
bool setFailed(bool fail, const char* reason, const 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:"
|
2025-05-23 02:29:32 +02:00
|
|
|
<< line << " when checking:" << nodep);
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(9, m_rootp, "", "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;
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "Increment to " << m_ops << " " << nodep << " called from line " << line);
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
|
|
|
|
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);
|
2025-08-19 23:02:10 +02:00
|
|
|
const 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);
|
2025-05-18 13:18:37 +02:00
|
|
|
if (m_leafp) m_leafp->polarity(!m_leafp->polarity());
|
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);
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstConst* const constp = VN_CAST(nodep->bitp(), Const);
|
2023-11-12 19:30:48 +01:00
|
|
|
CONST_BITOP_RETURN_IF(!constp, nodep->bitp());
|
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);
|
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());
|
2025-05-10 12:01:15 +02:00
|
|
|
if (m_leafp) m_leafp->limitBitRangeToLsb();
|
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
|
2024-01-21 13:25:24 +01:00
|
|
|
// subtree under NOT can be optimized only in XOR tree.
|
|
|
|
|
CONST_BITOP_RETURN_IF(!m_polarity && !isXorTree(), nodep);
|
2021-02-21 10:11:33 +01:00
|
|
|
incrOps(nodep, __LINE__);
|
2021-08-18 20:15:02 +02:00
|
|
|
for (const bool right : {false, true}) {
|
2024-01-18 01:48:07 +01:00
|
|
|
VL_RESTORER(m_leafp);
|
2021-06-21 00:28:39 +02:00
|
|
|
Restorer restorer{*this};
|
2022-07-06 01:33:37 +02:00
|
|
|
LeafInfo leafInfo{m_lsb};
|
2024-08-24 00:24:34 +02:00
|
|
|
// cppcheck-suppress danglingLifetime
|
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
|
2025-09-20 23:40:50 +02:00
|
|
|
UASSERT_OBJ(pol, nodep, "AND/OR tree expects m_polarity==true");
|
2023-03-26 09:29:10 +02:00
|
|
|
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);
|
2024-01-29 16:00:00 +01:00
|
|
|
CONST_BITOP_RETURN_IF(m_lsb, nodep); // the result of EQ/NE is 1 bit width
|
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) {
|
2024-01-28 14:36:09 +01:00
|
|
|
const bool maskFlip = isAndTree() ^ ref.polarity();
|
2022-07-24 12:54:37 +02:00
|
|
|
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,
|
2024-03-11 18:07:19 +01:00
|
|
|
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");
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "OR tree with const 1 term: " << v->refp());
|
2021-08-18 20:15:02 +02:00
|
|
|
// Known 1 bit in OR tree, whole result is 1
|
|
|
|
|
resultp = new AstConst{fl, AstConst::BitTrue{}};
|
|
|
|
|
} else if (visitor.isAndTree()) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "AND tree with const 0 term: " << v->refp());
|
2021-08-18 20:15:02 +02:00
|
|
|
// 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);
|
2024-03-11 18:07:19 +01:00
|
|
|
for (AstNode* const termp : termps) VL_DO_DANGLING(termp->deleteTree(), termp);
|
2021-08-18 20:15:02 +02:00
|
|
|
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;
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "Clean term: " << termps.back());
|
2021-08-18 20:15:02 +02:00
|
|
|
} else {
|
|
|
|
|
hasDirtyTerm = true;
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "Dirty term: " << termps.back());
|
2021-08-18 20:15:02 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "Dirty frozen term: " << termp);
|
2021-08-18 20:15:02 +02:00
|
|
|
}
|
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
|
2023-11-24 17:45:52 +01:00
|
|
|
cout << "- Bitop tree considered:\n";
|
2025-08-19 23:02:10 +02:00
|
|
|
for (const 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
|
|
|
}
|
2023-11-24 17:45:52 +01:00
|
|
|
cout << "- Needs flipping: " << needsFlip << "\n";
|
|
|
|
|
cout << "- Needs cleaning: " << needsCleaning << "\n";
|
|
|
|
|
cout << "- Size: " << resultOps << " input size: " << visitor.m_ops << "\n";
|
2025-10-01 22:20:50 +02:00
|
|
|
} // LCOV_EXCL_STOP
|
2021-08-18 20:15:02 +02:00
|
|
|
|
|
|
|
|
// 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) {
|
2024-03-11 18:07:19 +01:00
|
|
|
for (AstNode* const termp : termps) VL_DO_DANGLING(termp->deleteTree(), termp);
|
2021-08-18 20:15:02 +02:00
|
|
|
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.
|
2024-07-17 11:54:58 +02:00
|
|
|
const int maskWidth
|
|
|
|
|
= std::max(resultp->width(), resultWidth == 1 ? VL_IDATASIZE : resultWidth);
|
2021-08-18 20:15:02 +02:00
|
|
|
|
|
|
|
|
// 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 {
|
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-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
|
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
|
2023-10-17 13:38:45 +02:00
|
|
|
bool m_convertLogicToBit = false; // Convert logical operators to bitwise
|
2025-09-29 16:25:25 +02:00
|
|
|
bool m_hasJumpDelay = false; // JumpGo or Delay under this loop
|
|
|
|
|
bool m_hasLoopTest = false; // Contains AstLoopTest
|
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
|
2025-09-29 14:18:54 +02:00
|
|
|
VDouble0 m_statCondExprRedundant; // Conditional repeated expressions
|
|
|
|
|
VDouble0 m_statIfCondExprRedundant; // Conditional repeated expressions
|
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
|
2023-10-17 13:38:45 +02:00
|
|
|
std::map<const AstNode*, bool> m_containsMemberAccess; // Caches results of matchBiopToBitwise
|
2025-07-23 18:51:16 +02:00
|
|
|
std::unordered_set<AstJumpBlock*> m_usedJumpBlocks; // JumpBlocks used by some JumpGo
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2009-01-21 22:56:50 +01:00
|
|
|
|
2025-05-05 03:41:14 +02:00
|
|
|
V3Number constNumV(AstNode* nodep) {
|
|
|
|
|
// Contract C width to V width (if needed, else just direct copy)
|
|
|
|
|
// The upper zeros in the C representation can otherwise cause
|
|
|
|
|
// wrong results in some operations, e.g. MulS
|
|
|
|
|
const V3Number& numc = VN_AS(nodep, Const)->num();
|
|
|
|
|
return !numc.isNumber() ? numc : V3Number{nodep, nodep->widthMinV(), numc};
|
|
|
|
|
}
|
2025-08-19 23:02:10 +02:00
|
|
|
V3Number toNumC(AstNode* nodep, const V3Number& numv) {
|
2025-05-05 03:41:14 +02:00
|
|
|
// Extend V width back to C width for given node
|
|
|
|
|
return !numv.isNumber() ? numv : V3Number{nodep, nodep->width(), numv};
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-19 23:02:10 +02:00
|
|
|
bool operandConst(const 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;
|
2025-08-19 23:02:10 +02:00
|
|
|
const auto isEqOrNeq = [](const AstNode* nodep) -> bool { //
|
|
|
|
|
return VN_IS(nodep, Eq) || VN_IS(nodep, Neq);
|
|
|
|
|
};
|
2021-02-18 11:28:49 +01:00
|
|
|
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) {
|
2025-05-05 13:04:20 +02:00
|
|
|
andp->replaceWithKeepDType(ccastp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(andp), 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
|
|
|
}
|
2023-10-17 09:26:53 +02:00
|
|
|
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))
|
2025-08-20 19:21:24 +02:00
|
|
|
const 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
|
|
|
}
|
2025-08-20 19:21:24 +02:00
|
|
|
const AstNodeExpr* const bp = notp->lhsp();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!operandsSame(ap, bp)) return false;
|
|
|
|
|
// Do it
|
|
|
|
|
cp->unlinkFrBack();
|
2023-10-17 09:26:53 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(andp->unlinkFrBack()), andp);
|
2020-04-15 13:58:34 +02:00
|
|
|
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.
|
2025-08-20 19:21:24 +02:00
|
|
|
// cppcheck-suppress constVariablePointer // children unlinked below
|
2025-08-16 00:49:06 +02:00
|
|
|
AstCond* const condp = VN_CAST(nodep->rhsp(), Cond);
|
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;
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "AND(CONSTm, CONDcond(c, i, e))->CONDcond(c, AND(m,i), AND(m, e)) " << nodep);
|
2025-08-16 00:49:06 +02:00
|
|
|
AstCond* const newp = new AstCond{
|
|
|
|
|
condp->fileline(), 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),
|
2025-08-16 00:49:06 +02:00
|
|
|
condp->elsep()->unlinkFrBack()}};
|
2022-09-15 20:43:56 +02:00
|
|
|
newp->thenp()->dtypeFrom(nodep); // As And might have been to change widths
|
|
|
|
|
newp->elsep()->dtypeFrom(nodep);
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-11-10 19:17:29 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
2025-09-29 14:18:54 +02:00
|
|
|
|
|
|
|
|
bool matchCondCond(AstCond* nodep) {
|
|
|
|
|
// Same condition on either leg of a condition means that
|
|
|
|
|
// expression is either always true or always false
|
|
|
|
|
if (VN_IS(nodep->backp(), Cond)) return false; // Was checked when visited parent
|
|
|
|
|
if (!VN_IS(nodep->thenp(), Cond) && !VN_IS(nodep->elsep(), Cond))
|
|
|
|
|
return false; // Short circuit
|
|
|
|
|
std::vector<AstNodeExpr*> truesp;
|
|
|
|
|
std::vector<AstNodeExpr*> falsesp;
|
|
|
|
|
matchCondCondRecurse(nodep, truesp /*ref*/, falsesp /*ref*/);
|
|
|
|
|
return false; // Can optimize further
|
|
|
|
|
}
|
|
|
|
|
void matchCondCondRecurse(AstCond* nodep, std::vector<AstNodeExpr*>& truesp,
|
|
|
|
|
std::vector<AstNodeExpr*>& falsesp) {
|
|
|
|
|
// Avoid O(n^2) compares
|
|
|
|
|
// Could reduce cost with hash table, but seems unlikely to be worth cost
|
|
|
|
|
if (truesp.size() > 4 || falsesp.size() > 4) return;
|
|
|
|
|
if (!nodep->condp()->isPure()) return;
|
|
|
|
|
bool replaced = false;
|
|
|
|
|
for (AstNodeExpr* condp : truesp) {
|
|
|
|
|
if (replaced) break;
|
|
|
|
|
if (!operandsSame(nodep->condp(), condp)) continue;
|
|
|
|
|
UINFO(9, "COND(c, CONDb(c, tt, tf), f) -> CONDb(1, tt, tf) " << nodep);
|
|
|
|
|
replaceNum(nodep->condp(), 1);
|
|
|
|
|
replaced = true;
|
|
|
|
|
++m_statCondExprRedundant;
|
|
|
|
|
}
|
|
|
|
|
for (AstNodeExpr* condp : falsesp) {
|
|
|
|
|
if (replaced) break;
|
|
|
|
|
if (!operandsSame(nodep->condp(), condp)) continue;
|
|
|
|
|
UINFO(9, "COND(c, t, CONDb(c, ft, ff)) -> CONDb(0, ft, ff) " << nodep);
|
|
|
|
|
replaceZero(nodep->condp());
|
|
|
|
|
replaced = true;
|
|
|
|
|
++m_statCondExprRedundant;
|
|
|
|
|
}
|
|
|
|
|
if (AstCond* subCondp = VN_CAST(nodep->thenp(), Cond)) {
|
|
|
|
|
if (!replaced) truesp.emplace_back(nodep->condp());
|
|
|
|
|
matchCondCondRecurse(subCondp, truesp /*ref*/, falsesp /*ref*/);
|
|
|
|
|
if (!replaced) truesp.pop_back();
|
|
|
|
|
}
|
|
|
|
|
if (AstCond* subCondp = VN_CAST(nodep->elsep(), Cond)) {
|
|
|
|
|
if (!replaced) falsesp.emplace_back(nodep->condp());
|
|
|
|
|
matchCondCondRecurse(subCondp, truesp /*ref*/, falsesp /*ref*/);
|
|
|
|
|
if (!replaced) falsesp.pop_back();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void matchIfCondCond(AstNodeIf* nodep) {
|
|
|
|
|
// Same condition on either leg of a condition means that
|
|
|
|
|
// expression is either always true or always false
|
|
|
|
|
if (VN_IS(nodep->backp(), If)) return; // Was checked when visited parent
|
|
|
|
|
if (!VN_IS(nodep->thensp(), If) && !VN_IS(nodep->elsesp(), If)) return; // Short circuit
|
|
|
|
|
std::vector<AstNodeExpr*> truesp;
|
|
|
|
|
std::vector<AstNodeExpr*> falsesp;
|
|
|
|
|
matchIfCondCondRecurse(nodep, truesp /*ref*/, falsesp /*ref*/);
|
|
|
|
|
}
|
|
|
|
|
void matchIfCondCondRecurse(AstNodeIf* nodep, std::vector<AstNodeExpr*>& truesp,
|
|
|
|
|
std::vector<AstNodeExpr*>& falsesp) {
|
|
|
|
|
// Avoid O(n^2) compares
|
|
|
|
|
// Could reduce cost with hash table, but seems unlikely to be worth cost
|
|
|
|
|
if (truesp.size() > 4 || falsesp.size() > 4) return;
|
|
|
|
|
if (!nodep->condp()->isPure()) return;
|
|
|
|
|
bool replaced = false;
|
|
|
|
|
for (AstNodeExpr* condp : truesp) {
|
|
|
|
|
if (replaced) break;
|
|
|
|
|
if (!operandsSame(nodep->condp(), condp)) continue;
|
|
|
|
|
UINFO(9, "COND(c, CONDb(c, tt, tf), f) -> CONDb(1, tt, tf) " << nodep);
|
|
|
|
|
replaceNum(nodep->condp(), 1);
|
|
|
|
|
replaced = true;
|
|
|
|
|
++m_statIfCondExprRedundant;
|
|
|
|
|
}
|
|
|
|
|
for (AstNodeExpr* condp : falsesp) {
|
|
|
|
|
if (replaced) break;
|
|
|
|
|
if (!operandsSame(nodep->condp(), condp)) continue;
|
|
|
|
|
UINFO(9, "COND(c, t, CONDb(c, ft, ff)) -> CONDb(0, ft, ff) " << nodep);
|
|
|
|
|
replaceZero(nodep->condp());
|
|
|
|
|
replaced = true;
|
|
|
|
|
++m_statIfCondExprRedundant;
|
|
|
|
|
}
|
|
|
|
|
// We only check the first statement of parent IF is an If
|
|
|
|
|
// So we don't need to check for effects in the executing thensp/elsesp
|
|
|
|
|
// altering the child't condition. e.g. 'if (x) begin x=1; if (x) end'
|
|
|
|
|
if (AstNodeIf* subIfp = VN_CAST(nodep->thensp(), NodeIf)) {
|
|
|
|
|
if (!replaced) truesp.emplace_back(nodep->condp());
|
|
|
|
|
matchIfCondCondRecurse(subIfp, truesp /*ref*/, falsesp /*ref*/);
|
|
|
|
|
if (!replaced) truesp.pop_back();
|
|
|
|
|
}
|
|
|
|
|
if (AstNodeIf* subIfp = VN_CAST(nodep->elsesp(), NodeIf)) {
|
|
|
|
|
if (!replaced) falsesp.emplace_back(nodep->condp());
|
|
|
|
|
matchIfCondCondRecurse(subIfp, truesp /*ref*/, falsesp /*ref*/);
|
|
|
|
|
if (!replaced) falsesp.pop_back();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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);
|
2025-04-29 03:54:58 +02:00
|
|
|
const uint32_t significantBits = constp->num().widthToFit();
|
2021-06-18 17:18:30 +02:00
|
|
|
|
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) {
|
2025-05-05 13:04:20 +02:00
|
|
|
nodep->replaceWithKeepDType(
|
2022-11-21 02:13:55 +01:00
|
|
|
new AstConst{nodep->fileline(), AstConst::DTyped{}, nodep->dtypep()});
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2021-06-18 17:18:30 +02:00
|
|
|
return true;
|
|
|
|
|
} else if (orLIsRedundant) {
|
2025-05-05 13:04:20 +02:00
|
|
|
orp->replaceWithKeepDType(orp->rhsp()->unlinkFrBack());
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(orp), orp);
|
2021-06-18 17:18:30 +02:00
|
|
|
return false; // input node is still valid, keep going
|
|
|
|
|
} else if (orRIsRedundant) {
|
2025-05-05 13:04:20 +02:00
|
|
|
orp->replaceWithKeepDType(orp->lhsp()->unlinkFrBack());
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(orp), orp);
|
2021-06-18 17:18:30 +02:00
|
|
|
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).
|
|
|
|
|
|
2023-11-06 13:13:31 +01:00
|
|
|
const auto checkMask = [nodep, this](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();
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(rhsp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2021-06-18 19:52:02 +02:00
|
|
|
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
|
2024-10-08 14:15:36 +02:00
|
|
|
V3Number mask{nodep, nodep->width()};
|
|
|
|
|
const uint32_t shiftAmount = scp->num().toUInt();
|
|
|
|
|
mask.setMask(nodep->width() - shiftAmount, shiftAmount);
|
|
|
|
|
return checkMask(mask);
|
2021-06-18 19:52:02 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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
|
2024-03-11 18:07:19 +01:00
|
|
|
newp = ConstBitOpTreeVisitor::simplify(andp->rhsp(), width, 1, m_statBitOpReduction);
|
2021-08-18 20:15:02 +02:00
|
|
|
} else { // BitOpTree
|
2024-03-11 18:07:19 +01:00
|
|
|
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) {
|
2025-05-05 13:04:20 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "Transformed leaf of bit tree to " << newp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
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()
|
2023-11-28 03:41:32 +01:00
|
|
|
&& (!VN_AS(nodep->rhsp(), Const)->num().fitsInUInt() // > 2^32 shift
|
|
|
|
|
|| (VN_AS(nodep->rhsp(), Const)->toUInt()
|
|
|
|
|
>= static_cast<uint32_t>(nodep->width())))
|
2023-09-18 15:21:30 +02:00
|
|
|
&& nodep->lhsp()->isPure());
|
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()
|
2023-11-28 03:41:32 +01:00
|
|
|
&& (!VN_AS(nodep->rhsp(), Const)->num().fitsInUInt() // > 2^32 shift
|
|
|
|
|
|| (VN_AS(nodep->rhsp(), Const)->toUInt()
|
|
|
|
|
>= static_cast<uint32_t>(nodep->lhsp()->width())))
|
2023-09-18 15:21:30 +02:00
|
|
|
&& nodep->lhsp()->isPure());
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
bool operandIsTwo(const AstNode* nodep) {
|
2025-07-20 15:56:34 +02:00
|
|
|
const AstConst* const constp = VN_CAST(nodep, Const);
|
|
|
|
|
if (!constp) return false; // not constant
|
|
|
|
|
if (constp->num().isFourState()) return false; // four-state
|
|
|
|
|
if (nodep->width() > VL_QUADSIZE) return false; // too wide
|
|
|
|
|
if (nodep->isSigned() && constp->num().isNegative()) return false; // signed and negative
|
|
|
|
|
return constp->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;
|
2025-09-11 17:10:14 +02:00
|
|
|
|
|
|
|
|
// Only do it if we can fruther reduce one of the sides if pulling the shift through:
|
|
|
|
|
// - Either operands are constants
|
|
|
|
|
if (VN_IS(lhsp->lhsp(), Const)) return true;
|
|
|
|
|
if (VN_IS(lhsp->rhsp(), Const)) return true;
|
|
|
|
|
// - Const shift on either side
|
|
|
|
|
if (AstNodeBiop* const llp = VN_CAST(lhsp->lhsp(), NodeBiop)) {
|
|
|
|
|
return (VN_IS(llp, ShiftL) || VN_IS(llp, ShiftR)) && VN_IS(llp->rhsp(), Const);
|
|
|
|
|
}
|
|
|
|
|
if (AstNodeBiop* const lrp = VN_CAST(lhsp->rhsp(), NodeBiop)) {
|
|
|
|
|
return (VN_IS(lrp, ShiftL) || VN_IS(lrp, ShiftR)) && VN_IS(lrp->rhsp(), Const);
|
|
|
|
|
}
|
|
|
|
|
// Otherwise we would increase logic size
|
|
|
|
|
return false;
|
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.
|
2023-11-12 19:30:48 +01:00
|
|
|
return (VN_IS(nodep->bitp(), Const) && VN_IS(nodep->fromp(), NodeVarRef)
|
2021-10-22 16:15:42 +02:00
|
|
|
&& VN_AS(nodep->fromp(), NodeVarRef)->access().isReadOnly()
|
2023-11-12 19:30:48 +01:00
|
|
|
&& (static_cast<int>(VN_AS(nodep->bitp(), 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) {
|
2025-06-24 17:59:09 +02:00
|
|
|
return (VN_IS(nodep->lsbp(), 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
|
2025-08-20 19:21:24 +02:00
|
|
|
// cppcheck-suppress constVariablePointer // children unlinked below
|
2021-11-26 23:55:36 +01:00
|
|
|
AstExtend* const extendp = VN_CAST(nodep->fromp(), Extend);
|
2025-06-24 17:59:09 +02:00
|
|
|
if (!(m_doV && extendp && VN_IS(nodep->lsbp(), Const) && nodep->lsbConst() == 0
|
2020-04-15 13:58:34 +02:00
|
|
|
&& 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);
|
2025-06-24 17:59:09 +02:00
|
|
|
if (!(m_doV && bip && VN_IS(nodep->lsbp(), Const) && nodep->lsbConst() == 0)) return false;
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, nodep, "", "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()});
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, bip, "", "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})
|
2025-08-20 19:21:24 +02:00
|
|
|
const AstShiftR* const shiftp = VN_CAST(nodep->fromp(), ShiftR);
|
2025-06-24 17:59:09 +02:00
|
|
|
if (!(m_doV && shiftp && VN_IS(shiftp->rhsp(), Const) && VN_IS(nodep->lsbp(), Const))) {
|
2020-04-15 13:58:34 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const ap = shiftp->lhsp();
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstConst* const bp = VN_AS(shiftp->rhsp(), Const);
|
|
|
|
|
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;
|
|
|
|
|
//
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "SEL(SHIFTR(a,b),l,w) -> SEL(a,l+b,w)");
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, nodep, "", "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()};
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, newp, "", "SEL(SH)-ou");
|
2024-03-23 23:12:43 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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
|
|
|
//
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, nodep, "", "BI(EXTEND)-in");
|
2019-05-19 22:13:13 +02:00
|
|
|
smallerp->unlinkFrBack();
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(extendp->unlinkFrBack()), 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();
|
2025-04-30 14:08:44 +02:00
|
|
|
V3Number num{constp, subsize, constp->num()};
|
2022-11-21 02:13:55 +01:00
|
|
|
nodep->lhsp(new AstConst{constp->fileline(), num});
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(constp), constp);
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, nodep, "", "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;
|
2025-08-19 23:02:10 +02:00
|
|
|
const 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
|
|
|
|
|
|
|
|
// 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;
|
2025-06-24 17:59:09 +02:00
|
|
|
if (m_warn && VN_IS(nodep->lsbp(), Const) && doit) {
|
2021-06-21 00:32:57 +02:00
|
|
|
const int maxDeclBit = nodep->declRange().hiMaxSelect() * nodep->declElWidth()
|
|
|
|
|
+ (nodep->declElWidth() - 1);
|
2025-06-24 17:59:09 +02:00
|
|
|
if (VN_AS(nodep->lsbp(), 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="
|
2025-06-24 17:59:09 +02:00
|
|
|
<< nodep->lsbp()->name() << " width=" << nodep->widthConst());
|
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() << ":"
|
2025-05-23 02:29:32 +02:00
|
|
|
<< nodep->lsbConst());
|
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
|
|
|
}
|
|
|
|
|
|
2025-08-19 23:02:10 +02:00
|
|
|
static bool operandsSame(const AstNode* node1p, const AstNode* node2p) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// For now we just detect constants & simple vars, though it could be more generic
|
2024-02-08 04:16:08 +01:00
|
|
|
if (VN_IS(node1p, Const) && VN_IS(node2p, Const)) return node1p->sameGateTree(node2p);
|
|
|
|
|
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);
|
2023-10-18 23:36:09 +02:00
|
|
|
return node1p->isSame(node2p);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2024-02-08 04:16:08 +01:00
|
|
|
// Pattern created by coverage-line; avoid compiler tautological-compare warning
|
2025-08-19 23:02:10 +02:00
|
|
|
if (const AstAnd* const and1p = VN_CAST(node1p, And)) {
|
|
|
|
|
if (const AstAnd* const and2p = VN_CAST(node2p, And)) {
|
2024-02-08 04:16:08 +01:00
|
|
|
if (VN_IS(and1p->lhsp(), Const) && VN_IS(and1p->rhsp(), NodeVarRef)
|
|
|
|
|
&& VN_IS(and2p->lhsp(), Const) && VN_IS(and2p->rhsp(), NodeVarRef))
|
|
|
|
|
return node1p->sameGateTree(node2p);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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;
|
2023-10-09 11:03:57 +02:00
|
|
|
if (AstNode::afterCommentp(lowerIfp->elsesp())) 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 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);
|
2025-06-24 17:59:09 +02:00
|
|
|
if (!lstart || !rstart) return false; // too complicated
|
|
|
|
|
const int rend = (rstart->toSInt() + rhsp->widthConst());
|
2018-10-15 00:39:33 +02:00
|
|
|
return (rend == lstart->toSInt());
|
2014-11-03 01:52:49 +01:00
|
|
|
}
|
2025-09-18 00:45:28 +02:00
|
|
|
bool ifMergeAdjacent(const AstNodeExpr* lhsp, const 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;
|
2025-09-18 00:45:28 +02:00
|
|
|
const AstSel* const lselp = VN_CAST(lhsp, Sel);
|
|
|
|
|
const AstSel* const rselp = VN_CAST(rhsp, Sel);
|
|
|
|
|
if (!lselp && !rselp) return false;
|
|
|
|
|
if (lselp && !VN_IS(lselp->lsbp(), Const)) return false;
|
|
|
|
|
if (rselp && !VN_IS(rselp->lsbp(), Const)) return false;
|
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))
|
2025-09-18 00:45:28 +02:00
|
|
|
return ifMergeAdjacent(lhsp, VN_AS(rhsp, Concat)->lhsp());
|
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))
|
2025-09-18 00:45:28 +02:00
|
|
|
return ifMergeAdjacent(VN_AS(lhsp, Concat)->rhsp(), rhsp);
|
|
|
|
|
// a a[msb:j]
|
|
|
|
|
const AstNodeExpr* const lfromp = lselp ? lselp->fromp()
|
|
|
|
|
: lhsp->sameGateTree(rselp->fromp()) ? lhsp
|
|
|
|
|
: nullptr;
|
|
|
|
|
// a[i:0] a
|
|
|
|
|
const AstNodeExpr* const rfromp = rselp ? rselp->fromp()
|
|
|
|
|
: rhsp->sameGateTree(lselp->fromp()) ? rhsp
|
|
|
|
|
: nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!lfromp || !rfromp || !lfromp->sameGateTree(rfromp)) return false;
|
2025-09-18 00:45:28 +02:00
|
|
|
|
|
|
|
|
const int32_t lstart = lselp ? lselp->lsbConst() : 0;
|
|
|
|
|
const int32_t rstart = rselp ? rselp->lsbConst() : 0;
|
|
|
|
|
const int32_t rend = rstart + (rselp ? rselp->widthConst() : rhsp->width());
|
2019-05-19 22:13:13 +02:00
|
|
|
// a[i:j] a[j-1:k]
|
2025-09-18 00:45:28 +02:00
|
|
|
if (rend == lstart) return true;
|
2019-05-19 22:13:13 +02:00
|
|
|
// a[i:0] a[msb:j]
|
2025-09-18 00:45:28 +02:00
|
|
|
if (rend == rfromp->width() && lstart == 0) return true;
|
2019-05-19 22:13:13 +02:00
|
|
|
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
|
|
|
}
|
2024-05-11 00:47:58 +02:00
|
|
|
static bool operandsSameWidth(const AstNode* lhsp, const 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};
|
2025-05-05 12:31:06 +02:00
|
|
|
oldp->replaceWithKeepDType(newp);
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(6, oldp, "", "const_old");
|
|
|
|
|
UINFOTREE(6, newp, "", "_new");
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(oldp), 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
|
2023-09-18 15:21:30 +02:00
|
|
|
if (checkp->isPure()) {
|
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()};
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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) {
|
2025-05-05 03:41:14 +02:00
|
|
|
V3Number numv{nodep, nodep->widthMinV()};
|
|
|
|
|
nodep->numberOperate(numv, constNumV(nodep->lhsp()));
|
|
|
|
|
const V3Number& num = toNumC(nodep, numv);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "UNICONST -> " << num);
|
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) {
|
2025-05-05 03:41:14 +02:00
|
|
|
V3Number numv{nodep, nodep->widthMinV()};
|
|
|
|
|
nodep->numberOperate(numv, constNumV(nodep->lhsp()), constNumV(nodep->rhsp()));
|
|
|
|
|
const V3Number& num = toNumC(nodep, numv);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "BICONST -> " << num);
|
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) {
|
2025-05-05 03:41:14 +02:00
|
|
|
V3Number numv{nodep, nodep->widthMinV()};
|
|
|
|
|
nodep->numberOperate(numv, constNumV(nodep->lhsp()), constNumV(nodep->rhsp()),
|
|
|
|
|
constNumV(nodep->thsp()));
|
|
|
|
|
const V3Number& num = toNumC(nodep, numv);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "TRICONST -> " << num);
|
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) {
|
2025-05-05 03:41:14 +02:00
|
|
|
V3Number numv{nodep, nodep->widthMinV()};
|
|
|
|
|
nodep->numberOperate(numv, constNumV(nodep->lhsp()), constNumV(nodep->rhsp()),
|
|
|
|
|
constNumV(nodep->thsp()), constNumV(nodep->fhsp()));
|
|
|
|
|
const V3Number& num = toNumC(nodep, numv);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "QUADCONST -> " << num);
|
2020-05-10 20:27:22 +02:00
|
|
|
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};
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(6, oldp, "", "const_old");
|
|
|
|
|
UINFOTREE(6, newp, "", "_new");
|
2025-05-05 13:04:20 +02:00
|
|
|
oldp->replaceWithKeepDType(newp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(oldp), 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.
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(childp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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()) {
|
2025-05-05 13:04:20 +02:00
|
|
|
nodep->replaceWithKeepDType(childp);
|
2022-11-05 12:35:42 +01:00
|
|
|
} else {
|
2025-05-05 13:04:20 +02:00
|
|
|
nodep->replaceWithKeepDType(new AstRedOr{childp->fileline(), childp});
|
2022-11-05 12:35:42 +01:00
|
|
|
}
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2022-11-05 12:35:42 +01:00
|
|
|
}
|
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)
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, nodep, "", "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);
|
2025-05-05 03:41:14 +02:00
|
|
|
rp->dtypeFrom(nodep); // Upper widthMin more likely correct
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(rp->lhsp(), Const) && VN_IS(rp->rhsp(), Const)) replaceConst(rp);
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, nodep, "", "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);
|
2025-05-05 03:41:14 +02:00
|
|
|
lp->dtypeFrom(nodep); // Upper widthMin more likely correct
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, nodep, "", "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);
|
2025-05-05 03:41:14 +02:00
|
|
|
rp->dtypeFrom(nodep); // Upper widthMin more likely correct
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, nodep, "", "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();
|
2025-05-05 13:04:20 +02:00
|
|
|
nodep->replaceWithKeepDType(lp);
|
2019-05-19 22:13:13 +02:00
|
|
|
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);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(rp), rp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(rlp), 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);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(rp), rp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(lrp), lrp);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
nodep->v3fatalSrc("replaceAndOr on something operandAndOrSame shouldn't have matched");
|
|
|
|
|
}
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, nodep, "", "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();
|
2025-05-05 13:04:20 +02:00
|
|
|
nodep->replaceWithKeepDType(lp);
|
2019-05-19 22:13:13 +02:00
|
|
|
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.
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(rp), rp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(rrp), rrp);
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, nodep, "", "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{
|
2023-09-02 02:13:20 +02:00
|
|
|
lselp->fromp()->fileline(), rselp->fromp()->unlinkFrBack(), rstart, lwidth + rwidth};
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(5, "merged two adjacent sel " << lselp << " and " << rselp << " to one " << newselp);
|
2014-10-23 03:44:41 +02:00
|
|
|
|
2025-05-05 13:04:20 +02:00
|
|
|
nodep->replaceWithKeepDType(newselp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(lselp), lselp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(rselp), rselp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2014-10-23 03:44:41 +02:00
|
|
|
}
|
|
|
|
|
void replaceConcatMerge(AstConcat* nodep) {
|
2023-09-02 01:49:57 +02:00
|
|
|
// {llp OP lrp, rlp OP rrp} => {llp, rlp} OP {lrp, rrp}, where OP = AND/OR/XOR
|
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-05-05 13:02:52 +02:00
|
|
|
if (concatMergeable(lp, rp, 0)) {
|
2024-03-23 23:12:43 +01:00
|
|
|
AstNodeExpr* const llp = lp->lhsp();
|
|
|
|
|
AstNodeExpr* const lrp = lp->rhsp();
|
|
|
|
|
AstNodeExpr* const rlp = rp->lhsp();
|
|
|
|
|
AstNodeExpr* const rrp = rp->rhsp();
|
|
|
|
|
AstConcat* const newlp = new AstConcat{rlp->fileline(), llp->cloneTreePure(false),
|
|
|
|
|
rlp->cloneTreePure(false)};
|
|
|
|
|
AstConcat* const newrp = new AstConcat{rrp->fileline(), lrp->cloneTreePure(false),
|
|
|
|
|
rrp->cloneTreePure(false)};
|
2019-05-19 22:13:13 +02:00
|
|
|
// use the lhs to replace the parent concat
|
2024-03-23 23:12:43 +01:00
|
|
|
llp->replaceWith(newlp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(llp), llp);
|
|
|
|
|
lrp->replaceWith(newrp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(lrp), lrp);
|
2020-04-20 03:19:09 +02:00
|
|
|
lp->dtypeChgWidthSigned(newlp->width(), newlp->width(), VSigning::UNSIGNED);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(5, "merged " << nodep);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(rp->unlinkFrBack()), rp);
|
2025-05-05 13:04:20 +02:00
|
|
|
nodep->replaceWithKeepDType(lp->unlinkFrBack());
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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}));
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
void replacePowShift(AstNodeBiop* nodep) { // Pow or PowS
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(5, "POW(2,b)->SHIFTL(1,b) " << nodep);
|
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->lhsp()->dtypeFrom(nodep);
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(5, "MUL(2^n,b)->SHIFTL(b,n) " << nodep);
|
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)};
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(5, "DIV(b,2^n)->SHIFTR(b,n) " << nodep);
|
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)};
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(5, "MOD(b,2^n)->AND(b,2^n-1) " << nodep);
|
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}};
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-11-10 18:12:57 +01:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
void replaceShiftOp(AstNodeBiop* nodep) {
|
2025-09-11 17:10:14 +02:00
|
|
|
UINFO(5, "SHIFT(AND(a,b),CONST) with a/b special ->AND(SHIFT(a,CONST),SHIFT(b,CONST)) "
|
|
|
|
|
<< nodep);
|
2024-01-21 16:30:01 +01:00
|
|
|
const int width = nodep->width();
|
|
|
|
|
const int widthMin = nodep->widthMin();
|
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;
|
2023-10-15 04:20:42 +02:00
|
|
|
AstNodeBiop* const shift2p = nodep->cloneTree(true);
|
2020-04-15 13:58:34 +02:00
|
|
|
shift1p->lhsp(ap);
|
2023-09-17 04:50:54 +02:00
|
|
|
shift1p->rhsp(shiftp->cloneTreePure(true));
|
2020-04-15 13:58:34 +02:00
|
|
|
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);
|
2024-01-21 16:30:01 +01:00
|
|
|
newp->dtypeChgWidth(width, widthMin); // The new AND must have width of the original SHIFT
|
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) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "SHIFT(SHIFT(a,s1),s2)->SHIFT(a,ADD(s1,s2)) " << nodep);
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, nodep, "", "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;
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(shift1p), shift1p);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(shift2p), 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;
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(shift1p), shift1p);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(shift2p), 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}};
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, newp, "", "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
|
|
|
}
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(lhsp), 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;
|
2025-08-20 19:21:24 +02:00
|
|
|
// cppcheck-suppress constVariablePointer // children unlinked below
|
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;
|
2025-08-20 19:21:24 +02:00
|
|
|
const 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;
|
2025-08-20 19:21:24 +02:00
|
|
|
const 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;
|
2025-08-20 19:21:24 +02:00
|
|
|
const AstConst* const con1p = VN_CAST(sel1p->lsbp(), Const);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!con1p) return false;
|
2025-08-20 19:21:24 +02:00
|
|
|
const 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());
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "replaceAssignMultiSel " << nodep);
|
|
|
|
|
UINFO(4, " && " << nextp);
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, nodep, "", "comb1");
|
|
|
|
|
// UINFOTREE(1, nextp, "", "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
|
|
|
}
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, pnewp, "", "conew");
|
2025-05-05 13:04:20 +02:00
|
|
|
nodep->replaceWith(newp); // dypep intentionally changing
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nextp->unlinkFrBack()), 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 {
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
2025-05-13 01:19:38 +02:00
|
|
|
} else if (m_doV && VN_IS(nodep->lhsp(), Concat)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
bool need_temp = false;
|
2025-05-13 01:19:38 +02:00
|
|
|
bool need_temp_pure = !nodep->rhsp()->isPure();
|
|
|
|
|
if (m_warn && !VN_IS(nodep, AssignDly)
|
|
|
|
|
&& !need_temp_pure) { // 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
|
2025-05-13 01:19:38 +02:00
|
|
|
// If the rhs is not pure, we need a temporary variable anyway
|
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) {
|
2025-05-13 01:19:38 +02:00
|
|
|
UASSERT_OBJ(nodep->varp(), nodep, "Unlinked VarRef");
|
|
|
|
|
nodep->varp()->user4(1);
|
2022-01-09 23:34:10 +01:00
|
|
|
});
|
2022-10-20 14:48:44 +02:00
|
|
|
nodep->rhsp()->foreach([&need_temp](const AstVarRef* nodep) {
|
2025-05-13 01:19:38 +02:00
|
|
|
UASSERT_OBJ(nodep->varp(), nodep, "Unlinked VarRef");
|
|
|
|
|
if (nodep->varp()->user4()) need_temp = true;
|
2022-01-09 23:34:10 +01:00
|
|
|
});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2025-05-13 01:19:38 +02:00
|
|
|
if (need_temp_pure) {
|
|
|
|
|
// if the RHS is impure we need to create a temporary variable for it, because
|
|
|
|
|
// further handling involves copying of the RHS.
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " ASSITEMPPURE " << nodep);
|
2025-05-13 01:19:38 +02:00
|
|
|
// ASSIGN(CONCAT(lc1,lc2),rhs) -> ASSIGN(temp,rhs),
|
|
|
|
|
// ASSIGN(lc1,SEL(temp,{size1})),
|
|
|
|
|
// ASSIGN(lc2,SEL(temp,{size2}))
|
|
|
|
|
|
|
|
|
|
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
|
|
|
|
|
AstVar* const tempPurep = new AstVar{rhsp->fileline(), VVarType::BLOCKTEMP,
|
|
|
|
|
m_concswapNames.get(rhsp), rhsp->dtypep()};
|
|
|
|
|
m_modp->addStmtsp(tempPurep);
|
2025-07-04 00:00:39 +02:00
|
|
|
AstVarRef* const tempPureRefp
|
|
|
|
|
= new AstVarRef{rhsp->fileline(), tempPurep, VAccess::WRITE};
|
|
|
|
|
AstNodeAssign* const asnp
|
|
|
|
|
= VN_IS(nodep, AssignDly)
|
2025-08-09 00:21:12 +02:00
|
|
|
? new AstAssign{nodep->fileline(), tempPureRefp, rhsp}
|
2025-07-04 00:00:39 +02:00
|
|
|
: nodep->cloneType(tempPureRefp, rhsp);
|
2025-05-13 01:19:38 +02:00
|
|
|
nodep->addHereThisAsNext(asnp);
|
|
|
|
|
nodep->rhsp(new AstVarRef{rhsp->fileline(), tempPurep, VAccess::READ});
|
|
|
|
|
} else if (need_temp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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.
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " ASSITEMP " << nodep);
|
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 {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " ASSI " << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// ASSIGN(CONCAT(lc1,lc2),rhs) -> ASSIGN(lc1,SEL(rhs,{size})),
|
|
|
|
|
// ASSIGN(lc2,SEL(newrhs,{size}))
|
|
|
|
|
}
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, nodep, "", "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();
|
2023-09-17 04:50:54 +02:00
|
|
|
AstNodeExpr* const rhs2p = rhsp->cloneTreePure(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
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(conp), 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
|
2023-09-06 15:25:48 +02:00
|
|
|
// change the order of bits. Eliminate stream but keep its lhsp.
|
|
|
|
|
// Add a cast if needed.
|
|
|
|
|
AstStreamR* const streamp = VN_AS(nodep->rhsp(), StreamR)->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* srcp = streamp->lhsp()->unlinkFrBack();
|
2025-04-09 12:37:52 +02:00
|
|
|
AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
|
2025-07-27 21:29:56 +02:00
|
|
|
const AstNodeDType* const dstDTypep = nodep->lhsp()->dtypep()->skipRefp();
|
2023-09-06 15:25:48 +02:00
|
|
|
if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) {
|
2025-07-27 21:29:56 +02:00
|
|
|
if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) {
|
|
|
|
|
int srcElementBits = 0;
|
2025-08-19 23:02:10 +02:00
|
|
|
if (const AstNodeDType* const elemDtp = srcDTypep->subDTypep()) {
|
2025-07-27 21:29:56 +02:00
|
|
|
srcElementBits = elemDtp->width();
|
|
|
|
|
}
|
|
|
|
|
int dstElementBits = 0;
|
2025-08-19 23:02:10 +02:00
|
|
|
if (const AstNodeDType* const elemDtp = dstDTypep->subDTypep()) {
|
2025-07-27 21:29:56 +02:00
|
|
|
dstElementBits = elemDtp->width();
|
|
|
|
|
}
|
|
|
|
|
srcp = new AstCvtArrayToArray{
|
|
|
|
|
srcp->fileline(), srcp, nodep->dtypep(), false, 1,
|
|
|
|
|
dstElementBits, srcElementBits};
|
|
|
|
|
} else {
|
|
|
|
|
srcp = new AstCvtArrayToPacked{srcp->fileline(), srcp, nodep->dtypep()};
|
|
|
|
|
}
|
2024-03-21 23:26:42 +01:00
|
|
|
} else if (VN_IS(srcDTypep, UnpackArrayDType)) {
|
2024-08-06 14:48:46 +02:00
|
|
|
srcp = new AstCvtArrayToPacked{srcp->fileline(), srcp, srcDTypep};
|
2024-03-21 23:26:42 +01:00
|
|
|
// Handling the case where lhs is wider than rhs by inserting zeros. StreamL does
|
|
|
|
|
// not require this, since the left streaming operator implicitly handles this.
|
2024-08-06 14:48:46 +02:00
|
|
|
const int packedBits = nodep->lhsp()->widthMin();
|
|
|
|
|
const int unpackBits
|
2024-03-21 23:26:42 +01:00
|
|
|
= srcDTypep->arrayUnpackedElements() * srcDTypep->subDTypep()->widthMin();
|
|
|
|
|
const uint32_t offset = packedBits > unpackBits ? packedBits - unpackBits : 0;
|
|
|
|
|
srcp = new AstShiftL{srcp->fileline(), srcp,
|
2024-08-06 14:48:46 +02:00
|
|
|
new AstConst{srcp->fileline(), offset}, packedBits};
|
2023-09-06 15:25:48 +02:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->rhsp(srcp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(streamp), 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
|
2023-08-25 11:24:12 +02:00
|
|
|
AstNodeExpr* streamp = nodep->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const dstp = VN_AS(streamp, StreamL)->lhsp()->unlinkFrBack();
|
2025-04-09 12:37:52 +02:00
|
|
|
AstNodeDType* const dstDTypep = dstp->dtypep()->skipRefp();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const srcp = nodep->rhsp()->unlinkFrBack();
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
|
2023-08-25 11:24:12 +02:00
|
|
|
const int sWidth = srcp->width();
|
|
|
|
|
const int dWidth = dstp->width();
|
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);
|
2025-04-09 12:37:52 +02:00
|
|
|
if (VN_IS(srcDTypep, DynArrayDType) || VN_IS(srcDTypep, QueueDType)
|
|
|
|
|
|| VN_IS(srcDTypep, UnpackArrayDType)) {
|
2023-06-14 04:46:42 +02:00
|
|
|
streamp->dtypeSetStream();
|
|
|
|
|
} else {
|
|
|
|
|
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED);
|
|
|
|
|
}
|
2025-04-09 12:37:52 +02:00
|
|
|
if (VN_IS(dstDTypep, UnpackArrayDType)) {
|
|
|
|
|
streamp = new AstCvtPackedToArray{nodep->fileline(), streamp, dstDTypep};
|
2024-03-21 23:26:42 +01:00
|
|
|
} else {
|
|
|
|
|
if (dWidth == 0) {
|
2025-04-09 12:37:52 +02:00
|
|
|
streamp = new AstCvtPackedToArray{nodep->fileline(), streamp, dstDTypep};
|
2024-03-21 23:26:42 +01:00
|
|
|
} else if (sWidth >= dWidth) {
|
|
|
|
|
streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth};
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
nodep->lhsp(dstp);
|
|
|
|
|
nodep->rhsp(streamp);
|
2025-04-09 12:37:52 +02:00
|
|
|
nodep->dtypep(dstDTypep);
|
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(), 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.
|
2023-08-25 11:24:12 +02:00
|
|
|
AstNodeExpr* const streamp = nodep->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const dstp = VN_AS(streamp, StreamR)->lhsp()->unlinkFrBack();
|
2025-04-09 12:37:52 +02:00
|
|
|
AstNodeDType* const dstDTypep = dstp->dtypep()->skipRefp();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* srcp = nodep->rhsp()->unlinkFrBack();
|
2023-08-25 11:24:12 +02:00
|
|
|
const int sWidth = srcp->width();
|
|
|
|
|
const int dWidth = dstp->width();
|
2025-04-09 12:37:52 +02:00
|
|
|
if (VN_IS(dstDTypep, UnpackArrayDType)) {
|
2024-03-21 23:26:42 +01:00
|
|
|
const int dstBitWidth
|
2025-04-09 12:37:52 +02:00
|
|
|
= dWidth * VN_AS(dstDTypep, UnpackArrayDType)->arrayUnpackedElements();
|
2024-03-21 23:26:42 +01:00
|
|
|
// Handling the case where rhs is wider than lhs. StreamL does not require this
|
|
|
|
|
// since the combination of the left streaming operation and the implicit
|
|
|
|
|
// truncation in VL_ASSIGN_UNPACK automatically selects the left-most bits.
|
|
|
|
|
if (sWidth > dstBitWidth) {
|
|
|
|
|
srcp
|
|
|
|
|
= new AstSel{streamp->fileline(), srcp, sWidth - dstBitWidth, dstBitWidth};
|
|
|
|
|
}
|
2025-04-09 12:37:52 +02:00
|
|
|
srcp = new AstCvtPackedToArray{nodep->fileline(), srcp, dstDTypep};
|
2024-03-21 23:26:42 +01:00
|
|
|
} else {
|
2025-09-20 23:40:50 +02:00
|
|
|
UASSERT_OBJ(sWidth >= dWidth, nodep,
|
|
|
|
|
"sWidth >= dWidth should have caused an error earlier");
|
2024-03-21 23:26:42 +01:00
|
|
|
if (dWidth == 0) {
|
2025-04-09 12:37:52 +02:00
|
|
|
srcp = new AstCvtPackedToArray{nodep->fileline(), srcp, dstDTypep};
|
2024-03-21 23:26:42 +01:00
|
|
|
} else if (sWidth >= dWidth) {
|
|
|
|
|
srcp = new AstSel{streamp->fileline(), srcp, sWidth - dWidth, dWidth};
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
nodep->lhsp(dstp);
|
|
|
|
|
nodep->rhsp(srcp);
|
2025-04-09 12:37:52 +02:00
|
|
|
nodep->dtypep(dstDTypep);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(streamp), streamp);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Further reduce, any of the nodes may have more reductions.
|
|
|
|
|
return true;
|
2023-09-06 15:25:48 +02:00
|
|
|
} else if (m_doV && VN_IS(nodep->rhsp(), StreamL)) {
|
|
|
|
|
AstStreamL* streamp = VN_AS(nodep->rhsp(), StreamL);
|
2025-07-27 21:29:56 +02:00
|
|
|
AstNodeExpr* srcp = streamp->lhsp();
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
|
2025-07-27 21:29:56 +02:00
|
|
|
AstNodeDType* const dstDTypep = nodep->lhsp()->dtypep()->skipRefp();
|
|
|
|
|
if ((VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)
|
|
|
|
|
|| VN_IS(srcDTypep, UnpackArrayDType))) {
|
|
|
|
|
if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) {
|
|
|
|
|
int blockSize = 1;
|
|
|
|
|
if (const AstConst* const constp = VN_CAST(streamp->rhsp(), Const)) {
|
|
|
|
|
blockSize = constp->toSInt();
|
|
|
|
|
if (VL_UNLIKELY(blockSize <= 0)) {
|
|
|
|
|
// Not reachable due to higher level checks when parsing stream
|
|
|
|
|
// operators commented out to not fail v3error-coverage-checks.
|
|
|
|
|
// nodep->v3error("Stream block size must be positive, got " <<
|
|
|
|
|
// blockSize); nodep->v3error("Stream block size must be positive, got
|
|
|
|
|
// " << blockSize);
|
|
|
|
|
blockSize = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Not reachable due to higher level checks when parsing stream operators
|
|
|
|
|
// commented out to not fail v3error-coverage-checks.
|
|
|
|
|
// else {
|
|
|
|
|
// nodep->v3error("Stream block size must be constant (got " <<
|
|
|
|
|
// streamp->rhsp()->prettyTypeName() << ")");
|
|
|
|
|
// }
|
|
|
|
|
int srcElementBits = 0;
|
2025-08-19 23:02:10 +02:00
|
|
|
if (const AstNodeDType* const elemDtp = srcDTypep->subDTypep()) {
|
2025-07-27 21:29:56 +02:00
|
|
|
srcElementBits = elemDtp->width();
|
|
|
|
|
}
|
|
|
|
|
int dstElementBits = 0;
|
2025-08-19 23:02:10 +02:00
|
|
|
if (const AstNodeDType* const elemDtp = dstDTypep->subDTypep()) {
|
2025-07-27 21:29:56 +02:00
|
|
|
dstElementBits = elemDtp->width();
|
|
|
|
|
}
|
|
|
|
|
streamp->unlinkFrBack();
|
|
|
|
|
srcp = new AstCvtArrayToArray{
|
|
|
|
|
srcp->fileline(), srcp->unlinkFrBack(), dstDTypep, true,
|
|
|
|
|
blockSize, dstElementBits, srcElementBits};
|
|
|
|
|
nodep->rhsp(srcp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(streamp), streamp);
|
|
|
|
|
} else {
|
|
|
|
|
streamp->lhsp(new AstCvtArrayToPacked{srcp->fileline(), srcp->unlinkFrBack(),
|
|
|
|
|
dstDTypep});
|
|
|
|
|
streamp->dtypeFrom(dstDTypep);
|
|
|
|
|
}
|
2023-09-06 15:25:48 +02:00
|
|
|
}
|
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) {
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, nodep, "", "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);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, newp, "", "_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->fileline(nodep->fileline());
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "Simulate->" << newp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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);
|
2024-11-10 16:51:48 +01:00
|
|
|
m_modp = nodep;
|
|
|
|
|
m_concswapNames.reset();
|
|
|
|
|
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);
|
2024-11-10 16:51:48 +01:00
|
|
|
m_wremove = false;
|
|
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2023-08-11 18:28:37 +02:00
|
|
|
void visit(AstCLocalScope* nodep) override {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
if (!nodep->stmtsp()) {
|
|
|
|
|
nodep->unlinkFrBack();
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2023-08-11 18:28:37 +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);
|
2024-11-10 16:51:48 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2023-10-17 13:38:45 +02:00
|
|
|
bool containsMemberAccessRecurse(const AstNode* const nodep) {
|
|
|
|
|
if (!nodep) return false;
|
|
|
|
|
const auto it = m_containsMemberAccess.lower_bound(nodep);
|
|
|
|
|
if (it != m_containsMemberAccess.end() && it->first == nodep) return it->second;
|
|
|
|
|
bool result = false;
|
|
|
|
|
if (VN_IS(nodep, MemberSel) || VN_IS(nodep, MethodCall) || VN_IS(nodep, CMethodCall)) {
|
|
|
|
|
result = true;
|
|
|
|
|
} else if (const AstNodeFTaskRef* const funcRefp = VN_CAST(nodep, NodeFTaskRef)) {
|
|
|
|
|
if (containsMemberAccessRecurse(funcRefp->taskp())) result = true;
|
|
|
|
|
} else if (const AstNodeCCall* const funcRefp = VN_CAST(nodep, NodeCCall)) {
|
|
|
|
|
if (containsMemberAccessRecurse(funcRefp->funcp())) result = true;
|
|
|
|
|
} else if (const AstNodeFTask* const funcp = VN_CAST(nodep, NodeFTask)) {
|
|
|
|
|
// Assume that it has a member access
|
|
|
|
|
if (funcp->recursive()) result = true;
|
|
|
|
|
} else if (const AstCFunc* const funcp = VN_CAST(nodep, CFunc)) {
|
|
|
|
|
if (funcp->recursive()) result = true;
|
|
|
|
|
}
|
|
|
|
|
if (!result) {
|
|
|
|
|
result = containsMemberAccessRecurse(nodep->op1p())
|
|
|
|
|
|| containsMemberAccessRecurse(nodep->op2p())
|
|
|
|
|
|| containsMemberAccessRecurse(nodep->op3p())
|
|
|
|
|
|| containsMemberAccessRecurse(nodep->op4p());
|
|
|
|
|
}
|
|
|
|
|
if (!result && !VN_IS(nodep, NodeFTask)
|
|
|
|
|
&& !VN_IS(nodep, CFunc) // don't enter into next function
|
|
|
|
|
&& containsMemberAccessRecurse(nodep->nextp())) {
|
|
|
|
|
result = true;
|
|
|
|
|
}
|
|
|
|
|
m_containsMemberAccess.insert(it, std::make_pair(nodep, result));
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool matchBiopToBitwise(AstNodeBiop* const nodep) {
|
|
|
|
|
if (!m_convertLogicToBit) return false;
|
|
|
|
|
if (!nodep->lhsp()->width1()) return false;
|
|
|
|
|
if (!nodep->rhsp()->width1()) return false;
|
|
|
|
|
if (!nodep->isPure()) return false;
|
|
|
|
|
if (containsMemberAccessRecurse(nodep)) return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
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);
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstRand* const bRandp = VN_CAST(nodep->rhsp(), Rand);
|
2021-11-28 20:17:28 +01:00
|
|
|
if (!aRandp || !bRandp) return false;
|
|
|
|
|
if (!aRandp->combinable(bRandp)) return false;
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "Concat(Rand,Rand) => Rand: " << nodep);
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(aRandp->unlinkFrBack());
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2021-11-28 20:17:28 +01:00
|
|
|
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;
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "Sel(Rand) => Rand: " << nodep);
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(aRandp->unlinkFrBack());
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2021-11-28 20:17:28 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
2025-08-22 03:44:31 +02:00
|
|
|
bool matchToStringNConst(AstToStringN* nodep) {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
if (AstInitArray* const initp = VN_CAST(nodep->lhsp(), InitArray)) {
|
|
|
|
|
if (!(m_doExpensive || m_params)) return false;
|
|
|
|
|
// At present only support 1D unpacked arrays
|
|
|
|
|
const auto initOfConst = [](const AstNode* const nodep) -> bool { //
|
|
|
|
|
return VN_IS(nodep, Const) || VN_IS(nodep, InitItem);
|
|
|
|
|
};
|
|
|
|
|
if (initp->initsp() && !initp->initsp()->forall(initOfConst)) return false;
|
|
|
|
|
if (initp->defaultp() && !initp->defaultp()->forall(initOfConst)) return false;
|
|
|
|
|
} else if (!VN_IS(nodep->lhsp(), Const)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
replaceWithSimulation(nodep);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2025-08-19 23:02:10 +02:00
|
|
|
int operandConcatMove(const 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) {
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstNodeExpr* const ap = nodep->lhsp();
|
|
|
|
|
const 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.
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "Move concat: " << nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
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);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(bcConcp), 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);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(abConcp), 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(),
|
2023-09-17 04:50:54 +02:00
|
|
|
new AstLogNot{nodep->fileline(), rhsp->cloneTreePure(false)},
|
|
|
|
|
lhsp->cloneTreePure(false)}};
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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)
|
2025-08-20 19:21:24 +02:00
|
|
|
// cppcheck-suppress constVariablePointer // children unlinked below
|
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 lsb1p = nodep->lsbp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const lsb2p = belowp->lsbp()->unlinkFrBack();
|
2019-05-19 22:13:13 +02:00
|
|
|
// Eliminate lower range
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "Elim Lower range: " << nodep);
|
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()};
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(lsb1p), lsb1p);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(lsb2p), 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.
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstNodeExpr* const widep = lsb1p->width() > lsb2p->width() ? lsb1p : lsb2p;
|
2022-11-13 21:33:11 +01:00
|
|
|
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
|
|
|
}
|
2025-06-24 17:59:09 +02:00
|
|
|
AstSel* const newp = new AstSel{nodep->fileline(), fromp, newlsbp, nodep->widthConst()};
|
2025-05-05 13:04:20 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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()};
|
2025-05-05 13:04:20 +02:00
|
|
|
nodep->replaceWithKeepDType(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()};
|
2025-05-05 13:04:20 +02:00
|
|
|
nodep->replaceWithKeepDType(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()}};
|
2025-05-05 13:04:20 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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
|
2025-08-20 19:21:24 +02:00
|
|
|
// cppcheck-suppress constVariablePointer // children unlinked below
|
2021-11-26 23:55:36 +01:00
|
|
|
AstReplicate* const repp = VN_AS(nodep->fromp(), Replicate);
|
2023-11-12 19:30:48 +01:00
|
|
|
AstNodeExpr* const fromp = repp->srcp();
|
2025-08-20 19:21:24 +02:00
|
|
|
const AstConst* const lsbp = VN_CAST(nodep->lsbp(), Const);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!lsbp) 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();
|
2025-06-24 17:59:09 +02:00
|
|
|
AstSel* const newp = new AstSel{
|
|
|
|
|
nodep->fileline(), fromp,
|
|
|
|
|
new AstConst{lsbp->fileline(), lsbp->toUInt() % fromp->width()}, nodep->widthConst()};
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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))
|
2025-08-20 19:21:24 +02:00
|
|
|
// cppcheck-suppress constVariablePointer // children unlinked below
|
2023-11-12 19:30:48 +01:00
|
|
|
AstReplicate* const rep2p = VN_AS(nodep->srcp(), Replicate);
|
|
|
|
|
AstNodeExpr* const from2p = rep2p->srcp();
|
|
|
|
|
AstConst* const cnt1p = VN_CAST(nodep->countp(), Const);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!cnt1p) return false;
|
2023-11-12 19:30:48 +01:00
|
|
|
AstConst* const cnt2p = VN_CAST(rep2p->countp(), Const);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!cnt2p) return false;
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
|
|
|
|
from2p->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()};
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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)) {
|
2025-08-20 19:21:24 +02:00
|
|
|
const AstConst* const cnt1p = VN_CAST(VN_CAST(from1p, Replicate)->countp(), Const);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!cnt1p) return false;
|
2023-11-12 19:30:48 +01:00
|
|
|
from1p = VN_AS(from1p, Replicate)->srcp();
|
2019-05-19 22:13:13 +02:00
|
|
|
cnt1 = cnt1p->toUInt();
|
|
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(from2p, Replicate)) {
|
2025-08-20 19:21:24 +02:00
|
|
|
const AstConst* const cnt2p = VN_CAST(VN_CAST(from2p, Replicate)->countp(), Const);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!cnt2p) return false;
|
2023-11-12 19:30:48 +01:00
|
|
|
from2p = VN_AS(from2p, Replicate)->srcp();
|
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};
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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();
|
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
|
|
|
//
|
2025-06-24 17:59:09 +02:00
|
|
|
fromp->lhsp(
|
|
|
|
|
new AstSel{nodep->fileline(), bilhsp, lsbp->cloneTreePure(true), nodep->widthConst()});
|
|
|
|
|
fromp->rhsp(new AstSel{nodep->fileline(), birhsp, lsbp, nodep->widthConst()});
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(fromp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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();
|
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
|
|
|
//
|
2025-06-24 17:59:09 +02:00
|
|
|
fromp->lhsp(new AstSel{nodep->fileline(), bilhsp, lsbp, nodep->widthConst()});
|
2025-05-05 12:31:06 +02:00
|
|
|
nodep->replaceWithKeepDType(fromp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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);
|
2024-11-10 16:51:48 +01:00
|
|
|
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();
|
2025-05-05 13:04:20 +02:00
|
|
|
nodep->replaceWithKeepDType(fromp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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
|
|
|
}
|
2025-08-06 23:29:40 +02:00
|
|
|
|
|
|
|
|
// Evaluate a slice of an unpacked array. If constantification is
|
|
|
|
|
// required (m_required=true), call replaceWithSimulation() to compute
|
|
|
|
|
// the slice via simulation. Otherwise just iterate the children.
|
|
|
|
|
void visit(AstSliceSel* nodep) override {
|
2025-08-06 23:30:37 +02:00
|
|
|
// First constify or width any child nodes
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
if (!m_required) return; // Do nothing unless we are in parameter mode
|
|
|
|
|
// Fallback to simulation: this will invoke SimulateVisitor::visit(AstSliceSel*)
|
|
|
|
|
replaceWithSimulation(nodep);
|
2025-08-06 23:29:40 +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) {
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, valuep, "", "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
|
2023-12-05 04:11:07 +01:00
|
|
|
&& !nodep->varp()->isClassMember() && !nodep->varp()->sensIfacep()
|
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();
|
2025-05-23 02:29:32 +02:00
|
|
|
// UINFO(2, "constVisit " << cvtToHex(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;
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (m_selp && VN_IS(valuep, InitArray)) {
|
2025-08-20 19:21:24 +02:00
|
|
|
const AstInitArray* const initarp = VN_AS(valuep, InitArray);
|
2021-11-26 23:55:36 +01:00
|
|
|
const uint32_t bit = m_selp->bitConst();
|
2025-08-20 19:21:24 +02:00
|
|
|
const 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();
|
2025-05-23 02:29:32 +02:00
|
|
|
// UINFO(2, "constVisit " << cvtToHex(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;
|
|
|
|
|
}
|
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);
|
2025-05-05 13:04:20 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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);
|
2025-05-05 13:04:20 +02:00
|
|
|
nodep->replaceWithKeepDType(newp);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2020-05-08 03:09:14 +02:00
|
|
|
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
|
|
|
}
|
2023-10-09 11:50:31 +02:00
|
|
|
void visit(AstExprStmt* nodep) override {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
if (!AstNode::afterCommentp(nodep->stmtsp())) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, "ExprStmt(...) " << nodep << " " << nodep->resultp());
|
2023-10-09 11:50:31 +02:00
|
|
|
nodep->replaceWith(nodep->resultp()->unlinkFrBack());
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2023-10-09 11:50:31 +02:00
|
|
|
// Removing the ExprStmt might have made something impure above now pure
|
|
|
|
|
}
|
|
|
|
|
}
|
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()) {
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, nodep->itemp()->valuep(), "", "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".
|
|
|
|
|
|
2025-08-19 23:02:10 +02:00
|
|
|
bool onlySenItemInSenTree(const 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
|
2024-08-08 22:57:12 +02:00
|
|
|
&& !v3Global.opt.timing().isSetTrue() // If --timing, V3Sched would turn this into an
|
|
|
|
|
// infinite loop. See #5080
|
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
|
|
|
}
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), 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{}});
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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;
|
|
|
|
|
}
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, "senItem(NOT...) " << nodep << " " << invert);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (invert) nodep->edgeType(nodep->edgeType().invert());
|
2022-08-19 20:18:38 +02:00
|
|
|
sensp->replaceWith(lastSensp->unlinkFrBack());
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(sensp), 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);
|
2025-03-01 23:11:43 +01:00
|
|
|
if (aConstp->num().isLtXZ(bConstp->num())) return -1;
|
|
|
|
|
if (bConstp->num().isLtXZ(aConstp->num())) return 1;
|
2023-04-13 14:44:54 +02:00
|
|
|
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);
|
2025-09-27 14:22:17 +02:00
|
|
|
if (aCallp->method() < bCallp->method()) return -1;
|
|
|
|
|
if (aCallp->method() > bCallp->method()) return 1;
|
2023-04-13 14:44:54 +02:00
|
|
|
if (const int c = cmp(aCallp->fromp(), bCallp->fromp())) return c;
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstNodeExpr* aPinsp = aCallp->pinsp();
|
|
|
|
|
const AstNodeExpr* bPinsp = bCallp->pinsp();
|
2023-04-13 14:44:54 +02:00
|
|
|
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 {
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstNodeExpr* const lSensp = lhsp->sensp();
|
|
|
|
|
const AstNodeExpr* const rSensp = rhsp->sensp();
|
2023-04-13 14:44:54 +02:00
|
|
|
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) {
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, nodep, "", "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;
|
2025-08-20 19:21:24 +02:00
|
|
|
const AstNodeExpr* const lSenp = lItemp->sensp();
|
|
|
|
|
const AstNodeExpr* const rSenp = rItemp->sensp();
|
2023-04-13 14:44:54 +02:00
|
|
|
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()) {
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(rItemp->unlinkFrBack()), rItemp);
|
2023-04-13 14:44:54 +02:00
|
|
|
nextp = lItemp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Not identical terms, check if they can be combined
|
|
|
|
|
if (lSenp->width() != rSenp->width()) continue;
|
2025-08-20 19:21:24 +02:00
|
|
|
if (const AstAnd* const lAndp = VN_CAST(lSenp, And)) {
|
|
|
|
|
if (const AstAnd* const rAndp = VN_CAST(rSenp, And)) {
|
2023-04-13 14:44:54 +02:00
|
|
|
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
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(rItemp->unlinkFrBack()), rItemp);
|
2023-04-13 14:44:54 +02:00
|
|
|
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
|
|
|
}
|
2025-09-22 22:30:26 +02:00
|
|
|
void visit(AstAlias* 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
|
|
|
}
|
2025-09-30 07:40:17 +02:00
|
|
|
void visit(AstAliasScope* nodep) override {
|
|
|
|
|
// Don't perform any optimizations, keep the alias around
|
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;
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
// Process containing this AssignW as single body statement
|
|
|
|
|
AstAlways* const procp = VN_CAST(nodep->backp(), Always);
|
|
|
|
|
if (!procp || procp->stmtsp() != nodep || nodep->nextp()) return;
|
|
|
|
|
// Not VarXRef, as different refs may set different values to each hierarchy
|
|
|
|
|
AstNodeVarRef* const varrefp = VN_CAST(nodep->lhsp(), VarRef);
|
2020-04-15 13:58:34 +02:00
|
|
|
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
|
2024-05-10 19:19:51 +02:00
|
|
|
&& !varrefp->varScopep() // Not scoped (or each scope may have different initial val.)
|
|
|
|
|
&& !varrefp->varp()->isForced() // Not forced (not really a constant)
|
|
|
|
|
) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// ASSIGNW (VARREF, const) -> INITIAL ( ASSIGN (VARREF, const) )
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "constAssignW " << nodep);
|
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();
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
AstInitial* const newinitp = new AstInitial{flp, new AstAssign{flp, varrefp, exprp}};
|
|
|
|
|
procp->replaceWith(newinitp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(procp), procp);
|
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
|
|
|
}
|
2025-07-27 21:29:56 +02:00
|
|
|
void visit(AstCvtArrayToArray* nodep) override {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
// Handle the case where we have a stream operation inside a cast conversion
|
|
|
|
|
// To avoid infinite recursion, mark the node as processed by setting user1.
|
|
|
|
|
if (!nodep->user1()) {
|
|
|
|
|
nodep->user1(true);
|
|
|
|
|
// Check for both StreamL and StreamR operations
|
|
|
|
|
AstNodeStream* streamp = nullptr;
|
|
|
|
|
bool isReverse = false;
|
|
|
|
|
if (AstStreamL* const streamLp = VN_CAST(nodep->fromp(), StreamL)) {
|
|
|
|
|
streamp = streamLp;
|
|
|
|
|
isReverse = true; // StreamL reverses the operation
|
|
|
|
|
} else if (AstStreamR* const streamRp = VN_CAST(nodep->fromp(), StreamR)) {
|
|
|
|
|
streamp = streamRp;
|
|
|
|
|
isReverse = false; // StreamR doesn't reverse the operation
|
|
|
|
|
}
|
|
|
|
|
if (streamp) {
|
|
|
|
|
AstNodeExpr* srcp = streamp->lhsp();
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
|
2025-07-27 21:29:56 +02:00
|
|
|
AstNodeDType* const dstDTypep = nodep->dtypep()->skipRefp();
|
|
|
|
|
if (VN_IS(srcDTypep, QueueDType) && VN_IS(dstDTypep, QueueDType)) {
|
|
|
|
|
int blockSize = 1;
|
2025-08-19 23:02:10 +02:00
|
|
|
if (const AstConst* const constp = VN_CAST(streamp->rhsp(), Const)) {
|
2025-07-27 21:29:56 +02:00
|
|
|
blockSize = constp->toSInt();
|
|
|
|
|
if (VL_UNLIKELY(blockSize <= 0)) {
|
|
|
|
|
// Not reachable due to higher level checks when parsing stream
|
|
|
|
|
// operators commented out to not fail v3error-coverage-checks.
|
|
|
|
|
// nodep->v3error("Stream block size must be positive, got " <<
|
|
|
|
|
// blockSize);
|
|
|
|
|
blockSize = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Not reachable due to higher level checks when parsing stream operators
|
|
|
|
|
// commented out to not fail v3error-coverage-checks.
|
|
|
|
|
// else {
|
|
|
|
|
// nodep->v3error("Stream block size must be constant (got " <<
|
|
|
|
|
// streamp->rhsp()->prettyTypeName() << ")");
|
|
|
|
|
// }
|
|
|
|
|
int srcElementBits = 0;
|
2025-08-19 23:02:10 +02:00
|
|
|
if (const AstNodeDType* const elemDtp = srcDTypep->subDTypep()) {
|
2025-07-27 21:29:56 +02:00
|
|
|
srcElementBits = elemDtp->width();
|
|
|
|
|
}
|
|
|
|
|
int dstElementBits = 0;
|
2025-08-19 23:02:10 +02:00
|
|
|
if (const AstNodeDType* const elemDtp = dstDTypep->subDTypep()) {
|
2025-07-27 21:29:56 +02:00
|
|
|
dstElementBits = elemDtp->width();
|
|
|
|
|
}
|
|
|
|
|
streamp->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* newp = new AstCvtArrayToArray{
|
|
|
|
|
srcp->fileline(), srcp->unlinkFrBack(), dstDTypep, isReverse,
|
|
|
|
|
blockSize, dstElementBits, srcElementBits};
|
|
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(streamp), streamp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstRelease* nodep) override {
|
2025-08-20 19:21:24 +02:00
|
|
|
// cppcheck-suppress constVariablePointer // children unlinked below
|
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);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2021-12-17 18:56:33 +01:00
|
|
|
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()) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "IF(0,{any},{x}) => {x}: " << nodep);
|
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
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "IF(!0,{x},{any}) => {x}: " << nodep);
|
2022-09-15 20:43:56 +02:00
|
|
|
keepp = nodep->thensp();
|
2020-06-20 05:16:07 +02:00
|
|
|
} else {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "IF condition is X, retaining: " << nodep);
|
2020-06-20 05:16:07 +02:00
|
|
|
return;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
if (keepp) {
|
|
|
|
|
keepp->unlinkFrBackWithNext();
|
|
|
|
|
nodep->replaceWith(keepp);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
}
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2023-10-09 11:03:57 +02:00
|
|
|
} else if (!AstNode::afterCommentp(nodep->thensp())
|
|
|
|
|
&& !AstNode::afterCommentp(nodep->elsesp())) {
|
2023-09-18 15:21:30 +02:00
|
|
|
if (!nodep->condp()->isPure()) {
|
2025-08-08 11:10:40 +02:00
|
|
|
// Condition has side effect - leave, but don't need return value
|
|
|
|
|
UINFO(4, "IF({x}) ; => STMTEXPR({x}): " << nodep);
|
|
|
|
|
nodep->fileline()->warnOff(V3ErrorCode::IGNOREDRETURN, true);
|
|
|
|
|
AstNodeStmt* const newp
|
|
|
|
|
= new AstStmtExpr{nodep->fileline(), nodep->condp()->unlinkFrBack()};
|
|
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2021-09-13 21:52:53 +02:00
|
|
|
} else {
|
|
|
|
|
// Empty block, remove it
|
2025-08-08 11:10:40 +02:00
|
|
|
UINFO(4, "IF({x}) ; => ; : " << nodep);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
2021-09-13 21:52:53 +02:00
|
|
|
}
|
2023-10-09 11:03:57 +02:00
|
|
|
} else if (!AstNode::afterCommentp(nodep->thensp())) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "IF({x}) nullptr {...} => IF(NOT{x}}: " << nodep);
|
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
|
2023-10-09 15:45:09 +02:00
|
|
|
pushDeletep(nodep->thensp()->unlinkFrBackWithNext());
|
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()) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "IF(NOT {x}) => IF(x) swapped if/else" << nodep);
|
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);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (ifSameAssign(nodep)) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4,
|
|
|
|
|
"IF({a}) ASSIGN({b},{c}) else ASSIGN({b},{d}) => ASSIGN({b}, {a}?{c}:{d})");
|
2022-09-15 20:43:56 +02:00
|
|
|
AstNodeAssign* const thensp = VN_AS(nodep->thensp(), NodeAssign);
|
2025-08-20 19:21:24 +02:00
|
|
|
// cppcheck-suppress constVariablePointer // children unlinked below
|
2022-09-15 20:43:56 +02:00
|
|
|
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);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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)) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "IF({a}) IF({b}) => IF({a} && {b})");
|
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);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(lowerIfp), lowerIfp);
|
2025-01-25 16:29:35 +01:00
|
|
|
} else {
|
|
|
|
|
// Optimizations that don't reform the IF itself
|
|
|
|
|
if (operandBoolShift(nodep->condp())) replaceBoolShift(nodep->condp());
|
2025-09-29 14:18:54 +02:00
|
|
|
matchIfCondCond(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(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();
|
2025-08-20 19:21:24 +02:00
|
|
|
// cppcheck-suppress constVariablePointer // children unlinked below
|
2021-11-26 23:55:36 +01:00
|
|
|
AstSFormatF* const nformatp = nodep->fmtp();
|
2020-12-19 17:12:46 +01:00
|
|
|
// 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;
|
2023-10-20 00:33:58 +02:00
|
|
|
if (pformatp->exprsp() && !pformatp->exprsp()->isPureAndNext()) return false;
|
|
|
|
|
if (nformatp->exprsp() && !nformatp->exprsp()->isPureAndNext()) return false;
|
2020-12-19 17:12:46 +01:00
|
|
|
// 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
|
|
|
//
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "DISPLAY(SF({a})) DISPLAY(SF({b})) -> DISPLAY(SF({a}+{b}))");
|
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);
|
|
|
|
|
}
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), 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) {
|
2025-05-23 02:29:32 +02:00
|
|
|
// UINFO(9, " Display in " << nodep->text());
|
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)) {
|
2025-04-07 14:38:05 +02:00
|
|
|
case '%': break; // %% - still %%
|
|
|
|
|
case 'm': break; // %m - still %m - auto insert "name"
|
|
|
|
|
case 'l': break; // %l - still %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
|
2025-05-05 03:41:14 +02:00
|
|
|
const string out = constNumV(argp).displayed(nodep, fmt);
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, " DispConst: " << fmt << " -> " << out << " for "
|
2025-05-23 02:29:32 +02:00
|
|
|
<< argp);
|
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);
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(argp->unlinkFrBack()), 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);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " Display out " << nodep);
|
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
|
|
|
|
2023-09-16 04:05:55 +02:00
|
|
|
void visit(AstNodeCCall* nodep) override {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
m_hasJumpDelay = true; // As don't analyze inside tasks for timing controls
|
|
|
|
|
}
|
|
|
|
|
void visit(AstNodeFTaskRef* nodep) override {
|
|
|
|
|
// Note excludes AstFuncRef as other visitor below
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2023-09-16 04:05:55 +02:00
|
|
|
m_hasJumpDelay = true; // As don't analyze inside tasks for timing controls
|
|
|
|
|
}
|
|
|
|
|
void visit(AstFuncRef* nodep) override {
|
|
|
|
|
visit(static_cast<AstNodeFTaskRef*>(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
|
|
|
}
|
2025-09-29 16:25:25 +02:00
|
|
|
void visit(AstLoop* nodep) override {
|
|
|
|
|
VL_RESTORER(m_hasLoopTest);
|
2021-06-21 00:32:57 +02:00
|
|
|
const bool oldHasJumpDelay = m_hasJumpDelay;
|
2020-05-07 01:25:13 +02:00
|
|
|
m_hasJumpDelay = false;
|
2025-09-29 16:25:25 +02:00
|
|
|
m_hasLoopTest = false;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
bool thisLoopHasJumpDelay = m_hasJumpDelay;
|
|
|
|
|
m_hasJumpDelay = thisLoopHasJumpDelay || oldHasJumpDelay;
|
|
|
|
|
// If the first statement always break, the loop is useless
|
|
|
|
|
if (AstLoopTest* const testp = VN_CAST(nodep->stmtsp(), LoopTest)) {
|
|
|
|
|
if (testp->condp()->isZero()) {
|
|
|
|
|
nodep->v3warn(UNUSEDLOOP, "Loop condition is always false");
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// If last statement always breaks, repalce loop with body
|
|
|
|
|
if (AstNode* lastp = nodep->stmtsp()) {
|
|
|
|
|
while (AstNode* const nextp = lastp->nextp()) lastp = nextp;
|
|
|
|
|
if (AstLoopTest* const testp = VN_CAST(lastp, LoopTest)) {
|
|
|
|
|
if (testp->condp()->isZero()) {
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(testp->unlinkFrBack()), testp);
|
|
|
|
|
nodep->replaceWith(nodep->stmtsp()->unlinkFrBackWithNext());
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
return;
|
2017-12-27 03:35:08 +01:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-09-29 16:25:25 +02:00
|
|
|
// Warn on infinite loop
|
|
|
|
|
if (!m_hasLoopTest && !thisLoopHasJumpDelay) {
|
|
|
|
|
nodep->v3warn(INFINITELOOP, "Infinite loop (condition always true)");
|
|
|
|
|
nodep->fileline()->modifyWarnOff(V3ErrorCode::INFINITELOOP, true); // Complain once
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2025-09-29 16:25:25 +02:00
|
|
|
void visit(AstLoopTest* nodep) override {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
|
|
|
|
|
// If never breaks, remove
|
|
|
|
|
if (nodep->condp()->isNeqZero()) {
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_hasLoopTest = true;
|
|
|
|
|
|
|
|
|
|
// If always breaks, subsequent statements are dead code, delete them
|
|
|
|
|
if (nodep->condp()->isZero()) {
|
|
|
|
|
if (AstNode* const nextp = nodep->nextp()) {
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nextp->unlinkFrBackWithNext()), nodep);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (operandBoolShift(nodep->condp())) replaceBoolShift(nodep->condp());
|
|
|
|
|
}
|
|
|
|
|
|
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);
|
2023-10-09 15:45:09 +02:00
|
|
|
if (m_doNConst) VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), 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)) {
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
2022-12-23 22:17:08 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// TODO if there's an ExprStmt underneath just keep lower statements
|
|
|
|
|
// (No current test case needs this)
|
2025-08-17 00:32:40 +02:00
|
|
|
// TODO if non-pure, can remove. First need to clean up that many expressions used
|
|
|
|
|
// under this node do not yet properly indicate non-pure.
|
|
|
|
|
// Also, under here, a NodeUniOp/NodeBiOp that is pure (but child of such is not
|
|
|
|
|
// pure) can be peeled off until get to the non-pure child-node expression,
|
|
|
|
|
// because all that is needed is to execute what causes the side effect.
|
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 {
|
2025-07-23 18:51:16 +02:00
|
|
|
// Any statements following the JumpGo (at this statement level) never execute, delete
|
|
|
|
|
if (nodep->nextp()) pushDeletep(nodep->nextp()->unlinkFrBackWithNext());
|
|
|
|
|
|
|
|
|
|
// JumpGo as last statement in target JumpBlock (including last in a last sub-list),
|
|
|
|
|
// is a no-op, remove it.
|
|
|
|
|
for (AstNode* abovep = nodep->abovep(); abovep; abovep = abovep->abovep()) {
|
|
|
|
|
if (abovep == nodep->blockp()) {
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
|
|
|
return;
|
2019-12-01 23:19:18 +01:00
|
|
|
}
|
2025-07-23 18:51:16 +02:00
|
|
|
// Stop if not doing expensive, or if the above node is not the last in its list,
|
|
|
|
|
// ... or if it's not an 'if' TODO: it would be enough if it was not a branch.
|
|
|
|
|
if (!m_doExpensive || abovep->nextp() || !VN_IS(abovep, If)) break;
|
2019-12-01 23:19:18 +01:00
|
|
|
}
|
2025-07-23 18:51:16 +02:00
|
|
|
// Mark JumpBlock as used
|
|
|
|
|
m_usedJumpBlocks.emplace(nodep->blockp());
|
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 {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2025-07-23 18:51:16 +02:00
|
|
|
|
2025-09-29 16:25:25 +02:00
|
|
|
// If first statement is an AstLoopTest, pull it before the jump block
|
|
|
|
|
if (AstLoopTest* const testp = VN_CAST(nodep->stmtsp(), LoopTest)) {
|
|
|
|
|
nodep->addHereThisAsNext(testp->unlinkFrBack());
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-23 18:51:16 +02:00
|
|
|
// Remove if empty
|
|
|
|
|
if (!nodep->stmtsp()) {
|
|
|
|
|
UINFO(4, "JUMPLABEL => empty " << nodep);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If no JumpGo points to this node, replace it with its body
|
|
|
|
|
if (!m_usedJumpBlocks.count(nodep)) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "JUMPLABEL => unused " << nodep);
|
2025-07-23 18:51:16 +02:00
|
|
|
nodep->replaceWith(nodep->stmtsp()->unlinkFrBackWithNext());
|
2023-10-09 15:45:09 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), 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 &&
|
2025-09-18 14:34:29 +02:00
|
|
|
// DISABLE_BASE # Turnes off checking Ast's base class treeops
|
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)");
|
2023-09-18 15:21:30 +02:00
|
|
|
TREEOP ("AstAnd {$lhsp.isZero, $rhsp, $rhsp.isPure}", "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 ("AstOr {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
|
2023-10-15 04:34:37 +02:00
|
|
|
TREEOP ("AstShiftL {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
|
|
|
|
TREEOP ("AstShiftLOvr {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
|
|
|
|
TREEOP ("AstShiftR {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
|
|
|
|
TREEOP ("AstShiftROvr {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
|
|
|
|
TREEOP ("AstShiftRS {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
|
|
|
|
TREEOP ("AstShiftRSOvr{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
2019-05-19 22:13:13 +02:00
|
|
|
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)");
|
2023-10-15 04:34:37 +02:00
|
|
|
TREEOP ("AstShiftL {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
|
|
|
|
TREEOP ("AstShiftLOvr {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
|
|
|
|
TREEOP ("AstShiftR {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
|
|
|
|
TREEOP ("AstShiftROvr {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
|
|
|
|
TREEOP ("AstShiftRS {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
|
|
|
|
TREEOP ("AstShiftRSOvr{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)");
|
2019-05-19 22:13:13 +02:00
|
|
|
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)");
|
2023-09-18 15:21:30 +02:00
|
|
|
TREEOP ("AstOr {$lhsp.isAllOnes, $rhsp, $rhsp.isPure}", "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)");
|
2023-09-18 15:21:30 +02:00
|
|
|
TREEOP ("AstOr {$lhsp, $rhsp.isAllOnes, $lhsp.isPure}", "replaceWRhs(nodep)"); // ->allOnes
|
|
|
|
|
TREEOP ("AstLogOr {$lhsp, $rhsp.isNeqZero, $lhsp.isPure, 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)");
|
2024-05-11 00:47:58 +02:00
|
|
|
TREEOP ("AstMul {operandIsPowTwo($lhsp), operandsSameWidth($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)
|
2023-12-20 01:22:54 +01:00
|
|
|
TREEOP ("AstPow {operandIsTwo($lhsp), !$rhsp.isZero}", "replacePowShift(nodep)"); // 2**a == 1<<a
|
2025-07-20 15:56:34 +02:00
|
|
|
TREEOP ("AstPowSU {operandIsTwo($lhsp), !$rhsp.isZero}", "replacePowShift(nodep)"); // 2**a == 1<<a
|
2019-05-19 22:13:13 +02:00
|
|
|
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!
|
2025-08-16 00:49:06 +02:00
|
|
|
TREEOP ("AstCond{$condp.isZero, $thenp, $elsep}", "replaceWChild(nodep,$elsep)");
|
|
|
|
|
TREEOP ("AstCond{$condp.isNeqZero, $thenp, $elsep}", "replaceWChild(nodep,$thenp)");
|
|
|
|
|
TREEOPA("AstCond{$condp.isZero, $thenp.castConst, $elsep.castConst}", "replaceWChild(nodep,$elsep)");
|
|
|
|
|
TREEOPA("AstCond{$condp.isNeqZero, $thenp.castConst, $elsep.castConst}", "replaceWChild(nodep,$thenp)");
|
|
|
|
|
TREEOP ("AstCond{$condp, operandsSame($thenp,,$elsep)}","replaceWChild(nodep,$thenp)");
|
2025-09-29 14:18:54 +02:00
|
|
|
TREEOP ("AstCond{$condp, matchCondCond(nodep)}", "DONE") // Same condition then skip
|
2012-04-20 04:53:52 +02:00
|
|
|
// This visit function here must allow for short-circuiting.
|
2025-04-29 03:54:58 +02:00
|
|
|
TREEOPS("AstCond{$condp.isZero}", "replaceWIteratedThs(nodep)");
|
|
|
|
|
TREEOPS("AstCond{$condp.isNeqZero}", "replaceWIteratedRhs(nodep)");
|
|
|
|
|
TREEOP ("AstCond{$condp.castNot, $thenp, $elsep}", "AstCond{$condp->castNot()->lhsp(), $elsep, $thenp}");
|
2025-08-16 00:49:06 +02:00
|
|
|
TREEOP ("AstCond{$condp.width1, $thenp.width1, $thenp.isAllOnes, $elsep}", "AstLogOr {$condp, $elsep}"); // a?1:b == a||b
|
|
|
|
|
TREEOP ("AstCond{$condp.width1, $thenp.width1, $thenp, $elsep.isZero, !$elsep.isClassHandleValue}", "AstLogAnd{$condp, $thenp}"); // a?b:0 == a&&b
|
|
|
|
|
TREEOP ("AstCond{$condp.width1, $thenp.width1, $thenp, $elsep.isAllOnes}", "AstLogOr {AstNot{$condp}, $thenp}"); // a?b:1 == ~a||b
|
|
|
|
|
TREEOP ("AstCond{$condp.width1, $thenp.width1, $thenp.isZero, !$thenp.isClassHandleValue, $elsep}", "AstLogAnd{AstNot{$condp}, $elsep}"); // a?0:b == ~a&&b
|
|
|
|
|
TREEOP ("AstCond{!$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
|
2023-10-15 04:34:37 +02:00
|
|
|
TREEOP ("AstShiftL {operandHugeShiftL(nodep)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstShiftLOvr{operandHugeShiftL(nodep)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstShiftR {operandHugeShiftR(nodep)}", "replaceZero(nodep)");
|
|
|
|
|
TREEOP ("AstShiftROvr{operandHugeShiftR(nodep)}", "replaceZero(nodep)");
|
2019-05-19 22:13:13 +02:00
|
|
|
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)
|
2023-10-17 13:38:45 +02:00
|
|
|
TREEOPV("AstLogAnd{matchBiopToBitwise(nodep)}", "AstAnd{$lhsp,$rhsp}");
|
|
|
|
|
TREEOPV("AstLogOr {matchBiopToBitwise(nodep)}", "AstOr{$lhsp,$rhsp}");
|
2023-09-18 15:21:30 +02:00
|
|
|
TREEOPV("AstLogNot{$lhsp.width1}", "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
|
2024-05-11 00:47:58 +02:00
|
|
|
TREEOPV("AstExtend{operandsSameWidth(nodep,,$lhsp)}", "replaceWLhs(nodep)");
|
|
|
|
|
TREEOPV("AstExtend{$lhsp.castExtend}", "replaceExtend(nodep, VN_AS(nodep->lhsp(), Extend)->lhsp())");
|
2021-10-22 14:56:48 +02:00
|
|
|
TREEOPV("AstExtendS{$lhsp.castExtendS}", "replaceExtend(nodep, VN_AS(nodep->lhsp(), ExtendS)->lhsp())");
|
2023-11-12 19:30:48 +01:00
|
|
|
TREEOPV("AstReplicate{$srcp, $countp.isOne, $srcp->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
|
2023-11-12 19:30:48 +01:00
|
|
|
TREEOPV("AstReplicate{$srcp.castReplicate, operandRepRep(nodep)}", "DONE"); // {2{3{lhs}}}->{6{lhs}}
|
2017-09-22 03:05:42 +02:00
|
|
|
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");
|
2025-06-24 17:59:09 +02:00
|
|
|
TREEOPA("AstSel{$fromp.castConst, $lsbp.castConst, }", "replaceConst(nodep)");
|
|
|
|
|
TREEOPV("AstSel{$fromp.castConcat, $lsbp.castConst, }", "replaceSelConcat(nodep)");
|
|
|
|
|
TREEOPV("AstSel{$fromp.castReplicate, $lsbp.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)");
|
2023-11-12 20:41:18 +01:00
|
|
|
TREEOPV("AstSel{$fromp.castAnd,$lsbp.castConst}", "replaceSelIntoBiop(nodep)");
|
|
|
|
|
TREEOPV("AstSel{$fromp.castOr,$lsbp.castConst}", "replaceSelIntoBiop(nodep)");
|
|
|
|
|
TREEOPV("AstSel{$fromp.castXor,$lsbp.castConst}", "replaceSelIntoBiop(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())");
|
2025-09-18 14:34:29 +02:00
|
|
|
TREEOP ("AstToStringN{DISABLE_BASE}", "DONE"); // Avoid uniop(const); use matchToStringNConst
|
2025-08-22 03:44:31 +02:00
|
|
|
TREEOP ("AstToStringN{matchToStringNConst(nodep)}", "DONE");
|
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 {
|
2023-07-03 19:55:24 +02:00
|
|
|
PROC_PARAMS_NOWARN,
|
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) {
|
2023-07-03 19:55:24 +02:00
|
|
|
case PROC_PARAMS_NOWARN: m_doV = true; m_doNConst = true; m_params = true;
|
|
|
|
|
m_required = false; break;
|
|
|
|
|
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;
|
2023-10-17 13:38:45 +02:00
|
|
|
case PROC_V_WARN: m_doV = true; m_doNConst = true; m_warn = true; m_convertLogicToBit = true; break;
|
2023-07-03 19:55:24 +02:00
|
|
|
case PROC_V_NOWARN: m_doV = true; m_doNConst = true; break;
|
|
|
|
|
case PROC_V_EXPENSIVE: m_doV = true; m_doNConst = true; m_doExpensive = true; break;
|
|
|
|
|
case PROC_CPP: m_doV = false; m_doNConst = true; m_doCpp = true; break;
|
|
|
|
|
default: v3fatalSrc("Bad case"); break;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
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
|
|
|
}
|
2025-09-29 14:18:54 +02:00
|
|
|
V3Stats::addStatSum("Optimizations, Cond redundant expressions", m_statCondExprRedundant);
|
|
|
|
|
V3Stats::addStatSum("Optimizations, If cond redundant expressions",
|
|
|
|
|
m_statIfCondExprRedundant);
|
2021-02-21 10:11:33 +01:00
|
|
|
}
|
|
|
|
|
|
2009-10-15 02:13:04 +02:00
|
|
|
AstNode* mainAcceptEdit(AstNode* nodep) {
|
2023-10-09 11:50:31 +02:00
|
|
|
VIsCached::clearCacheTree(); // Avoid using any stale isPure
|
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
|
2025-08-17 03:03:12 +02:00
|
|
|
void V3Const::constifyParamsEdit(AstNode* nodep) {
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, nodep, "", "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};
|
2025-08-20 19:21:24 +02:00
|
|
|
// cppcheck-suppress constVariablePointer // edited below
|
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.
|
2023-07-03 19:55:24 +02:00
|
|
|
if (varp->valuep()) visitor.mainAcceptEdit(varp->valuep());
|
|
|
|
|
} else {
|
2025-08-17 03:03:12 +02:00
|
|
|
(void)visitor.mainAcceptEdit(nodep);
|
2023-07-03 19:55:24 +02:00
|
|
|
}
|
2025-03-12 01:48:22 +01:00
|
|
|
// Because we do edits, nodep links may get trashed and core dump if have next line
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, nodep, "", "forceConDONE");
|
2023-07-03 19:55:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! Constify this cell node's parameter list if possible
|
2025-08-17 03:03:12 +02:00
|
|
|
void V3Const::constifyParamsNoWarnEdit(AstNode* nodep) {
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, nodep, "", "forceConPRE");
|
2023-07-03 19:55:24 +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);
|
|
|
|
|
ConstVisitor visitor{ConstVisitor::PROC_PARAMS_NOWARN, /* globalPass: */ false};
|
2025-08-20 19:21:24 +02:00
|
|
|
// cppcheck-suppress constVariablePointer // edited below
|
2023-07-03 19:55:24 +02:00
|
|
|
if (AstVar* const varp = VN_CAST(nodep, Var)) {
|
|
|
|
|
// If a var wants to be constified, it's really a param, and
|
|
|
|
|
// we want the value to be constant. We aren't passed just the
|
|
|
|
|
// init value because we need widthing above to handle the var's type.
|
2019-05-19 22:13:13 +02:00
|
|
|
if (varp->valuep()) visitor.mainAcceptEdit(varp->valuep());
|
2012-04-20 04:53:52 +02:00
|
|
|
} else {
|
2025-08-17 03:03:12 +02:00
|
|
|
(void)visitor.mainAcceptEdit(nodep);
|
2012-04-20 04:53:52 +02:00
|
|
|
}
|
|
|
|
|
// Because we do edits, nodep links may get trashed and core dump this.
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, nodep, "", "forceConDONE");
|
2012-04-20 04:53:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! 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.
|
2025-08-30 13:42:41 +02:00
|
|
|
void V3Const::constifyGenerateParamsEdit(AstNode* nodep) {
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, nodep, "", "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};
|
2025-08-20 19:21:24 +02:00
|
|
|
// cppcheck-suppress constVariablePointer // edited below
|
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 {
|
2025-08-30 13:42:41 +02:00
|
|
|
VL_DO_DANGLING(visitor.mainAcceptEdit(nodep), 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.
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, nodep, "", "forceConDONE");
|
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
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
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
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("const", 0, dumpTreeEitherLevel() >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2006-10-05 02:45:39 +02:00
|
|
|
void V3Const::constifyCpp(AstNetlist* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
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
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("const_cpp", 0, dumpTreeEitherLevel() >= 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
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
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
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("const", 0, dumpTreeEitherLevel() >= 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
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
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
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("const", 0, dumpTreeEitherLevel() >= 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
|
|
|
}
|