2020-05-30 22:09:05 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Merge branches/ternary ?:
|
|
|
|
|
//
|
|
|
|
|
// Code available from: https://verilator.org
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2022-01-01 14:26:40 +01:00
|
|
|
// Copyright 2003-2022 by Wilson Snyder. This program is free software; you
|
2020-05-30 22:09:05 +02:00
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
|
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
|
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3BranchMerge's Transformations:
|
|
|
|
|
//
|
|
|
|
|
// Look for sequences of assignments with ternary conditional on the right
|
|
|
|
|
// hand side with the same condition:
|
|
|
|
|
// lhs0 = cond ? then0 : else0;
|
|
|
|
|
// lhs1 = cond ? then1 : else1;
|
|
|
|
|
// lhs2 = cond ? then2 : else2;
|
|
|
|
|
//
|
|
|
|
|
// This seems to be a common pattern and can make the C compiler take a
|
|
|
|
|
// long time when compiling it with optimization. For us it's easy and fast
|
|
|
|
|
// to convert this to 'if' statements because we know the pattern is common:
|
|
|
|
|
// if (cond) {
|
|
|
|
|
// lhs0 = then0;
|
|
|
|
|
// lhs1 = then1;
|
|
|
|
|
// lhs2 = then2;
|
|
|
|
|
// } else {
|
|
|
|
|
// lhs0 = else0;
|
|
|
|
|
// lhs1 = else1;
|
|
|
|
|
// lhs2 = else2;
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// For 1-bit signals, we consider strength reduced forms to be conditionals,
|
|
|
|
|
// but only if we already encountered a true conditional we can merge with.
|
|
|
|
|
// If we did, then act as if:
|
|
|
|
|
// 'lhs = cond & value' is actually 'lhs = cond ? value : 1'd0'
|
|
|
|
|
// 'lhs = cond' is actually 'lhs = cond ? 1'd1 : 1'd0'.
|
|
|
|
|
//
|
2021-08-19 14:56:23 +02:00
|
|
|
// Also merges consecutive AstNodeIf statements with the same condition.
|
|
|
|
|
//
|
2020-05-30 22:09:05 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
|
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3MergeCond.h"
|
|
|
|
|
#include "V3Stats.h"
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
2021-08-19 14:56:23 +02:00
|
|
|
enum class Mergeable {
|
|
|
|
|
YES, // Tree can be merged
|
|
|
|
|
NO_COND_ASSIGN, // Tree cannot be merged because it contains an assignment to a condition
|
|
|
|
|
NO_IMPURE // Tree cannot be merged because it contains an impure node
|
|
|
|
|
};
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class CheckMergeableVisitor final : public AstNVisitor {
|
2020-05-30 22:09:05 +02:00
|
|
|
private:
|
|
|
|
|
// STATE
|
2021-08-19 14:56:23 +02:00
|
|
|
bool m_condAssign = false; // Does this tree contain an assignment to a condition variable??
|
|
|
|
|
bool m_impure = false; // Does this tree contain an impure node?
|
2020-05-30 22:09:05 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNode* nodep) override {
|
2021-08-19 14:56:23 +02:00
|
|
|
if (m_impure) return;
|
2020-05-30 22:09:05 +02:00
|
|
|
// Clear if node is impure
|
|
|
|
|
if (!nodep->isPure()) {
|
2021-08-19 14:56:23 +02:00
|
|
|
UINFO(9, "Not mergeable due to impure node" << nodep << endl);
|
|
|
|
|
m_impure = true;
|
2020-05-30 22:09:05 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstVarRef* nodep) override {
|
2021-08-19 14:56:23 +02:00
|
|
|
if (m_impure || m_condAssign) return;
|
2020-05-30 22:09:05 +02:00
|
|
|
// Clear if it's an LValue referencing a marked variable
|
2020-11-07 16:37:55 +01:00
|
|
|
if (nodep->access().isWriteOrRW() && nodep->varp()->user1()) {
|
2021-08-19 14:56:23 +02:00
|
|
|
UINFO(9, "Not mergeable due assignment to condition" << nodep << endl);
|
|
|
|
|
m_condAssign = true;
|
2020-05-30 22:09:05 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2020-11-17 01:56:16 +01:00
|
|
|
CheckMergeableVisitor() = default;
|
2020-05-30 22:09:05 +02:00
|
|
|
|
|
|
|
|
// Return false if this node should not be merged at all because:
|
|
|
|
|
// - It contains an impure expression
|
|
|
|
|
// - It contains an LValue referencing the condition
|
2021-08-19 14:56:23 +02:00
|
|
|
Mergeable operator()(const AstNode* node) {
|
|
|
|
|
m_condAssign = false;
|
|
|
|
|
m_impure = false;
|
|
|
|
|
iterateChildrenConst(const_cast<AstNode*>(node));
|
|
|
|
|
if (m_impure) { // Impure is stronger than cond assign
|
|
|
|
|
return Mergeable::NO_IMPURE;
|
|
|
|
|
} else if (m_condAssign) {
|
|
|
|
|
return Mergeable::NO_COND_ASSIGN;
|
|
|
|
|
} else {
|
|
|
|
|
return Mergeable::YES;
|
|
|
|
|
}
|
2020-05-30 22:09:05 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class MarkVarsVisitor final : public AstNVisitor {
|
2020-05-30 22:09:05 +02:00
|
|
|
private:
|
|
|
|
|
// METHODS
|
|
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstVarRef* nodep) override { nodep->varp()->user1(1); }
|
|
|
|
|
virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
2020-05-30 22:09:05 +02:00
|
|
|
|
|
|
|
|
public:
|
2020-07-02 13:48:50 +02:00
|
|
|
// Remove marks from AstVars (clear user1)
|
2021-07-25 19:38:27 +02:00
|
|
|
static void clear() { AstNode::user1ClearTree(); }
|
2020-07-02 13:48:50 +02:00
|
|
|
|
|
|
|
|
// Mark all AstVars referenced by setting user1
|
|
|
|
|
void mark(AstNode* node) { iterate(node); }
|
2020-05-30 22:09:05 +02:00
|
|
|
};
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class MergeCondVisitor final : public AstNVisitor {
|
2020-05-30 22:09:05 +02:00
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
2021-08-19 14:56:23 +02:00
|
|
|
// AstVar::user1 -> Flag set for variables referenced by m_mgCondp
|
|
|
|
|
// AstNode::user2 -> Flag marking node as included in merge because cheap to duplicate
|
2021-11-26 16:02:04 +01:00
|
|
|
const AstUser1InUse m_user1InUse;
|
|
|
|
|
const AstUser2InUse m_user2InUse;
|
2020-05-30 22:09:05 +02:00
|
|
|
|
|
|
|
|
// STATE
|
|
|
|
|
VDouble0 m_statMerges; // Statistic tracking
|
|
|
|
|
VDouble0 m_statMergedItems; // Statistic tracking
|
|
|
|
|
VDouble0 m_statLongestList; // Statistic tracking
|
|
|
|
|
|
2020-08-16 15:55:36 +02:00
|
|
|
AstNode* m_mgFirstp = nullptr; // First node in merged sequence
|
|
|
|
|
AstNode* m_mgCondp = nullptr; // The condition of the first node
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNode* m_mgLastp = nullptr; // Last node in merged sequence
|
2020-08-16 15:55:36 +02:00
|
|
|
const AstNode* m_mgNextp = nullptr; // Next node in list being examined
|
|
|
|
|
uint32_t m_listLenght = 0; // Length of current list
|
2020-05-30 22:09:05 +02:00
|
|
|
|
|
|
|
|
CheckMergeableVisitor m_checkMergeable; // Sub visitor for encapsulation & speed
|
|
|
|
|
MarkVarsVisitor m_markVars; // Sub visitor for encapsulation & speed
|
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
|
|
|
|
|
|
|
|
|
// This function extracts the Cond node from the RHS, if there is one and
|
|
|
|
|
// it is in a supported position, which are:
|
|
|
|
|
// - RHS is the Cond
|
|
|
|
|
// - RHS is And(Const, Cond). This And is inserted often by V3Clean.
|
2021-07-25 19:38:27 +02:00
|
|
|
static AstNodeCond* extractCond(AstNode* rhsp) {
|
2020-05-30 22:09:05 +02:00
|
|
|
if (AstNodeCond* const condp = VN_CAST(rhsp, NodeCond)) {
|
|
|
|
|
return condp;
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstAnd* const andp = VN_CAST(rhsp, And)) {
|
2020-05-30 22:09:05 +02:00
|
|
|
if (AstNodeCond* const condp = VN_CAST(andp->rhsp(), NodeCond)) {
|
2021-02-22 03:25:21 +01:00
|
|
|
if (VN_IS(andp->lhsp(), Const)) return condp;
|
2020-05-30 22:09:05 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-08-15 16:12:55 +02:00
|
|
|
return nullptr;
|
2020-05-30 22:09:05 +02:00
|
|
|
}
|
|
|
|
|
|
2021-08-19 14:56:23 +02:00
|
|
|
// Predicate to check if an expression yields only 0 or 1 (i.e.: a 1-bit value)
|
|
|
|
|
static bool yieldsOneOrZero(const AstNode* nodep) {
|
|
|
|
|
UASSERT_OBJ(!nodep->isWide(), nodep, "Cannot handle wide nodes");
|
2021-10-22 16:15:42 +02:00
|
|
|
if (const AstConst* const constp = VN_CAST(nodep, Const)) {
|
2021-08-19 14:56:23 +02:00
|
|
|
return constp->num().toUQuad() <= 1;
|
|
|
|
|
}
|
2021-10-22 16:15:42 +02:00
|
|
|
if (const AstVarRef* const vrefp = VN_CAST(nodep, VarRef)) {
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstVar* const varp = vrefp->varp();
|
2021-08-19 14:56:23 +02:00
|
|
|
return varp->widthMin() == 1 && !varp->dtypep()->isSigned();
|
|
|
|
|
}
|
2021-10-22 16:15:42 +02:00
|
|
|
if (const AstShiftR* const shiftp = VN_CAST(nodep, ShiftR)) {
|
2021-08-19 14:56:23 +02:00
|
|
|
// Shift right by width - 1 or more
|
2021-10-22 16:15:42 +02:00
|
|
|
if (const AstConst* const constp = VN_CAST(shiftp->rhsp(), Const)) {
|
|
|
|
|
const AstVarRef* const vrefp = VN_CAST(shiftp->lhsp(), VarRef);
|
2021-08-19 14:56:23 +02:00
|
|
|
const int width = vrefp && !vrefp->varp()->dtypep()->isSigned()
|
|
|
|
|
? vrefp->varp()->widthMin()
|
|
|
|
|
: shiftp->width();
|
|
|
|
|
if (constp->toSInt() >= width - 1) return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (VN_IS(nodep, Eq) || VN_IS(nodep, Neq) || VN_IS(nodep, Lt) || VN_IS(nodep, Lte)
|
|
|
|
|
|| VN_IS(nodep, Gt) || VN_IS(nodep, Gte)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2021-10-22 16:15:42 +02:00
|
|
|
if (const AstNodeBiop* const biopp = VN_CAST(nodep, NodeBiop)) {
|
2021-08-19 14:56:23 +02:00
|
|
|
if (VN_IS(nodep, And))
|
|
|
|
|
return yieldsOneOrZero(biopp->lhsp()) || yieldsOneOrZero(biopp->rhsp());
|
|
|
|
|
if (VN_IS(nodep, Or) || VN_IS(nodep, Xor))
|
|
|
|
|
return yieldsOneOrZero(biopp->lhsp()) && yieldsOneOrZero(biopp->rhsp());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-10-22 16:15:42 +02:00
|
|
|
if (const AstNodeCond* const condp = VN_CAST(nodep, NodeCond)) {
|
2021-08-19 14:56:23 +02:00
|
|
|
return yieldsOneOrZero(condp->expr1p()) && yieldsOneOrZero(condp->expr2p());
|
|
|
|
|
}
|
2021-10-22 16:15:42 +02:00
|
|
|
if (const AstCCast* const castp = VN_CAST(nodep, CCast)) {
|
2021-08-19 14:56:23 +02:00
|
|
|
// Cast never sign extends
|
|
|
|
|
return yieldsOneOrZero(castp->lhsp());
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Apply (1'b1 & _) cleaning mask if necessary. This is required because this pass is after
|
|
|
|
|
// V3Clean, and sometimes we have an AstAnd with a 1-bit condition on one side, but a more
|
|
|
|
|
// than 1-bit value on the other side, so we need to keep only the LSB.
|
2021-07-25 19:38:27 +02:00
|
|
|
static AstNode* maskLsb(AstNode* nodep) {
|
2021-08-19 14:56:23 +02:00
|
|
|
if (yieldsOneOrZero(nodep)) return nodep;
|
|
|
|
|
// Otherwise apply masking
|
2020-11-29 14:23:36 +01:00
|
|
|
AstNode* const maskp = new AstConst(nodep->fileline(), AstConst::BitTrue());
|
2021-08-19 14:56:23 +02:00
|
|
|
// Mask on left, as conventional
|
|
|
|
|
return new AstAnd(nodep->fileline(), maskp, nodep);
|
2020-05-30 22:09:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fold the RHS expression assuming the given condition state. Unlink bits
|
|
|
|
|
// from the RHS which is only used once, and can be reused. What remains
|
|
|
|
|
// of the RHS is expected to be deleted by the caller.
|
|
|
|
|
AstNode* foldAndUnlink(AstNode* rhsp, bool condTrue) {
|
|
|
|
|
if (rhsp->sameTree(m_mgCondp)) {
|
2020-11-29 17:31:38 +01:00
|
|
|
return new AstConst(rhsp->fileline(), AstConst::BitTrue{}, condTrue);
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstNodeCond* const condp = extractCond(rhsp)) {
|
2020-05-30 22:09:05 +02:00
|
|
|
AstNode* const resp
|
|
|
|
|
= condTrue ? condp->expr1p()->unlinkFrBack() : condp->expr2p()->unlinkFrBack();
|
2021-02-22 03:25:21 +01:00
|
|
|
if (condp == rhsp) { //
|
|
|
|
|
return resp;
|
|
|
|
|
}
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstAnd* const andp = VN_CAST(rhsp, And)) {
|
2020-05-30 22:09:05 +02:00
|
|
|
UASSERT_OBJ(andp->rhsp() == condp, rhsp, "Should not try to fold this");
|
2021-07-12 00:42:01 +02:00
|
|
|
return new AstAnd{andp->fileline(), andp->lhsp()->cloneTree(false), resp};
|
2020-05-30 22:09:05 +02:00
|
|
|
}
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstAnd* const andp = VN_CAST(rhsp, And)) {
|
2020-05-30 22:09:05 +02:00
|
|
|
if (andp->lhsp()->sameTree(m_mgCondp)) {
|
|
|
|
|
return condTrue ? maskLsb(andp->rhsp()->unlinkFrBack())
|
2021-07-12 00:42:01 +02:00
|
|
|
: new AstConst{rhsp->fileline(), AstConst::BitFalse()};
|
2020-05-30 22:09:05 +02:00
|
|
|
} else {
|
|
|
|
|
UASSERT_OBJ(andp->rhsp()->sameTree(m_mgCondp), rhsp,
|
|
|
|
|
"AstAnd doesn't hold condition expression");
|
|
|
|
|
return condTrue ? maskLsb(andp->lhsp()->unlinkFrBack())
|
2021-07-12 00:42:01 +02:00
|
|
|
: new AstConst{rhsp->fileline(), AstConst::BitFalse()};
|
2020-05-30 22:09:05 +02:00
|
|
|
}
|
2021-08-19 14:56:23 +02:00
|
|
|
} else if (VN_IS(rhsp, WordSel) || VN_IS(rhsp, VarRef) || VN_IS(rhsp, Const)) {
|
|
|
|
|
return rhsp->cloneTree(false);
|
2020-05-30 22:09:05 +02:00
|
|
|
}
|
2021-08-19 14:56:23 +02:00
|
|
|
rhsp->dumpTree("Don't know how to fold expression: ");
|
2020-06-05 01:49:18 +02:00
|
|
|
rhsp->v3fatalSrc("Don't know how to fold expression");
|
2020-05-30 22:09:05 +02:00
|
|
|
}
|
|
|
|
|
|
2021-08-19 14:56:23 +02:00
|
|
|
void mergeEnd(int lineno) {
|
|
|
|
|
UASSERT(m_mgFirstp, "mergeEnd without list " << lineno);
|
|
|
|
|
// We might want to recursively merge an AstIf. We stash it in this variable.
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNodeIf* recursivep = nullptr;
|
2021-08-19 14:56:23 +02:00
|
|
|
// Drop leading cheap nodes. These were only added in the hope of finding
|
|
|
|
|
// an earlier reduced form, but we failed to do so.
|
|
|
|
|
while (m_mgFirstp->user2() && m_mgFirstp != m_mgLastp) {
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNode* const backp = m_mgFirstp;
|
2021-08-19 14:56:23 +02:00
|
|
|
m_mgFirstp = m_mgFirstp->nextp();
|
|
|
|
|
--m_listLenght;
|
|
|
|
|
UASSERT_OBJ(m_mgFirstp && m_mgFirstp->backp() == backp, m_mgLastp,
|
|
|
|
|
"The list should have a non-cheap element");
|
|
|
|
|
}
|
|
|
|
|
// Drop trailing cheap nodes. These were only added in the hope of finding
|
|
|
|
|
// a later conditional to merge, but we failed to do so.
|
|
|
|
|
while (m_mgLastp->user2() && m_mgFirstp != m_mgLastp) {
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNode* const nextp = m_mgLastp;
|
2021-08-19 14:56:23 +02:00
|
|
|
m_mgLastp = m_mgLastp->backp();
|
|
|
|
|
--m_listLenght;
|
|
|
|
|
UASSERT_OBJ(m_mgLastp && m_mgLastp->nextp() == nextp, m_mgFirstp,
|
|
|
|
|
"Cheap assignment should not be at the front of the list");
|
|
|
|
|
}
|
2020-05-30 22:09:05 +02:00
|
|
|
// Merge if list is longer than one node
|
|
|
|
|
if (m_mgFirstp != m_mgLastp) {
|
|
|
|
|
UINFO(6, "MergeCond - First: " << m_mgFirstp << " Last: " << m_mgLastp << endl);
|
|
|
|
|
++m_statMerges;
|
|
|
|
|
if (m_listLenght > m_statLongestList) m_statLongestList = m_listLenght;
|
|
|
|
|
|
2021-08-19 14:56:23 +02:00
|
|
|
// We need a copy of the condition in the new equivalent 'if' statement,
|
|
|
|
|
// and we also need to keep track of it for comparisons later.
|
|
|
|
|
m_mgCondp = m_mgCondp->cloneTree(false);
|
2020-05-30 22:09:05 +02:00
|
|
|
// Create equivalent 'if' statement and insert it before the first node
|
2021-08-19 14:56:23 +02:00
|
|
|
AstIf* const resultp = new AstIf(m_mgCondp->fileline(), m_mgCondp, nullptr, nullptr);
|
|
|
|
|
m_mgFirstp->addHereThisAsNext(resultp);
|
2020-05-30 22:09:05 +02:00
|
|
|
// Unzip the list and insert under branches
|
|
|
|
|
AstNode* nextp = m_mgFirstp;
|
|
|
|
|
do {
|
|
|
|
|
// Grab next pointer and unlink
|
|
|
|
|
AstNode* const currp = nextp;
|
2020-08-15 16:12:55 +02:00
|
|
|
nextp = currp != m_mgLastp ? currp->nextp() : nullptr;
|
2020-05-30 22:09:05 +02:00
|
|
|
currp->unlinkFrBack();
|
|
|
|
|
// Skip over comments
|
|
|
|
|
if (VN_IS(currp, Comment)) {
|
|
|
|
|
VL_DO_DANGLING(currp->deleteTree(), currp);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// Count
|
|
|
|
|
++m_statMergedItems;
|
2021-08-19 14:56:23 +02:00
|
|
|
if (AstNodeAssign* const assignp = VN_CAST(currp, NodeAssign)) {
|
|
|
|
|
// Unlink RHS and clone to get the 2 assignments (reusing assignp)
|
|
|
|
|
AstNode* const rhsp = assignp->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeAssign* const thenp = assignp;
|
|
|
|
|
AstNodeAssign* const elsep = assignp->cloneTree(false);
|
|
|
|
|
// Construct the new RHSs and add to branches
|
|
|
|
|
thenp->rhsp(foldAndUnlink(rhsp, true));
|
|
|
|
|
elsep->rhsp(foldAndUnlink(rhsp, false));
|
|
|
|
|
resultp->addIfsp(thenp);
|
|
|
|
|
resultp->addElsesp(elsep);
|
|
|
|
|
// Cleanup
|
|
|
|
|
VL_DO_DANGLING(rhsp->deleteTree(), rhsp);
|
|
|
|
|
} else {
|
2021-10-22 14:56:48 +02:00
|
|
|
AstNodeIf* const ifp = VN_AS(currp, NodeIf);
|
2021-08-19 14:56:23 +02:00
|
|
|
UASSERT_OBJ(ifp, currp, "Must be AstNodeIf");
|
|
|
|
|
// Move branch contents under new if
|
|
|
|
|
if (AstNode* const listp = ifp->ifsp()) {
|
|
|
|
|
resultp->addIfsp(listp->unlinkFrBackWithNext());
|
|
|
|
|
}
|
|
|
|
|
if (AstNode* const listp = ifp->elsesp()) {
|
|
|
|
|
resultp->addElsesp(listp->unlinkFrBackWithNext());
|
|
|
|
|
}
|
|
|
|
|
// Cleanup
|
|
|
|
|
VL_DO_DANGLING(ifp->deleteTree(), ifp);
|
|
|
|
|
}
|
2020-05-30 22:09:05 +02:00
|
|
|
} while (nextp);
|
2021-08-19 14:56:23 +02:00
|
|
|
// Recursively merge the resulting AstIf
|
|
|
|
|
recursivep = resultp;
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstNodeIf* const ifp = VN_CAST(m_mgFirstp, NodeIf)) {
|
2021-08-19 14:56:23 +02:00
|
|
|
// There was nothing to merge this AstNodeIf with, but try to merge it's branches
|
|
|
|
|
recursivep = ifp;
|
2020-05-30 22:09:05 +02:00
|
|
|
}
|
|
|
|
|
// Reset state
|
2020-08-15 16:12:55 +02:00
|
|
|
m_mgFirstp = nullptr;
|
|
|
|
|
m_mgCondp = nullptr;
|
|
|
|
|
m_mgLastp = nullptr;
|
|
|
|
|
m_mgNextp = nullptr;
|
2020-07-02 13:48:50 +02:00
|
|
|
m_markVars.clear();
|
2021-08-19 14:56:23 +02:00
|
|
|
AstNode::user2ClearTree();
|
|
|
|
|
// Merge recursively within the branches
|
|
|
|
|
if (recursivep) {
|
|
|
|
|
iterateAndNextNull(recursivep->ifsp());
|
|
|
|
|
// Close list, if there is one at the end of the then branch
|
|
|
|
|
if (m_mgFirstp) mergeEnd(__LINE__);
|
|
|
|
|
iterateAndNextNull(recursivep->elsesp());
|
|
|
|
|
// Close list, if there is one at the end of the else branch
|
|
|
|
|
if (m_mgFirstp) mergeEnd(__LINE__);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if the node can be simplified if included under the if
|
|
|
|
|
bool isSimplifiableNode(AstNode* nodep) {
|
|
|
|
|
UASSERT_OBJ(m_mgFirstp, nodep, "Cannot check with empty list");
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstNodeAssign* const assignp = VN_CAST(nodep, NodeAssign)) {
|
2021-08-19 14:56:23 +02:00
|
|
|
// If it's an assignment to a 1-bit signal, try reduced forms
|
|
|
|
|
if (assignp->lhsp()->widthMin() == 1) {
|
|
|
|
|
// Is it a 'lhs = cond & value' or 'lhs = value & cond'?
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstAnd* const andp = VN_CAST(assignp->rhsp(), And)) {
|
2021-08-19 14:56:23 +02:00
|
|
|
if (andp->lhsp()->sameTree(m_mgCondp) || andp->rhsp()->sameTree(m_mgCondp)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Is it simply 'lhs = cond'?
|
|
|
|
|
if (assignp->rhsp()->sameTree(m_mgCondp)) return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if this node is cheap enough that duplicating it in two branches of an
|
|
|
|
|
// AstIf and is hence not likely to cause a performance degradation if doing so.
|
|
|
|
|
bool isCheapNode(AstNode* nodep) const {
|
|
|
|
|
if (VN_IS(nodep, Comment)) return true;
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstNodeAssign* const assignp = VN_CAST(nodep, NodeAssign)) {
|
2021-08-19 14:56:23 +02:00
|
|
|
// Check LHS
|
|
|
|
|
AstNode* lhsp = assignp->lhsp();
|
|
|
|
|
while (AstWordSel* const wselp = VN_CAST(lhsp, WordSel)) {
|
|
|
|
|
// WordSel index is not constant, so might be expensive
|
|
|
|
|
if (!VN_IS(wselp->bitp(), Const)) return false;
|
|
|
|
|
lhsp = wselp->fromp();
|
|
|
|
|
}
|
|
|
|
|
// LHS is not a VarRef, so might be expensive
|
|
|
|
|
if (!VN_IS(lhsp, VarRef)) return false;
|
|
|
|
|
|
|
|
|
|
// Check RHS
|
|
|
|
|
AstNode* rhsp = assignp->rhsp();
|
|
|
|
|
while (AstWordSel* const wselp = VN_CAST(rhsp, WordSel)) {
|
|
|
|
|
// WordSel index is not constant, so might be expensive
|
|
|
|
|
if (!VN_IS(wselp->bitp(), Const)) return false;
|
|
|
|
|
rhsp = wselp->fromp();
|
|
|
|
|
}
|
|
|
|
|
// RHS is not a VarRef or Constant so might be expensive
|
|
|
|
|
if (!VN_IS(rhsp, VarRef) && !VN_IS(rhsp, Const)) return false;
|
|
|
|
|
|
|
|
|
|
// Otherwise it is a cheap assignment
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2020-05-30 22:09:05 +02:00
|
|
|
}
|
|
|
|
|
|
2021-08-19 14:56:23 +02:00
|
|
|
void addToList(AstNode* nodep, AstNode* condp, int line) {
|
2020-05-30 22:09:05 +02:00
|
|
|
// Set up head of new list if node is first in list
|
|
|
|
|
if (!m_mgFirstp) {
|
2021-08-19 14:56:23 +02:00
|
|
|
UASSERT_OBJ(condp, nodep, "Cannot start new list without condition " << line);
|
2020-05-30 22:09:05 +02:00
|
|
|
m_mgFirstp = nodep;
|
|
|
|
|
m_mgCondp = condp;
|
|
|
|
|
m_listLenght = 0;
|
2020-07-02 13:48:50 +02:00
|
|
|
m_markVars.mark(condp);
|
2021-08-19 14:56:23 +02:00
|
|
|
// Add any preceding nodes to the list that would allow us to extend the merge range
|
|
|
|
|
for (;;) {
|
|
|
|
|
AstNode* const backp = m_mgFirstp->backp();
|
|
|
|
|
if (!backp || backp->nextp() != m_mgFirstp) break; // Don't move up the tree
|
|
|
|
|
if (m_checkMergeable(backp) != Mergeable::YES) break;
|
|
|
|
|
if (isSimplifiableNode(backp)) {
|
|
|
|
|
++m_listLenght;
|
|
|
|
|
m_mgFirstp = backp;
|
|
|
|
|
} else if (isCheapNode(backp)) {
|
|
|
|
|
backp->user2(1);
|
|
|
|
|
++m_listLenght;
|
|
|
|
|
m_mgFirstp = backp;
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-30 22:09:05 +02:00
|
|
|
}
|
|
|
|
|
// Add node
|
|
|
|
|
++m_listLenght;
|
|
|
|
|
// Track end of list
|
|
|
|
|
m_mgLastp = nodep;
|
2021-08-19 14:56:23 +02:00
|
|
|
// Set up expected next node in list.
|
2020-05-30 22:09:05 +02:00
|
|
|
m_mgNextp = nodep->nextp();
|
|
|
|
|
// If last under parent, done with current list
|
2021-08-19 14:56:23 +02:00
|
|
|
if (!m_mgNextp) mergeEnd(__LINE__);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If this node is the next expected node and is helpful to add to the list, do so,
|
|
|
|
|
// otherwise end the current merge. Return ture if added, false if ended merge.
|
|
|
|
|
bool addIfHelpfulElseEndMerge(AstNode* nodep) {
|
|
|
|
|
UASSERT_OBJ(m_mgFirstp, nodep, "List must be open");
|
|
|
|
|
if (m_mgNextp == nodep) {
|
|
|
|
|
if (isSimplifiableNode(nodep)) {
|
|
|
|
|
addToList(nodep, nullptr, __LINE__);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (isCheapNode(nodep)) {
|
|
|
|
|
nodep->user2(1);
|
|
|
|
|
addToList(nodep, nullptr, __LINE__);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Not added to list, so we are done with the current list
|
|
|
|
|
mergeEnd(__LINE__);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool checkOrMakeMergeable(AstNode* nodep) {
|
|
|
|
|
const Mergeable reason = m_checkMergeable(nodep);
|
|
|
|
|
// If meregeable, we are done
|
|
|
|
|
if (reason == Mergeable::YES) return true;
|
|
|
|
|
// Node not mergeable.
|
|
|
|
|
// If no current list, then this node is just special, move on.
|
|
|
|
|
if (!m_mgFirstp) return false;
|
|
|
|
|
// Otherwise finish current list
|
|
|
|
|
mergeEnd(__LINE__);
|
|
|
|
|
// If a tree was not mergeable due to an assignment to a condition,
|
|
|
|
|
// then finishing the current list makes it mergeable again.
|
|
|
|
|
return reason == Mergeable::NO_COND_ASSIGN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void mergeEndIfIncompatible(AstNode* nodep, AstNode* condp) {
|
|
|
|
|
if (m_mgFirstp && (m_mgNextp != nodep || !condp->sameTree(m_mgCondp))) {
|
|
|
|
|
// Node in different list, or has different condition. Finish current list.
|
|
|
|
|
mergeEnd(__LINE__);
|
|
|
|
|
}
|
2020-05-30 22:09:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNodeAssign* nodep) override {
|
2020-05-30 22:09:05 +02:00
|
|
|
AstNode* const rhsp = nodep->rhsp();
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstNodeCond* const condp = extractCond(rhsp)) {
|
2021-08-19 14:56:23 +02:00
|
|
|
// Check if mergeable
|
|
|
|
|
if (!checkOrMakeMergeable(nodep)) return;
|
|
|
|
|
// Close potentially incompatible pending merge
|
|
|
|
|
mergeEndIfIncompatible(nodep, condp->condp());
|
2020-05-30 22:09:05 +02:00
|
|
|
// Add current node
|
2021-08-19 14:56:23 +02:00
|
|
|
addToList(nodep, condp->condp(), __LINE__);
|
2020-05-30 22:09:05 +02:00
|
|
|
} else if (m_mgFirstp) {
|
2021-08-19 14:56:23 +02:00
|
|
|
addIfHelpfulElseEndMerge(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void visit(AstNodeIf* nodep) override {
|
|
|
|
|
// Check if mergeable
|
|
|
|
|
if (!checkOrMakeMergeable(nodep)) {
|
|
|
|
|
// If not mergeable, try to merge the branches
|
|
|
|
|
iterateAndNextNull(nodep->ifsp());
|
|
|
|
|
iterateAndNextNull(nodep->elsesp());
|
|
|
|
|
return;
|
2020-05-30 22:09:05 +02:00
|
|
|
}
|
2021-08-19 14:56:23 +02:00
|
|
|
// Close potentially incompatible pending merge
|
|
|
|
|
mergeEndIfIncompatible(nodep, nodep->condp());
|
|
|
|
|
// Add current node
|
|
|
|
|
addToList(nodep, nodep->condp(), __LINE__);
|
2020-05-30 22:09:05 +02:00
|
|
|
}
|
2021-08-19 14:56:23 +02:00
|
|
|
|
2020-05-30 22:09:05 +02:00
|
|
|
// For speed, only iterate what is necessary.
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNetlist* nodep) override { iterateAndNextNull(nodep->modulesp()); }
|
|
|
|
|
virtual void visit(AstNodeModule* nodep) override { iterateAndNextNull(nodep->stmtsp()); }
|
|
|
|
|
virtual void visit(AstCFunc* nodep) override {
|
2020-05-30 22:09:05 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
// Close list, if there is one at the end of the function
|
2021-08-19 14:56:23 +02:00
|
|
|
if (m_mgFirstp) mergeEnd(__LINE__);
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNodeStmt* nodep) override {
|
|
|
|
|
if (m_mgFirstp && addIfHelpfulElseEndMerge(nodep)) return;
|
|
|
|
|
iterateChildren(nodep);
|
2020-05-30 22:09:05 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNode* nodep) override {}
|
2020-05-30 22:09:05 +02:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
|
|
|
|
explicit MergeCondVisitor(AstNetlist* nodep) {
|
2020-07-02 13:48:50 +02:00
|
|
|
m_markVars.clear();
|
2020-05-30 22:09:05 +02:00
|
|
|
iterate(nodep);
|
|
|
|
|
}
|
2020-08-15 17:44:10 +02:00
|
|
|
virtual ~MergeCondVisitor() override {
|
2020-05-30 22:09:05 +02:00
|
|
|
V3Stats::addStat("Optimizations, MergeCond merges", m_statMerges);
|
|
|
|
|
V3Stats::addStat("Optimizations, MergeCond merged items", m_statMergedItems);
|
|
|
|
|
V3Stats::addStat("Optimizations, MergeCond longest merge", m_statLongestList);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// MergeConditionals class functions
|
|
|
|
|
|
|
|
|
|
void V3MergeCond::mergeAll(AstNetlist* nodep) {
|
|
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2021-11-26 16:52:36 +01:00
|
|
|
{ MergeCondVisitor{nodep}; }
|
2020-05-30 22:09:05 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("merge_cond", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
|
|
|
|
|
}
|