From cd30c22d1cc887f7cc95f7eeb0a575bbc1d04545 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 20 Apr 2026 18:28:11 +0100 Subject: [PATCH] Optimize more Dfg patterns (#7452) --- src/V3DfgPeephole.cpp | 227 +++++++++++++++++++++++------- src/V3DfgPeepholePatterns.h | 3 + test_regress/t/t_dfg_peephole.v | 9 +- test_regress/t/t_opt_const_dfg.py | 2 +- 4 files changed, 186 insertions(+), 55 deletions(-) diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index a1ae95f26..f2e4b9ce0 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -407,6 +407,12 @@ class V3DfgPeephole final : public DfgVisitor { return make(examplep->fileline(), examplep->dtype(), operands...); } + // Replicate 'bitp' to 'vtxp->width()' bits + DfgVertex* replicate(DfgVertex* vtxp, DfgVertex* bitp) { + if (vtxp->dtype() == m_bitDType) return bitp; + return make(vtxp, bitp, makeI32(vtxp->fileline(), vtxp->width())); + } + // Check two vertex are the same, or the same constant value static bool isSame(const DfgVertex* ap, const DfgVertex* bp) { if (ap == bp) return true; @@ -564,15 +570,25 @@ class V3DfgPeephole final : public DfgVisitor { } } - // 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(); + if VL_CONSTEXPR_CXX17 (IsCommutative::value) { + if (!lhsp->hasMultipleSinks() && rlVtxp->hasMultipleSinks()) { + APPLYING(ROTATE_ASSOC_COMM_MULTIUSE) { + replace(make(vtxp, rlVtxp, make(vtxp, lhsp, rrVtxp))); + 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 if 'b OP c' has a single use and can subsequently be removed, + // otherwise there is no improvement. + // '(a OP (b OP c))' -> '(a OP b) OP c' if (Vertex* const existingp = m_cache.get(resultDType(lhsp, rlVtxp), lhsp, rlVtxp)) { @@ -703,10 +719,10 @@ class V3DfgPeephole final : public DfgVisitor { tryPushBitwiseOpThroughConcat(Vertex* const vtxp, DfgConst* constp, DfgConcat* concatp) { FileLine* const flp = vtxp->fileline(); - // If at least one of the sides of the Concat constant, or width 1 (i.e.: can be - // further simplified), then push the Vertex past the Concat - if (concatp->lhsp()->is() || concatp->rhsp()->is() // - || concatp->lhsp()->dtype() == m_bitDType || concatp->rhsp()->dtype() == m_bitDType) { + // If at least one of the sides of the Concat constant, then push Vertex past Concat + DfgConst* const catLConstp = concatp->lhsp()->cast(); + DfgConst* const catRConstp = concatp->rhsp()->cast(); + if (catLConstp || catRConstp) { APPLYING(PUSH_BITWISE_OP_THROUGH_CONCAT) { const uint32_t width = concatp->width(); const DfgDataType& lDtype = concatp->lhsp()->dtype(); @@ -715,14 +731,30 @@ class V3DfgPeephole final : public DfgVisitor { const uint32_t rWidth = rDtype.size(); // The new Lhs vertex - DfgConst* const newLhsConstp = makeZero(constp->fileline(), lWidth); - newLhsConstp->num().opSel(constp->num(), width - 1, rWidth); - Vertex* const newLhsp = make(flp, lDtype, newLhsConstp, concatp->lhsp()); + DfgVertex* const newLhsp = [&]() -> DfgVertex* { + DfgConst* const newLhsConstp = makeZero(constp->fileline(), lWidth); + if (catLConstp) { + V3Number num{constp->fileline(), static_cast(lWidth), 0u}; + num.opSel(constp->num(), width - 1, rWidth); + foldOp(newLhsConstp->num(), num, catLConstp->num()); + return newLhsConstp; + } + newLhsConstp->num().opSel(constp->num(), width - 1, rWidth); + return make(flp, lDtype, newLhsConstp, concatp->lhsp()); + }(); // The new Rhs vertex - DfgConst* const newRhsConstp = makeZero(constp->fileline(), rWidth); - newRhsConstp->num().opSel(constp->num(), rWidth - 1, 0); - Vertex* const newRhsp = make(flp, rDtype, newRhsConstp, concatp->rhsp()); + DfgVertex* const newRhsp = [&]() -> DfgVertex* { + DfgConst* const newRhsConstp = makeZero(constp->fileline(), rWidth); + if (catRConstp) { + V3Number num{constp->fileline(), static_cast(rWidth), 0u}; + num.opSel(constp->num(), rWidth - 1, 0); + foldOp(newRhsConstp->num(), num, catRConstp->num()); + return newRhsConstp; + } + newRhsConstp->num().opSel(constp->num(), rWidth - 1, 0); + return make(flp, rDtype, newRhsConstp, concatp->rhsp()); + }(); // Replace this vertex replace(make(concatp, newLhsp, newRhsp)); @@ -797,6 +829,46 @@ class V3DfgPeephole final : public DfgVisitor { return false; } + template + VL_ATTR_WARN_UNUSED_RESULT bool tryPushBitwiseOpThrougSel(Bitwise* const vtxp) { + DfgVertex* const lhsp = vtxp->lhsp(); + DfgVertex* const rhsp = vtxp->rhsp(); + + if (DfgSel* const lSelp = lhsp->cast()) { + DfgSel* rSelp = nullptr; + DfgVertex* extrap = nullptr; + if (DfgSel* const selp = rhsp->cast()) { + rSelp = selp; + } else if (Bitwise* const bitwisep = rhsp->cast()) { + if (DfgSel* const rlSelp = bitwisep->lhsp()->template cast()) { + rSelp = rlSelp; + extrap = bitwisep->rhsp(); + } else if (DfgSel* const rrSelp = bitwisep->rhsp()->template cast()) { + rSelp = rrSelp; + extrap = bitwisep->lhsp(); + } + } + if (rSelp) { + DfgVertex* const lFromp = lSelp->fromp(); + DfgVertex* const rFromp = rSelp->fromp(); + if (lFromp->dtype() == rFromp->dtype() && lFromp->width() <= VL_QUADSIZE // + && lSelp->lsb() == rSelp->lsb()) { + APPLYING(PUSH_BITWISE_THROUGH_SEL) { + Bitwise* const bwp + = make(vtxp->fileline(), lSelp->fromp()->dtype(), + lSelp->fromp(), rSelp->fromp()); + DfgVertex* resp = make(vtxp, bwp, lSelp->lsb()); + if (extrap) resp = make(vtxp, resp, extrap); + replace(resp); + return true; + } + } + } + } + + return false; + } + template VL_ATTR_WARN_UNUSED_RESULT bool tryReplaceBitwiseWithReduction(Bitwise* vtxp) { UASSERT_OBJ(vtxp->width() == 1, vtxp, "Width must be 1"); @@ -806,13 +878,15 @@ class V3DfgPeephole final : public DfgVisitor { DfgVertex* const rhsp = vtxp->rhsp(); if (DfgSel* const lSelp = lhsp->template cast()) { - DfgSel* rSelp = rhsp->template cast(); + DfgSel* rSelp = nullptr; DfgVertex* extrap = nullptr; - if (!rSelp) { - if (Bitwise* const rBitwisep = rhsp->template cast()) { - rSelp = rBitwisep->lhsp()->template cast(); - extrap = rBitwisep->rhsp(); - } + if (DfgSel* const selp = rhsp->template cast()) { + rSelp = selp; + } else if (Bitwise* const rBitwisep = rhsp->template cast()) { + rSelp = rBitwisep->lhsp()->template cast(); + extrap = rBitwisep->rhsp(); + } else if (Reduction* const rRedp = rhsp->template cast()) { + rSelp = rRedp->srcp()->template cast(); } if (rSelp) { uint32_t lsb = 0; @@ -1380,18 +1454,27 @@ class V3DfgPeephole final : public DfgVisitor { if (tryPushBitwiseOpThroughReductions(vtxp)) return; - if (DfgNot* const lhsNotp = lhsp->cast()) { + if (tryPushBitwiseOpThrougSel(vtxp)) return; + + { + DfgNot* const lNotp = lhsp->cast(); + DfgNot* const rNotp = rhsp->cast(); // ~A & A is all zeroes - if (lhsNotp->srcp() == rhsp) { + if ((lNotp && isSame(lNotp->srcp(), rhsp)) || (rNotp && isSame(lhsp, rNotp->srcp()))) { APPLYING(REPLACE_CONTRADICTORY_AND) { replace(makeZero(flp, vtxp->width())); return; } } - // ~A & (A & _) or ~A & (_ & A) is all zeroes - if (DfgAnd* const rhsAndp = rhsp->cast()) { - if (lhsNotp->srcp() == rhsAndp->lhsp() || lhsNotp->srcp() == rhsAndp->rhsp()) { + if (DfgAnd* const rSamep = rhsp->cast()) { + DfgNot* const rlNotp = rSamep->lhsp()->cast(); + DfgNot* const rrNotp = rSamep->rhsp()->cast(); + // ~A & (A & _) or ~A & (_ & A) is all zeroes + if ((lNotp && isSame(lNotp->srcp(), rSamep->lhsp())) + || (lNotp && isSame(lNotp->srcp(), rSamep->rhsp())) + || (rlNotp && isSame(lhsp, rlNotp->srcp())) + || (rrNotp && isSame(lhsp, rrNotp->srcp()))) { APPLYING(REPLACE_CONTRADICTORY_AND_3) { replace(makeZero(flp, vtxp->width())); return; @@ -1494,24 +1577,29 @@ class V3DfgPeephole final : public DfgVisitor { if (tryPushBitwiseOpThroughReductions(vtxp)) return; - if (DfgNot* const lhsNotp = lhsp->cast()) { + if (tryPushBitwiseOpThrougSel(vtxp)) return; + + { + DfgNot* const lNotp = lhsp->cast(); + DfgNot* const rNotp = rhsp->cast(); // ~A | A is all ones - if (lhsNotp->srcp() == rhsp) { + if ((lNotp && isSame(lNotp->srcp(), rhsp)) || (rNotp && isSame(lhsp, rNotp->srcp()))) { APPLYING(REPLACE_TAUTOLOGICAL_OR) { - DfgConst* const resp = makeZero(flp, vtxp->width()); - resp->num().setAllBits1(); - replace(resp); + replace(makeOnes(flp, vtxp->width())); return; } } - // ~A | (A | _) or ~A | (_ | A) is all ones - if (DfgOr* const rhsOrp = rhsp->cast()) { - if (lhsNotp->srcp() == rhsOrp->lhsp() || lhsNotp->srcp() == rhsOrp->rhsp()) { + if (DfgOr* const rSamep = rhsp->cast()) { + DfgNot* const rlNotp = rSamep->lhsp()->cast(); + DfgNot* const rrNotp = rSamep->rhsp()->cast(); + // ~A | (A | _) or ~A | (_ | A) is all ones + if ((lNotp && isSame(lNotp->srcp(), rSamep->lhsp())) + || (lNotp && isSame(lNotp->srcp(), rSamep->rhsp())) + || (rlNotp && isSame(lhsp, rlNotp->srcp())) + || (rrNotp && isSame(lhsp, rrNotp->srcp()))) { APPLYING(REPLACE_TAUTOLOGICAL_OR_3) { - DfgConst* const resp = makeZero(flp, vtxp->width()); - resp->num().setAllBits1(); - replace(resp); + replace(makeOnes(flp, vtxp->width())); return; } } @@ -1556,6 +1644,8 @@ class V3DfgPeephole final : public DfgVisitor { if (tryPushBitwiseOpThroughReductions(vtxp)) return; + if (tryPushBitwiseOpThrougSel(vtxp)) return; + if (vtxp->dtype() == m_bitDType) { if (tryReplaceBitwiseWithReduction(vtxp)) return; } @@ -2541,53 +2631,63 @@ class V3DfgPeephole final : public DfgVisitor { } if (vtxp->dtype() == m_bitDType) { - if (isZero(thenp)) { // a ? 0 : b becomes ~a & b - APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ZERO) { - replace(make(vtxp, make(vtxp, condp), elsep)); - return; - } - } - if (thenp == condp) { // a ? a : b becomes a | b + if (isSame(condp, thenp)) { // a ? a : b becomes a | b APPLYING(REPLACE_COND_WITH_THEN_BRANCH_COND) { replace(make(vtxp, condp, elsep)); return; } } - if (elsep == condp) { // a ? b : a becomes a & b + if (isSame(condp, elsep)) { // a ? b : a becomes a & b APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_COND) { replace(make(vtxp, condp, thenp)); return; } } + } + + if (vtxp->width() <= VL_QUADSIZE) { + if (isZero(thenp)) { // a ? 0 : b becomes ~a & b + APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ZERO) { + DfgVertex* const maskp = replicate(vtxp, make(condp, condp)); + replace(make(vtxp, maskp, elsep)); + return; + } + } if (isOnes(thenp)) { // a ? 1 : b becomes a | b APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ONES) { - replace(make(vtxp, condp, elsep)); + DfgVertex* const maskp = replicate(vtxp, condp); + replace(make(vtxp, maskp, elsep)); return; } } if (isZero(elsep)) { // a ? b : 0 becomes a & b APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ZERO) { - replace(make(vtxp, condp, thenp)); + DfgVertex* const maskp = replicate(vtxp, condp); + replace(make(vtxp, maskp, thenp)); return; } } if (isOnes(elsep)) { // a ? b : 1 becomes ~a | b APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ONES) { - replace(make(vtxp, make(vtxp, condp), thenp)); + DfgVertex* const maskp = replicate(vtxp, make(condp, condp)); + replace(make(vtxp, maskp, thenp)); 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()); + DfgVertex* const maskp = replicate(vtxp, condp); + DfgAnd* const andp = make(vtxp, maskp, 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()); + DfgVertex* const maskp = replicate(vtxp, condp); + DfgAnd* const andp = make(vtxp, maskp, tOrp->lhsp()); replace(make(vtxp, tOrp->rhsp(), andp)); return; } @@ -2620,6 +2720,33 @@ class V3DfgPeephole final : public DfgVisitor { } } } + + if (!tConcatp->hasMultipleSinks()) { + if (DfgConcat* const tRCatp = tConcatp->rhsp()->cast()) { + if (!tRCatp->hasMultipleSinks()) { + if (DfgSel* const tLSelp = tConcatp->lhsp()->cast()) { + if (DfgSel* const tRRSelp = tRCatp->rhsp()->cast()) { + if (tLSelp->lsb() == tRCatp->width() // + && tRRSelp->lsb() == 0 // + && isSame(tLSelp->fromp(), elsep) // + && isSame(tRRSelp->fromp(), elsep)) { + APPLYING(REPLACE_COND_INSERT) { + DfgVertex* const newTp = tRCatp->lhsp(); + DfgVertex* const newEp = make( + flp, newTp->dtype(), elsep, tRRSelp->width()); + DfgCond* const newCp = make(flp, newTp->dtype(), + condp, newTp, newEp); + replace(make( + vtxp, tLSelp, + make(tRCatp, newCp, tRRSelp))); + return; + } + } + } + } + } + } + } } if (isEqOne(thenp) && isZero(elsep)) { diff --git a/src/V3DfgPeepholePatterns.h b/src/V3DfgPeepholePatterns.h index 0a8969592..05df94530 100644 --- a/src/V3DfgPeepholePatterns.h +++ b/src/V3DfgPeepholePatterns.h @@ -51,6 +51,7 @@ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PULL_NOTS_THROUGH_COND) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_BITWISE_OP_THROUGH_CONCAT) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_BITWISE_THROUGH_REDUCTION) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_BITWISE_THROUGH_SEL) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_COMMUTATIVE_BINARY_THROUGH_COND) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_COMPARE_OP_THROUGH_CONCAT) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_CONCAT_THROUGH_COND_LHS) \ @@ -110,6 +111,7 @@ _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_INSERT) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_OR_THEN_COND_LHS) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_OR_THEN_COND_RHS) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_SAME_CAT_LHS) \ @@ -160,6 +162,7 @@ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REUSE_ASSOC_BINARY_LHS_WITH_LHS_OF_RHS) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REUSE_ASSOC_BINARY_LHS_WITH_RHS_OF_RHS) \ _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, RIGHT_LEANING_ASSOC) \ + _FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, ROTATE_ASSOC_COMM_MULTIUSE) \ _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_SIDES_IN_BINARY) diff --git a/test_regress/t/t_dfg_peephole.v b/test_regress/t/t_dfg_peephole.v index 30e685075..0d88de5d9 100644 --- a/test_regress/t/t_dfg_peephole.v +++ b/test_regress/t/t_dfg_peephole.v @@ -250,10 +250,10 @@ module t ( `signal(REUSE_ASSOC_LHS_WITH_RHS_OF_RHS_XOR_COMMON, rand_a[23:4] ^ rand_a[39:20]); `signal(REUSE_ASSOC_LHS_WITH_RHS_OF_RHS_XOR, rand_a[23:4] ^ (~rand_b[24:5] ^ rand_a[39:20])); - `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_CONST_ONE_ZERO, rand_a[0] ? 80'b1 : 80'b0); + `signal(REPLACE_COND_CONST_ZERO_ONE, rand_a[0] ? 80'b0 : 80'b1); + `signal(REPLACE_COND_CONST_ONES_ZERO, rand_a[0] ? -80'b1 : 80'b0); + `signal(REPLACE_COND_CONST_ZERO_ONAE, rand_a[0] ? 80'b0 : -80'b1); `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]}); @@ -325,6 +325,7 @@ module t ( `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]); + `signal(REPLACE_COND_INSERT, rand_a[0] ? {rand_b[63:40], {1'd0, rand_b[38:0]}} : rand_b); // Operators that should work wiht mismatched widths `signal(MISMATCHED_ShiftL,const_a << 4'd2); diff --git a/test_regress/t/t_opt_const_dfg.py b/test_regress/t/t_opt_const_dfg.py index 2d16873bb..6ad2d3549 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+)', 41) + test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 38) test.file_grep(test.stats, r'SplitVar, packed variables split automatically\s+(\d+)', 1) test.passes()