From ab0264deca151f6cab0c9f8549777ed6362559f4 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 14 Apr 2026 16:22:54 +0100 Subject: [PATCH] Optimize more Dfg peephole patterns (#7423) --- src/V3DfgPeephole.cpp | 907 +++++++++++++----------- src/V3DfgPeepholePatterns.h | 24 +- test_regress/t/t_dfg_peephole.v | 17 +- test_regress/t/t_dfg_stats_patterns.out | 4 +- test_regress/t/t_opt_const_dfg.py | 2 +- 5 files changed, 546 insertions(+), 408 deletions(-) diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index 6acff64b7..62ad31269 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -41,6 +41,7 @@ #include "V3Ast.h" #include "V3Dfg.h" #include "V3DfgCache.h" +#include "V3DfgDataType.h" #include "V3DfgPasses.h" #include "V3DfgPeepholePatterns.h" @@ -51,6 +52,8 @@ VL_DEFINE_DEBUG_FUNCTIONS; // clang-format off +namespace { + template struct ReductionToBitwiseImpl {}; template <> struct ReductionToBitwiseImpl { using type = DfgAnd; }; @@ -67,7 +70,74 @@ template <> struct BitwiseToReductionImpl { using type = DfgRedXor; }; template using BitwiseToReduction = typename BitwiseToReductionImpl::type; -namespace { +// Associative binary operators (a op (b op c) == (a op b) op c) +template +struct IsAssociative : public std::false_type {}; +template <> struct IsAssociative : public std::true_type {}; +template <> struct IsAssociative : public std::true_type {}; +template <> struct IsAssociative : public std::true_type {}; +template <> struct IsAssociative : public std::true_type {}; +template <> struct IsAssociative : public std::true_type {}; +template <> struct IsAssociative : public std::true_type {}; +template <> struct IsAssociative : public std::true_type {}; + +// Commutative binary operators (a op b == b op a) +template +struct IsCommutative : public std::false_type {}; +template <> struct IsCommutative : public std::true_type {}; +template <> struct IsCommutative : public std::true_type {}; +template <> struct IsCommutative : public std::true_type {}; +template <> struct IsCommutative : public std::true_type {}; +template <> struct IsCommutative : public std::true_type {}; +template <> struct IsCommutative : public std::true_type {}; +template <> struct IsCommutative : public std::true_type {}; +template <> struct IsCommutative : public std::true_type {}; + +// Idempotent binary operators (a op a == a) +template +struct IsIdempotent : public std::false_type {}; +template <> struct IsIdempotent : public std::true_type {}; +template <> struct IsIdempotent : public std::true_type {}; + +// Binary result dtype +template +const DfgDataType& resultDType(const DfgVertex* lhsp, const DfgVertex* rhsp); +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return lhsp->dtype(); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return lhsp->dtype(); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return DfgDataType::packed(lhsp->width() + rhsp->width()); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return lhsp->dtype(); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return lhsp->dtype(); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return DfgDataType::packed(1); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return DfgDataType::packed(1); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return DfgDataType::packed(1); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return DfgDataType::packed(1); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return DfgDataType::packed(1); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return DfgDataType::packed(1); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return DfgDataType::packed(1); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return DfgDataType::packed(1); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return DfgDataType::packed(1); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return DfgDataType::packed(1); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return DfgDataType::packed(1); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return DfgDataType::packed(1); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return DfgDataType::packed(1); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return lhsp->dtype(); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return lhsp->dtype(); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return lhsp->dtype(); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return lhsp->dtype(); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return DfgDataType::packed(1); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return lhsp->dtype(); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return lhsp->dtype(); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return lhsp->dtype(); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return lhsp->dtype(); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return lhsp->dtype(); } +// template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return ; } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return lhsp->dtype(); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return lhsp->dtype(); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return lhsp->dtype(); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return lhsp->dtype(); } +template <> const DfgDataType& resultDType (const DfgVertex* lhsp, const DfgVertex* rhsp) { return lhsp->dtype(); } + +// Unary constant folding template void foldOp(V3Number& out, const V3Number& src); template <> void foldOp (V3Number& out, const V3Number& src) { out.opAssign(src); } template <> void foldOp (V3Number& out, const V3Number& src) { out.opExtendS(src, src.width()); } @@ -78,6 +148,7 @@ template <> void foldOp (V3Number& out, const V3Number& src) { ou template <> void foldOp (V3Number& out, const V3Number& src) { out.opRedOr(src); } template <> void foldOp (V3Number& out, const V3Number& src) { out.opRedXor(src); } +// Binary constant folding template void foldOp(V3Number& out, const V3Number& lhs, const V3Number& rhs); template <> void foldOp (V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opAdd(lhs, rhs); } template <> void foldOp (V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opAnd(lhs, rhs); } @@ -299,6 +370,9 @@ class V3DfgPeephole final : public DfgVisitor { DfgConst* makeZero(FileLine* flp, uint32_t width) { return new DfgConst{m_dfg, flp, width, 0}; } + DfgConst* makeZero(FileLine* flp, const DfgDataType& dtype) { + return makeZero(flp, dtype.size()); + } // Create a DfgConst vertex with the given width and value all ones DfgConst* makeOnes(FileLine* flp, uint32_t width) { @@ -389,128 +463,116 @@ class V3DfgPeephole final : public DfgVisitor { return false; } - // Constant fold binary vertex, return true if folded + // Generic transformations that apply to binary vertices. Returns true if vtxp was replaced. template - VL_ATTR_WARN_UNUSED_RESULT bool foldBinary(Vertex* const vtxp) { - static_assert(std::is_base_of::value, "Must invoke on binary"); - static_assert(std::is_final::value, "Must invoke on final class"); - if (DfgConst* const lhsp = vtxp->inputp(0)->template cast()) { - if (DfgConst* const rhsp = vtxp->inputp(1)->template cast()) { - APPLYING(FOLD_BINARY) { - DfgConst* const resultp = makeZero(vtxp->fileline(), vtxp->width()); - foldOp(resultp->num(), lhsp->num(), rhsp->num()); - replace(resultp); - return true; - } - } - } - return false; - } - - // Transformations that apply to all associative binary vertices. - // Returns true if vtxp was replaced. - template - VL_ATTR_WARN_UNUSED_RESULT bool associativeBinary(Vertex* const vtxp) { + VL_ATTR_WARN_UNUSED_RESULT bool binary(Vertex* const vtxp) { static_assert(std::is_base_of::value, "Must invoke on binary"); static_assert(std::is_final::value, "Must invoke on final class"); + FileLine* const flp = vtxp->fileline(); + // LHS/RHS of the vertex DfgVertex* const lhsp = vtxp->lhsp(); DfgVertex* const rhsp = vtxp->rhsp(); - FileLine* const flp = vtxp->fileline(); - + // LHS/RHS as constant DfgConst* const lConstp = lhsp->cast(); DfgConst* const rConstp = rhsp->cast(); + // LHS/RHS as same vertex type + Vertex* const lSamep = lhsp->cast(); + Vertex* const rSamep = rhsp->cast(); + // Constant folding if (lConstp && rConstp) { - APPLYING(FOLD_ASSOC_BINARY) { - DfgConst* const resultp = makeZero(flp, vtxp->width()); + APPLYING(FOLD_BINARY) { + DfgConst* const resultp = makeZero(flp, resultDType(lhsp, rhsp)); foldOp(resultp->num(), lConstp->num(), rConstp->num()); replace(resultp); return true; } } - if (lConstp) { - if (Vertex* const rVtxp = rhsp->cast()) { - if (DfgConst* const rlConstp = rVtxp->lhsp()->template cast()) { + // Idempotent + if VL_CONSTEXPR_CXX17 (IsIdempotent::value) { + if (isSame(lhsp, rhsp)) { + APPLYING(REMOVE_IDEMPOTENT_BINARY) { + replace(lhsp); + return true; + } + } + } + + // Associative + if VL_CONSTEXPR_CXX17 (IsAssociative::value) { + // Also commutative and idempotent + if VL_CONSTEXPR_CXX17 (IsCommutative::value && IsIdempotent::value) { + if (lSamep && (isSame(lSamep->lhsp(), rhsp) || isSame(lSamep->rhsp(), rhsp))) { + APPLYING(REMOVE_ACI_BINARY_LHS) { + replace(lSamep); + return true; + } + } + if (rSamep && (isSame(lhsp, rSamep->lhsp()) || isSame(lhsp, rSamep->rhsp()))) { + APPLYING(REMOVE_ACI_BINARY_RHS) { + replace(rSamep); + return true; + } + } + } + + if (lConstp && rSamep) { + if (DfgConst* const rlConstp = rSamep->lhsp()->template cast()) { + // TODO: Maybe only if !rSamep->hasMultipleSinks() APPLYING(FOLD_ASSOC_BINARY_LHS_OF_RHS) { - // Fold constants - const uint32_t width = std::is_same::value - ? lConstp->width() + rlConstp->width() - : vtxp->width(); - DfgConst* const constp = makeZero(flp, width); - foldOp(constp->num(), lConstp->num(), rlConstp->num()); - - // Replace vertex - replace(make(vtxp, constp, rVtxp->rhsp())); + DfgConst* const cp = makeZero(flp, resultDType(lConstp, rlConstp)); + foldOp(cp->num(), lConstp->num(), rlConstp->num()); + replace(make(vtxp, cp, rSamep->rhsp())); return true; } } } - } - if (rConstp) { - if (Vertex* const lVtxp = lhsp->cast()) { - if (DfgConst* const lrConstp = lVtxp->rhsp()->template cast()) { + if (lSamep && rConstp) { + if (DfgConst* const lrConstp = lSamep->rhsp()->template cast()) { + // TODO: Maybe only if !lSameps->hasMultipleSinks() APPLYING(FOLD_ASSOC_BINARY_RHS_OF_LHS) { - // Fold constants - const uint32_t width = std::is_same::value - ? lrConstp->width() + rConstp->width() - : vtxp->width(); - DfgConst* const constp = makeZero(flp, width); - foldOp(constp->num(), lrConstp->num(), rConstp->num()); - - // Replace vertex - replace(make(vtxp, lVtxp->lhsp(), constp)); + DfgConst* const cp = makeZero(flp, resultDType(lrConstp, rConstp)); + foldOp(cp->num(), lrConstp->num(), rConstp->num()); + replace(make(vtxp, lSamep->lhsp(), cp)); return true; } } } - } - // Make associative trees right leaning to reduce pattern variations, and for better CSE - if (Vertex* const alhsp = vtxp->lhsp()->template cast()) { - if (!alhsp->hasMultipleSinks()) { - DfgVertex* const ap = alhsp->lhsp(); - DfgVertex* const bp = alhsp->rhsp(); - DfgVertex* const cp = vtxp->rhsp(); - // Only do this if the rhs is not th same as the operands of the LHS, otherwise + // Make associative trees right leaning to reduce variations + if (lSamep && !lSamep->hasMultipleSinks()) { + DfgVertex* const ap = lSamep->lhsp(); + DfgVertex* const bp = lSamep->rhsp(); + DfgVertex* const cp = rhsp; + // Only do this if the rhs is not the same as the operands of the LHS, otherwise // SWAP_SIDE_IN_COMMUTATIVE_BINARY can get in a loop with this pattern. if (ap != cp && bp != cp) { APPLYING(RIGHT_LEANING_ASSOC) { // Rotate the expression tree rooted at 'vtxp' to the right, // producing a right-leaning tree - - // Concatenation dtypes need adjusting, other assoc vertices preserve types - const DfgDataType& childDType - = std::is_same::value - ? DfgDataType::packed(bp->width() + cp->width()) - : vtxp->dtype(); - + const DfgDataType& childDType = resultDType(bp, cp); Vertex* const bcp = make(vtxp->fileline(), childDType, bp, cp); - replace(make(alhsp->fileline(), vtxp->dtype(), ap, bcp)); + replace(make(lSamep->fileline(), vtxp->dtype(), ap, bcp)); return true; } } } - } - // Attempt to reuse associative binary expressions if hey already exist, e.g.: - // '(a OP (b OP c))' -> '(a OP b) OP c', iff '(a OP b)' already exists, or - // '(a OP c) OP b' iff '(a OP c)' already exists and the vertex is commutative. - // Only do this is 'b OP c' has a single use and can subsequently be removed, - // otherwise there is no improvement. - if (!rhsp->hasMultipleSinks()) { - if (Vertex* rVtxp = rhsp->template cast()) { - DfgVertex* const rlVtxp = rVtxp->lhsp(); - DfgVertex* const rrVtxp = rVtxp->rhsp(); + // Attempt to reuse associative binary expressions if hey already exist, e.g.: + // '(a OP (b OP c))' -> '(a OP b) OP c', iff '(a OP b)' already exists, or + // '(a OP c) OP b' iff '(a OP c)' already exists and the vertex is commutative. + // Only do this is 'b OP c' has a single use and can subsequently be removed, + // otherwise there is no improvement. + if (rSamep && !rSamep->hasMultipleSinks()) { + DfgVertex* const rlVtxp = rSamep->lhsp(); + DfgVertex* const rrVtxp = rSamep->rhsp(); - const DfgDataType& dtype - = std::is_same::value - ? DfgDataType::packed(lhsp->width() + rlVtxp->width()) - : vtxp->dtype(); - - if (Vertex* const existingp = m_cache.get(dtype, lhsp, rlVtxp)) { + // '(a OP (b OP c))' -> '(a OP b) OP c' + if (Vertex* const existingp + = m_cache.get(resultDType(lhsp, rlVtxp), lhsp, rlVtxp)) { UASSERT_OBJ(existingp->hasSinks(), vtxp, "Existing vertex should be used"); if (existingp != rhsp) { APPLYING(REUSE_ASSOC_BINARY_LHS_WITH_LHS_OF_RHS) { @@ -519,9 +581,11 @@ class V3DfgPeephole final : public DfgVisitor { } } } - // Concat is not commutative - if VL_CONSTEXPR_CXX17 (!std::is_same::value) { - if (Vertex* const existingp = m_cache.get(dtype, lhsp, rrVtxp)) { + + // '(a OP (b OP c))' -> '(a OP c) OP b' iff also commutative + if VL_CONSTEXPR_CXX17 (IsCommutative::value) { + if (Vertex* const existingp + = m_cache.get(resultDType(lhsp, rrVtxp), lhsp, rrVtxp)) { UASSERT_OBJ(existingp->hasSinks(), vtxp, "Existing vertex should be used"); if (existingp != rhsp) { APPLYING(REUSE_ASSOC_BINARY_LHS_WITH_RHS_OF_RHS) { @@ -534,88 +598,47 @@ class V3DfgPeephole final : public DfgVisitor { } } - return false; - } - - // Transformations that apply to all commutative binary vertices - template - VL_ATTR_WARN_UNUSED_RESULT bool commutativeBinary(Vertex* const vtxp) { - static_assert(std::is_base_of::value, "Must invoke on binary"); - static_assert(std::is_final::value, "Must invoke on final class"); - - DfgVertex* const lhsp = vtxp->lhsp(); - DfgVertex* const rhsp = vtxp->rhsp(); - - // If constant LHS, push through cond if used once. (Enables branch combining) - if (DfgConst* const lConstp = lhsp->cast()) { - if (DfgCond* const rCondp = rhsp->cast()) { - if (!rCondp->hasMultipleSinks()) { - APPLYING(PUSH_COMMUTATIVE_BINARY_THROUGH_COND) { - DfgVertex* const tp = make(vtxp, lConstp, rCondp->thenp()); - DfgVertex* const ep = make(vtxp, lConstp, rCondp->elsep()); - replace(make(vtxp, rCondp->condp(), tp, ep)); - return true; - } - } - } - return false; - } - - // Ensure Const is on left-hand side to simplify other patterns - { - const bool lIsConst = lhsp->is(); - const bool rIsConst = rhsp->is(); - if (lIsConst != rIsConst) { - if (rIsConst) { - APPLYING(SWAP_CONST_IN_COMMUTATIVE_BINARY) { - replace(make(vtxp, rhsp, lhsp)); - return true; + // Commutative + if VL_CONSTEXPR_CXX17 (IsCommutative::value) { + // If constant LHS, push through cond if used once. (Enables branch combining) + if (lConstp) { + if (DfgCond* const rCondp = rhsp->cast()) { + if (!rCondp->hasMultipleSinks()) { + APPLYING(PUSH_COMMUTATIVE_BINARY_THROUGH_COND) { + DfgVertex* const tp = make(vtxp, lConstp, rCondp->thenp()); + DfgVertex* const ep = make(vtxp, lConstp, rCondp->elsep()); + replace(make(vtxp, rCondp->condp(), tp, ep)); + return true; + } } } return false; } } - // Ensure Not is on the left-hand side to simplify other patterns - { - const bool lIsNot = lhsp->is(); - const bool rIsNot = rhsp->is(); - if (lIsNot != rIsNot) { - if (rIsNot) { - APPLYING(SWAP_NOT_IN_COMMUTATIVE_BINARY) { - replace(make(vtxp, rhsp, lhsp)); - return true; - } - } - return false; - } - } - - // Ensure same vertex is on the right-hand side to simplify other patterns - { - const bool lIsSame = lhsp->is(); - const bool rIsSame = rhsp->is(); - if (lIsSame != rIsSame) { - if (lIsSame) { - APPLYING(SWAP_SAME_IN_COMMUTATIVE_BINARY) { - replace(make(vtxp, rhsp, lhsp)); - return true; - } - } - return false; - } - } - - // Otherwise put sides in order based on unique iD, this makes - // 'a op b' and 'b op a' end up the same for better combining. - { + // Swap sides to simplify other patterns - This needs to be the last pattern + const bool swapSides = [&]() { + // Don't swap if not commutative, duh + if VL_CONSTEXPR_CXX17 (!IsCommutative::value) return false; + // Const go on left + if (lConstp) return false; + if (rConstp) return true; + // Not go on left + if (lhsp->is()) return false; + if (rhsp->is()) return true; + // Same go on right + if (rSamep) return false; + if (lSamep) return true; + // Otherwise put sides in order based on unique ID, this makes + // 'a op b' and 'b op a' end up the same for better combining. const VertexInfo& lInfo = m_vInfo[lhsp]; const VertexInfo& rInfo = m_vInfo[rhsp]; - if (lInfo.m_id > rInfo.m_id) { - APPLYING(SWAP_SIDE_IN_COMMUTATIVE_BINARY) { - replace(make(vtxp, rhsp, lhsp)); - return true; - } + return lInfo.m_id > rInfo.m_id; + }(); + if (swapSides) { + APPLYING(SWAP_SIDES_IN_BINARY) { + replace(make(vtxp, rhsp, lhsp)); + return true; } } @@ -711,9 +734,11 @@ class V3DfgPeephole final : public DfgVisitor { tryPushCompareOpThroughConcat(Vertex* const vtxp, DfgConst* constp, DfgConcat* concatp) { FileLine* const flp = vtxp->fileline(); - // If at least one of the sides of the Concat is constant, then push the Vertex past - // the Concat - if (concatp->lhsp()->is() || concatp->rhsp()->is()) { + // If at least one of the sides of the Concat is constant, or the concat is unused once, + // then push the Vertex past the Concat + if (!concatp->hasMultipleSinks() // + || concatp->lhsp()->is() // + || concatp->rhsp()->is()) { APPLYING(PUSH_COMPARE_OP_THROUGH_CONCAT) { const uint32_t width = concatp->width(); const uint32_t lWidth = concatp->lhsp()->width(); @@ -732,16 +757,13 @@ class V3DfgPeephole final : public DfgVisitor { = make(flp, m_bitDType, newRhsConstp, concatp->rhsp()); // The replacement Vertex - DfgVertexBinary* const resp - = std::is_same::value - ? make(concatp->fileline(), m_bitDType, newLhsp, newRhsp) - : nullptr; - UASSERT_OBJ(resp, vtxp, - "Unhandled vertex type in 'tryPushCompareOpThroughConcat': " - << vtxp->typeName()); - - // Replace this vertex - replace(resp); + if VL_CONSTEXPR_CXX17 (std::is_same::value) { + replace(make(concatp->fileline(), m_bitDType, newLhsp, newRhsp)); + } else if VL_CONSTEXPR_CXX17 (std::is_same::value) { + replace(make(concatp->fileline(), m_bitDType, newLhsp, newRhsp)); + } else { + vtxp->v3fatalSrc("Unhandled vertex type: " << vtxp->typeName()); + } return true; } } @@ -1218,9 +1240,9 @@ class V3DfgPeephole final : public DfgVisitor { } } - // Sel from a partial temporary (including narrowed vertex) + // Sel from a partial variable (including narrowed vertex) if (DfgVarPacked* const varp = fromp->cast()) { - if (varp->tmpForp() && varp->srcp()) { + if (varp->srcp() && !varp->isVolatile()) { // Must be a splice, otherwise it would have been inlined DfgSplicePacked* splicep = varp->srcp()->as(); DfgVertex* driverp = nullptr; @@ -1276,20 +1298,10 @@ class V3DfgPeephole final : public DfgVisitor { //========================================================================= void visit(DfgAnd* const vtxp) override { + if (binary(vtxp)) return; + DfgVertex* const lhsp = vtxp->lhsp(); DfgVertex* const rhsp = vtxp->rhsp(); - - if (isSame(lhsp, rhsp)) { - APPLYING(REMOVE_AND_WITH_SELF) { - replace(lhsp); - return; - } - } - - if (associativeBinary(vtxp)) return; - - if (commutativeBinary(vtxp)) return; - FileLine* const flp = vtxp->fileline(); // Bubble pushing (De Morgan) @@ -1360,23 +1372,31 @@ class V3DfgPeephole final : public DfgVisitor { if (vtxp->dtype() == m_bitDType) { if (tryReplaceBitwiseWithReduction(vtxp)) return; } + + { + DfgReplicate* repp = lhsp->cast(); + DfgCond* condp = rhsp->cast(); + if (!repp && !condp) { + repp = rhsp->cast(); + condp = lhsp->cast(); + } + if (repp && condp && repp->srcp()->size() == 1 && !condp->hasMultipleSinks() + && isZero(condp->elsep())) { + APPLYING(REPLACE_AND_REP_COND_ELSE_ZERO) { + DfgAnd* const newCondp + = make(condp->condp(), repp->srcp(), condp->condp()); + replace(make(condp, newCondp, condp->thenp(), condp->elsep())); + return; + } + } + } } void visit(DfgOr* const vtxp) override { + if (binary(vtxp)) return; + DfgVertex* const lhsp = vtxp->lhsp(); DfgVertex* const rhsp = vtxp->rhsp(); - - if (isSame(lhsp, rhsp)) { - APPLYING(REMOVE_OR_WITH_SELF) { - replace(lhsp); - return; - } - } - - if (associativeBinary(vtxp)) return; - - if (commutativeBinary(vtxp)) return; - FileLine* const flp = vtxp->fileline(); // Bubble pushing (De Morgan) @@ -1473,6 +1493,8 @@ class V3DfgPeephole final : public DfgVisitor { } void visit(DfgXor* const vtxp) override { + if (binary(vtxp)) return; + DfgVertex* const lhsp = vtxp->lhsp(); DfgVertex* const rhsp = vtxp->rhsp(); @@ -1483,10 +1505,6 @@ class V3DfgPeephole final : public DfgVisitor { } } - if (associativeBinary(vtxp)) return; - - if (commutativeBinary(vtxp)) return; - if (DfgConst* const lConstp = lhsp->cast()) { if (lConstp->isZero()) { APPLYING(REMOVE_XOR_WITH_ZERO) { @@ -1517,9 +1535,7 @@ class V3DfgPeephole final : public DfgVisitor { //========================================================================= void visit(DfgAdd* const vtxp) override { - if (associativeBinary(vtxp)) return; - - if (commutativeBinary(vtxp)) return; + if (binary(vtxp)) return; DfgVertex* const lhsp = vtxp->lhsp(); DfgVertex* const rhsp = vtxp->rhsp(); @@ -1622,7 +1638,7 @@ class V3DfgPeephole final : public DfgVisitor { } void visit(DfgConcat* const vtxp) override { - if (associativeBinary(vtxp)) return; + if (binary(vtxp)) return; DfgVertex* const lhsp = vtxp->lhsp(); DfgVertex* const rhsp = vtxp->rhsp(); @@ -1844,20 +1860,216 @@ class V3DfgPeephole final : public DfgVisitor { } } } + + // Convert various forms to REPLICATE + if (isSame(lhsp, rhsp)) { + APPLYING(REPLACE_CONCAT_SAME) { + replace(make(vtxp, lhsp, makeI32(flp, 2))); + return; + } + } + if (DfgReplicate* const lRepp = lhsp->cast()) { + if (isSame(lRepp->srcp(), rhsp)) { + APPLYING(REPLACE_CONCAT_SAME_REP_ON_LHS) { + const uint32_t count = lRepp->countp()->as()->toU32() + 1; + replace(make(vtxp, rhsp, makeI32(flp, count))); + return; + } + } + } + if (DfgReplicate* const rRepp = rhsp->cast()) { + if (isSame(lhsp, rRepp->srcp())) { + APPLYING(REPLACE_CONCAT_SAME_REP_ON_RHS) { + const uint32_t count = rRepp->countp()->as()->toU32() + 1; + replace(make(vtxp, lhsp, makeI32(flp, count))); + return; + } + } + } } void visit(DfgDiv* const vtxp) override { - if (foldBinary(vtxp)) return; + if (binary(vtxp)) return; } void visit(DfgDivS* const vtxp) override { - if (foldBinary(vtxp)) return; + if (binary(vtxp)) return; + } + + void visit(DfgGt* const vtxp) override { + if (binary(vtxp)) return; + + DfgVertex* const lhsp = vtxp->lhsp(); + DfgVertex* const rhsp = vtxp->rhsp(); + FileLine* const flp = vtxp->fileline(); + + if (isSame(lhsp, rhsp)) { + APPLYING(FOLD_SELF_GT) { + replace(makeZero(flp, 1)); + return; + } + } + } + + void visit(DfgGtS* const vtxp) override { + if (binary(vtxp)) return; + + DfgVertex* const lhsp = vtxp->lhsp(); + DfgVertex* const rhsp = vtxp->rhsp(); + FileLine* const flp = vtxp->fileline(); + + if (isSame(lhsp, rhsp)) { + APPLYING(FOLD_SELF_GTS) { + replace(makeZero(flp, 1)); + return; + } + } + } + + void visit(DfgGte* const vtxp) override { + if (binary(vtxp)) return; + + DfgVertex* const lhsp = vtxp->lhsp(); + DfgVertex* const rhsp = vtxp->rhsp(); + FileLine* const flp = vtxp->fileline(); + + if (isSame(lhsp, rhsp)) { + APPLYING(FOLD_SELF_GTE) { + replace(makeOnes(flp, 1)); + return; + } + } + } + + void visit(DfgGteS* const vtxp) override { + if (binary(vtxp)) return; + + DfgVertex* const lhsp = vtxp->lhsp(); + DfgVertex* const rhsp = vtxp->rhsp(); + FileLine* const flp = vtxp->fileline(); + + if (isSame(lhsp, rhsp)) { + APPLYING(FOLD_SELF_GTES) { + replace(makeOnes(flp, 1)); + return; + } + } + } + + void visit(DfgLogAnd* const vtxp) override { + if (binary(vtxp)) return; + + DfgVertex* const lhsp = vtxp->lhsp(); + DfgVertex* const rhsp = vtxp->rhsp(); + + if (lhsp->width() == 1 && rhsp->width() == 1) { + APPLYING(REPLACE_LOGAND_WITH_AND) { + replace(make(vtxp, lhsp, rhsp)); + return; + } + } + } + + void visit(DfgLogEq* const vtxp) override { + if (binary(vtxp)) return; + } + + void visit(DfgLogIf* const vtxp) override { + if (binary(vtxp)) return; + } + + void visit(DfgLogOr* const vtxp) override { + if (binary(vtxp)) return; + + DfgVertex* const lhsp = vtxp->lhsp(); + DfgVertex* const rhsp = vtxp->rhsp(); + + if (lhsp->width() == 1 && rhsp->width() == 1) { + APPLYING(REPLACE_LOGOR_WITH_OR) { + replace(make(vtxp, lhsp, rhsp)); + return; + } + } + } + + void visit(DfgLt* const vtxp) override { + if (binary(vtxp)) return; + + DfgVertex* const lhsp = vtxp->lhsp(); + DfgVertex* const rhsp = vtxp->rhsp(); + FileLine* const flp = vtxp->fileline(); + + if (isSame(lhsp, rhsp)) { + APPLYING(FOLD_SELF_LT) { + replace(makeZero(flp, 1)); + return; + } + } + } + + void visit(DfgLtS* const vtxp) override { + if (binary(vtxp)) return; + + DfgVertex* const lhsp = vtxp->lhsp(); + DfgVertex* const rhsp = vtxp->rhsp(); + FileLine* const flp = vtxp->fileline(); + + if (isSame(lhsp, rhsp)) { + APPLYING(FOLD_SELF_LTS) { + replace(makeZero(flp, 1)); + return; + } + } + } + + void visit(DfgLte* const vtxp) override { + if (binary(vtxp)) return; + + DfgVertex* const lhsp = vtxp->lhsp(); + DfgVertex* const rhsp = vtxp->rhsp(); + FileLine* const flp = vtxp->fileline(); + + if (isSame(lhsp, rhsp)) { + APPLYING(FOLD_SELF_LTE) { + replace(makeOnes(flp, 1)); + return; + } + } + } + + void visit(DfgLteS* const vtxp) override { + if (binary(vtxp)) return; + + DfgVertex* const lhsp = vtxp->lhsp(); + DfgVertex* const rhsp = vtxp->rhsp(); + FileLine* const flp = vtxp->fileline(); + + if (isSame(lhsp, rhsp)) { + APPLYING(FOLD_SELF_LTES) { + replace(makeOnes(flp, 1)); + return; + } + } + } + + void visit(DfgModDiv* const vtxp) override { + if (binary(vtxp)) return; + } + + void visit(DfgModDivS* const vtxp) override { + if (binary(vtxp)) return; + } + + void visit(DfgMul* const vtxp) override { + if (binary(vtxp)) return; + } + + void visit(DfgMulS* const vtxp) override { + if (binary(vtxp)) return; } void visit(DfgEq* const vtxp) override { - if (foldBinary(vtxp)) return; - - if (commutativeBinary(vtxp)) return; + if (binary(vtxp)) return; DfgVertex* const lhsp = vtxp->lhsp(); DfgVertex* const rhsp = vtxp->rhsp(); @@ -1874,187 +2086,27 @@ class V3DfgPeephole final : public DfgVisitor { if (DfgConcat* const rhsConcatp = rhsp->cast()) { if (tryPushCompareOpThroughConcat(vtxp, lhsConstp, rhsConcatp)) return; } - } - } - void visit(DfgGt* const vtxp) override { - if (foldBinary(vtxp)) return; + if (rhsp->dtype() == m_bitDType) { + if (isZero(lhsConstp)) { + APPLYING(REPLACE_EQ_BIT_0) { + replace(make(vtxp, rhsp)); + return; + } + } - DfgVertex* const lhsp = vtxp->lhsp(); - DfgVertex* const rhsp = vtxp->rhsp(); - FileLine* const flp = vtxp->fileline(); - - if (isSame(lhsp, rhsp)) { - APPLYING(FOLD_SELF_GT) { - replace(makeZero(flp, 1)); - return; + if (isOnes(lhsConstp)) { + APPLYING(REMOVE_EQ_BIT_1) { + replace(rhsp); + return; + } + } } } } - void visit(DfgGtS* const vtxp) override { - if (foldBinary(vtxp)) return; - - DfgVertex* const lhsp = vtxp->lhsp(); - DfgVertex* const rhsp = vtxp->rhsp(); - FileLine* const flp = vtxp->fileline(); - - if (isSame(lhsp, rhsp)) { - APPLYING(FOLD_SELF_GTS) { - replace(makeZero(flp, 1)); - return; - } - } - } - - void visit(DfgGte* const vtxp) override { - if (foldBinary(vtxp)) return; - - DfgVertex* const lhsp = vtxp->lhsp(); - DfgVertex* const rhsp = vtxp->rhsp(); - FileLine* const flp = vtxp->fileline(); - - if (isSame(lhsp, rhsp)) { - APPLYING(FOLD_SELF_GTE) { - replace(makeOnes(flp, 1)); - return; - } - } - } - - void visit(DfgGteS* const vtxp) override { - if (foldBinary(vtxp)) return; - - DfgVertex* const lhsp = vtxp->lhsp(); - DfgVertex* const rhsp = vtxp->rhsp(); - FileLine* const flp = vtxp->fileline(); - - if (isSame(lhsp, rhsp)) { - APPLYING(FOLD_SELF_GTES) { - replace(makeOnes(flp, 1)); - return; - } - } - } - - void visit(DfgLogAnd* const vtxp) override { - if (foldBinary(vtxp)) return; - - DfgVertex* const lhsp = vtxp->lhsp(); - DfgVertex* const rhsp = vtxp->rhsp(); - - if (lhsp->width() == 1 && rhsp->width() == 1) { - APPLYING(REPLACE_LOGAND_WITH_AND) { - replace(make(vtxp, lhsp, rhsp)); - return; - } - } - } - - void visit(DfgLogEq* const vtxp) override { - if (foldBinary(vtxp)) return; - } - - void visit(DfgLogIf* const vtxp) override { - if (foldBinary(vtxp)) return; - } - - void visit(DfgLogOr* const vtxp) override { - if (foldBinary(vtxp)) return; - - DfgVertex* const lhsp = vtxp->lhsp(); - DfgVertex* const rhsp = vtxp->rhsp(); - - if (lhsp->width() == 1 && rhsp->width() == 1) { - APPLYING(REPLACE_LOGOR_WITH_OR) { - replace(make(vtxp, lhsp, rhsp)); - return; - } - } - } - - void visit(DfgLt* const vtxp) override { - if (foldBinary(vtxp)) return; - - DfgVertex* const lhsp = vtxp->lhsp(); - DfgVertex* const rhsp = vtxp->rhsp(); - FileLine* const flp = vtxp->fileline(); - - if (isSame(lhsp, rhsp)) { - APPLYING(FOLD_SELF_LT) { - replace(makeZero(flp, 1)); - return; - } - } - } - - void visit(DfgLtS* const vtxp) override { - if (foldBinary(vtxp)) return; - - DfgVertex* const lhsp = vtxp->lhsp(); - DfgVertex* const rhsp = vtxp->rhsp(); - FileLine* const flp = vtxp->fileline(); - - if (isSame(lhsp, rhsp)) { - APPLYING(FOLD_SELF_LTS) { - replace(makeZero(flp, 1)); - return; - } - } - } - - void visit(DfgLte* const vtxp) override { - if (foldBinary(vtxp)) return; - - DfgVertex* const lhsp = vtxp->lhsp(); - DfgVertex* const rhsp = vtxp->rhsp(); - FileLine* const flp = vtxp->fileline(); - - if (isSame(lhsp, rhsp)) { - APPLYING(FOLD_SELF_LTE) { - replace(makeOnes(flp, 1)); - return; - } - } - } - - void visit(DfgLteS* const vtxp) override { - if (foldBinary(vtxp)) return; - - DfgVertex* const lhsp = vtxp->lhsp(); - DfgVertex* const rhsp = vtxp->rhsp(); - FileLine* const flp = vtxp->fileline(); - - if (isSame(lhsp, rhsp)) { - APPLYING(FOLD_SELF_LTES) { - replace(makeOnes(flp, 1)); - return; - } - } - } - - void visit(DfgModDiv* const vtxp) override { - if (foldBinary(vtxp)) return; - } - - void visit(DfgModDivS* const vtxp) override { - if (foldBinary(vtxp)) return; - } - - void visit(DfgMul* const vtxp) override { - if (associativeBinary(vtxp)) return; - - if (commutativeBinary(vtxp)) return; - } - - void visit(DfgMulS* const vtxp) override { - if (associativeBinary(vtxp)) return; - - if (commutativeBinary(vtxp)) return; - } - void visit(DfgNeq* const vtxp) override { - if (foldBinary(vtxp)) return; + if (binary(vtxp)) return; DfgVertex* const lhsp = vtxp->lhsp(); DfgVertex* const rhsp = vtxp->rhsp(); @@ -2066,37 +2118,67 @@ class V3DfgPeephole final : public DfgVisitor { return; } } + + if (DfgConst* const lhsConstp = lhsp->cast()) { + if (DfgConcat* const rhsConcatp = rhsp->cast()) { + if (tryPushCompareOpThroughConcat(vtxp, lhsConstp, rhsConcatp)) return; + } + + if (rhsp->dtype() == m_bitDType) { + if (isZero(lhsConstp)) { + APPLYING(REMOVE_NEQ_BIT_0) { + replace(rhsp); + return; + } + } + + if (isOnes(lhsConstp)) { + APPLYING(REPLACE_NEQ_BIT_1) { + replace(make(vtxp, rhsp)); + return; + } + } + } + } } void visit(DfgPow* const vtxp) override { - if (foldBinary(vtxp)) return; + if (binary(vtxp)) return; } void visit(DfgPowSS* const vtxp) override { - if (foldBinary(vtxp)) return; + if (binary(vtxp)) return; } void visit(DfgPowSU* const vtxp) override { - if (foldBinary(vtxp)) return; + if (binary(vtxp)) return; } void visit(DfgPowUS* const vtxp) override { - if (foldBinary(vtxp)) return; + if (binary(vtxp)) return; } void visit(DfgReplicate* const vtxp) override { + if (DfgConst* const sConstp = vtxp->srcp()->cast()) { + DfgConst* const cConstp = vtxp->countp()->as(); + APPLYING(FOLD_REPLICATE) { + DfgConst* const resultp = makeZero(vtxp->fileline(), vtxp->width()); + foldOp(resultp->num(), sConstp->num(), cConstp->num()); + replace(resultp); + return; + } + } + if (vtxp->dtype() == vtxp->srcp()->dtype()) { APPLYING(REMOVE_REPLICATE_ONCE) { replace(vtxp->srcp()); return; } } - - if (foldBinary(vtxp)) return; } void visit(DfgShiftL* const vtxp) override { - if (foldBinary(vtxp)) return; + if (binary(vtxp)) return; if (optimizeShiftRHS(vtxp)) return; DfgVertex* const lhsp = vtxp->lhsp(); @@ -2185,7 +2267,7 @@ class V3DfgPeephole final : public DfgVisitor { } void visit(DfgShiftR* const vtxp) override { - if (foldBinary(vtxp)) return; + if (binary(vtxp)) return; if (optimizeShiftRHS(vtxp)) return; DfgVertex* const lhsp = vtxp->lhsp(); @@ -2274,12 +2356,12 @@ class V3DfgPeephole final : public DfgVisitor { } void visit(DfgShiftRS* const vtxp) override { - if (foldBinary(vtxp)) return; + if (binary(vtxp)) return; if (optimizeShiftRHS(vtxp)) return; } void visit(DfgSub* const vtxp) override { - if (foldBinary(vtxp)) return; + if (binary(vtxp)) return; DfgVertex* const lhsp = vtxp->lhsp(); DfgVertex* const rhsp = vtxp->rhsp(); @@ -2465,6 +2547,22 @@ class V3DfgPeephole final : public DfgVisitor { return; } } + if (DfgOr* const tOrp = thenp->cast()) { + if (isSame(tOrp->lhsp(), elsep)) { // a ? b | c : b becomes b | (a & c) + APPLYING(REPLACE_COND_THEN_OR_LHS) { + DfgAnd* const andp = make(vtxp, condp, tOrp->rhsp()); + replace(make(vtxp, tOrp->lhsp(), andp)); + return; + } + } + if (isSame(tOrp->rhsp(), elsep)) { // a ? b | c : c becomes c | (a & b) + APPLYING(REPLACE_COND_THEN_OR_RHS) { + DfgAnd* const andp = make(vtxp, condp, tOrp->lhsp()); + replace(make(vtxp, tOrp->rhsp(), andp)); + return; + } + } + } } if (DfgConcat* const tConcatp = thenp->cast()) { @@ -2494,7 +2592,7 @@ class V3DfgPeephole final : public DfgVisitor { } } - if (isZero(elsep) && isEqOne(thenp)) { + if (isEqOne(thenp) && isZero(elsep)) { APPLYING(REPLACE_COND_CONST_ONE_ZERO) { DfgVertex* resp = condp; if (const uint32_t extend = vtxp->width() - 1) { @@ -2518,6 +2616,21 @@ class V3DfgPeephole final : public DfgVisitor { } } + if (isOnes(thenp) && isZero(elsep)) { + APPLYING(REPLACE_COND_CONST_ONES_ZERO) { + replace(make(vtxp, condp, makeI32(flp, vtxp->width()))); + return; + } + } + + if (isZero(thenp) && isOnes(elsep)) { + APPLYING(REPLACE_COND_CONST_ZERO_ONES) { + replace(make(vtxp, make(condp, condp), + makeI32(flp, vtxp->width()))); + return; + } + } + if (DfgCond* const tCondp = thenp->cast()) { if (isSame(condp, tCondp->condp())) { APPLYING(REPLACE_COND_SAME_COND_THEN) { diff --git a/src/V3DfgPeepholePatterns.h b/src/V3DfgPeepholePatterns.h index b8aa011c9..0a8969592 100644 --- a/src/V3DfgPeepholePatterns.h +++ b/src/V3DfgPeepholePatterns.h @@ -27,12 +27,12 @@ // Enumeration of each peephole optimization. Must be kept in sorted order (enforced by tests). // clang-format off #define FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION(macro) \ - _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, FOLD_ASSOC_BINARY) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, FOLD_ASSOC_BINARY_LHS_OF_RHS) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, FOLD_ASSOC_BINARY_RHS_OF_LHS) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, FOLD_BINARY) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, FOLD_MUX_FROM_ONES) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, FOLD_MUX_FROM_ZERO) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, FOLD_REPLICATE) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, FOLD_SEL) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, FOLD_SELF_EQ) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, FOLD_SELF_GT) \ @@ -68,16 +68,19 @@ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_SEL_THROUGH_SPLICE) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_SHIFTL_THROUGH_COND) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_SHIFTR_THROUGH_COND) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_ACI_BINARY_LHS) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_ACI_BINARY_RHS) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_ADD_ZERO) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_AND_WITH_ONES) \ - _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_AND_WITH_SELF) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_CONCAT_OF_ADJOINING_SELS) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_COND_WITH_BRANCHES_SAME) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_COND_WITH_FALSE_CONDITION) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_COND_WITH_TRUE_CONDITION) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_EQ_BIT_1) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_FULL_WIDTH_SEL) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_IDEMPOTENT_BINARY) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_NEQ_BIT_0) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_NOT_NOT) \ - _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_OR_WITH_SELF) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_OR_WITH_ZERO) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_REDUNDANT_ZEXT_ON_RHS_OF_SHIFT) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REMOVE_REPLICATE_ONCE) \ @@ -92,13 +95,19 @@ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_ADD_WITH_COUNT_ONES) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_AND_OF_NOT_AND_NEQ) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_AND_OF_NOT_AND_NOT) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_AND_REP_COND_ELSE_ZERO) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_AND_WITH_ZERO) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_BITWISE_OF_REDUCTION_OF_SELS_WITH_REDUCTION) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_BITWISE_OF_SELS_WITH_REDUCTION) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_CONCAT_SAME) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_CONCAT_SAME_REP_ON_LHS) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_CONCAT_SAME_REP_ON_RHS) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_CONCAT_SEL_BOTTOM_AND_ZERO_WITH_SHIFTL) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_CONCAT_ZERO_AND_SEL_TOP_WITH_SHIFTR) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_CONST_ONES_ZERO) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_CONST_ONE_ZERO) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_CONST_ZERO_ONE) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_CONST_ZERO_ONES) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_DEC) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_INC) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_OR_THEN_COND_LHS) \ @@ -107,6 +116,8 @@ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_SAME_CAT_RHS) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_SAME_COND_ELSE) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_SAME_COND_THEN) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_THEN_OR_LHS) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_THEN_OR_RHS) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_WITH_ELSE_BRANCH_COND) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_WITH_ELSE_BRANCH_ONES) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_WITH_ELSE_BRANCH_ZERO) \ @@ -116,10 +127,12 @@ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_CONTRADICTORY_AND) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_CONTRADICTORY_AND_3) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_DISTRIBUTIVE_BINARY) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_EQ_BIT_0) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_EXTEND) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_LOGAND_WITH_AND) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_LOGOR_WITH_OR) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_MUX_WITH_SEL) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_NEQ_BIT_1) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_NESTED_CONCAT_OF_ADJOINING_SELS_ON_LHS) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_NESTED_CONCAT_OF_ADJOINING_SELS_ON_RHS) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_NOT_EQ) \ @@ -149,10 +162,7 @@ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, RIGHT_LEANING_ASSOC) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, SWAP_COND_WITH_NEQ_CONDITION) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, SWAP_COND_WITH_NOT_CONDITION) \ - _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, SWAP_CONST_IN_COMMUTATIVE_BINARY) \ - _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, SWAP_NOT_IN_COMMUTATIVE_BINARY) \ - _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, SWAP_SAME_IN_COMMUTATIVE_BINARY) \ - _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, SWAP_SIDE_IN_COMMUTATIVE_BINARY) + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, SWAP_SIDES_IN_BINARY) // clang-format on class VDfgPeepholePattern final { diff --git a/test_regress/t/t_dfg_peephole.v b/test_regress/t/t_dfg_peephole.v index 15b6100d1..30e685075 100644 --- a/test_regress/t/t_dfg_peephole.v +++ b/test_regress/t/t_dfg_peephole.v @@ -125,7 +125,8 @@ module t ( `signal(SWAP_VAR_IN_COMMUTATIVE_BINARY, rand_b + rand_a); `signal(PUSH_BITWISE_OP_THROUGH_CONCAT, 32'h12345678 ^ {8'h0, rand_a[23:0]}); `signal(PUSH_BITWISE_OP_THROUGH_CONCAT_2, 32'h12345678 ^ {rand_b[7:0], rand_a[23:0]}); - `signal(PUSH_COMPARE_OP_THROUGH_CONCAT, 4'b1011 == {2'b10, rand_a[1:0]}); + `signal(PUSH_COMPARE_OP_THROUGH_CONCAT_EQ, 4'b1011 == {2'b10, rand_a[1:0]}); + `signal(PUSH_COMPARE_OP_THROUGH_CONCAT_NE, 4'b1011 != {2'b10, rand_a[1:0]}); `signal(PUSH_REDUCTION_THROUGH_COND_WITH_CONST_BRANCH, |(rand_a[32] ? rand_a[3:0] : 4'h0)); `signal(REPLACE_REDUCTION_OF_CONST_AND, &const_a); `signal(REPLACE_REDUCTION_OF_CONST_OR, |const_a); @@ -251,6 +252,8 @@ module t ( `signal(REPLACE_COND_CONST_ONE_ZERO, rand_a[0] ? 8'b1 : 8'b0); `signal(REPLACE_COND_CONST_ZERO_ONE, rand_a[0] ? 8'b0 : 8'b1); + `signal(REPLACE_COND_CONST_ONES_ZERO, rand_a[0] ? 8'hff : 8'b0); + `signal(REPLACE_COND_CONST_ZERO_ONAE, rand_a[0] ? 8'b0 : 8'hff); `signal(REPLACE_COND_CAT_LHS_CONST_ONE_ZERO, rand_a[0] ? {8'b1, rand_b[0]} : {8'b0, rand_b[1]}); `signal(REPLACE_COND_CAT_LHS_CONST_ZERO_ONE, rand_a[0] ? {8'b0, rand_b[0]} : {8'b1, rand_b[1]}); `signal(REPLACE_COND_SAME_CAT_LHS, rand_a[0] ? {8'd0, rand_b[0]} : {8'd0, rand_b[1]}); @@ -310,6 +313,18 @@ module t ( `signal(FOLD_MUX_FROM_ONES, ones[rand_a[5:0]]); `signal(FOLD_MUX_FROM_ZERO, zero[rand_a[5:0]]); `signal(REPLACE_MUX_WITH_SEL, rand_a[const_a[5:0]]); + `signal(REPLACE_COND_THEN_OR_LHS, rand_a[0] ? rand_a[1] | rand_b[1] : rand_a[1]); + `signal(REPLACE_COND_THEN_OR_RHS, rand_a[0] ? rand_a[1] | rand_b[1] : rand_b[1]); + `signal(REMOVE_ACI_BINARY_LHS, ~rand_a & (~rand_b & ~rand_a)); + `signal(REMOVE_ACI_BINARY_RHS, (~rand_b & ~rand_a) & ~rand_a); + `signal(REPLACE_CONCAT_SAME, {rand_a, rand_a}); + `signal(REPLACE_CONCAT_REP_ON_LHS, {{2{rand_a}}, rand_a}); + `signal(REPLACE_CONCAT_REP_ON_RHS, {rand_a, {2{rand_a}}}); + `signal(REPLACE_AND_REP_COND_ELSE_ZERO, {4{rand_a[0]}} & (rand_a[1] ? rand_b[3:0] : 4'd0)); + `signal(REPLACE_EQ_BIT_0, 1'b0 == rand_a[0]); + `signal(REMOVE_EQ_BIT_1, 1'b1 == rand_a[0]); + `signal(REMOVE_NEQ_BIT_0, 1'b0 != rand_a[0]); + `signal(REPLACE_NEQ_BIT_1, 1'b1 != rand_a[0]); // Operators that should work wiht mismatched widths `signal(MISMATCHED_ShiftL,const_a << 4'd2); diff --git a/test_regress/t/t_dfg_stats_patterns.out b/test_regress/t/t_dfg_stats_patterns.out index c18754212..59c6272db 100644 --- a/test_regress/t/t_dfg_stats_patterns.out +++ b/test_regress/t/t_dfg_stats_patterns.out @@ -91,8 +91,8 @@ DFG patterns with depth 3 DFG patterns with depth 4 1 (CONCAT (REDXOR (AND (NOT _:A/a):B*/a (NOT _:C/a):D*/a):E*/a):F/1 (CONCAT (REDXOR (AND _:D*/a _:G*/a):H/a):I/1 (CONCAT (REDXOR _:J/a):K/1 (CONCAT _:L/1 _:M/b):N/c):O/d):P/e):Q/f - 1 (CONCAT (REDXOR (AND (NOT _:A/a):B*/a (NOT _:C/a):D*/a):E/a):F/1 (CONCAT (REDXOR (AND _:G*/a _:D*/a):H/a):I/1 (CONCAT (SEL@0 _:J*/a):K/1 (CONCAT _:L/1 _:M/b):N/c):O/d):P/e):Q/f - 1 (CONCAT (REDXOR (AND (NOT _:A/a):B*/a (NOT _:C/a):D*/a):E/a):F/1 (CONCAT (SEL@0 (AND _:B*/a _:G*/a):H*/a):I/1 (CONCAT (REDXOR _:J*/b):K/1 (CONCAT _:L/1 _:M/c):N/d):O/e):P/b):Q/f + 1 (CONCAT (REDXOR (AND (NOT _:A/a):B*/a (NOT _:C/a):D*/a):E/a):F/1 (CONCAT (REDXOR (AND _:D*/a _:G*/a):H/a):I/1 (CONCAT (SEL@0 _:J*/a):K/1 (CONCAT _:L/1 _:M/b):N/c):O/d):P/e):Q/f + 1 (CONCAT (REDXOR (AND (NOT _:A/a):B*/a (NOT _:C/a):D*/a):E/a):F/1 (CONCAT (SEL@0 (AND _:D*/a _:G*/a):H*/a):I/1 (CONCAT (REDXOR _:J*/b):K/1 (CONCAT _:L/1 _:M/c):N/d):O/e):P/b):Q/f 1 (CONCAT (REDXOR (REPLICATE (NOT _:A*/a):B/a cA:C/a):D*/b):E/1 (CONCAT (REDXOR (SEL@0 _:D*/b):F/c):G/1 (REDXOR (REPLICATE _:A*/a cA:H/a):I/b):J*/1):K/d):L/e 1 (CONCAT (REDXOR (REPLICATE (REPLICATE _:A*/a cA:B/b):C*/b cA:D/b):E/c):F*/1 (CONCAT (REDXOR (REPLICATE _:G/b cA:H/b):I*/c):J/1 (CONCAT (REDXOR _:K/d):L/1 (REDXOR _:E/c):F*/1):M/e):N/f):O/g 1 (CONCAT (REDXOR (REPLICATE (REPLICATE _:A*/a cA:B/b):C*/c cA:D/b):E*/b):F/1 (CONCAT (REDXOR (REPLICATE _:E*/b cA:G/b):H/d):I*/1 (CONCAT (REDXOR _:J*/d):K/1 (CONCAT _:L/1 _:I*/1):M/e):N/f):O/g):P/h diff --git a/test_regress/t/t_opt_const_dfg.py b/test_regress/t/t_opt_const_dfg.py index eb5626311..2d16873bb 100755 --- a/test_regress/t/t_opt_const_dfg.py +++ b/test_regress/t/t_opt_const_dfg.py @@ -18,7 +18,7 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "--stats", test.pli_filename test.execute() if test.vlt: - test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 43) + test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 41) test.file_grep(test.stats, r'SplitVar, packed variables split automatically\s+(\d+)', 1) test.passes()