From 0c93c3844f69c5dd01d522e6dde13756c4b716da Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Fri, 18 Jun 2021 16:18:30 +0100 Subject: [PATCH] Simplify AND(CONST,OR(_,_)) with redundant terms V3Expand generates a lot of OR nodes that are under a clearing mask, and have redundant terms, e.g.: 0xff & (a << 8 | b >> 24). The 'a << 8' term in there is redundant as it's bottom bits are all zero where the mask is non-zero. V3Const now removes these redundant terms. --- src/V3Const.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 7c78cb314..9a8796602 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -710,6 +710,48 @@ private: VL_DO_DANGLING(nodep->deleteTree(), nodep); return true; } + bool matchMaskedOr(AstAnd* nodep) { + // Masking an OR with terms that have no bits set under the mask is replaced with masking + // only the remaining terms. Canonical example as generated by V3Expand is: + // 0xff & (a << 8 | b >> 24) --> 0xff & (b >> 24) + + // Compute how many significant bits are in the mask + const AstConst* const constp = VN_CAST(nodep->lhsp(), Const); + const uint32_t significantBits = constp->num().widthMin(); + + AstOr* const orp = VN_CAST(nodep->rhsp(), Or); + + // Predicate for checking whether the bottom 'significantBits' bits of the given expression + // are all zeroes. + const auto checkBottomClear = [=](const AstNode* nodep) -> bool { + if (const AstShiftL* const shiftp = VN_CAST_CONST(nodep, ShiftL)) { + if (const AstConst* const scp = VN_CAST_CONST(shiftp->rhsp(), Const)) { + return scp->num().toUInt() >= significantBits; + } + } + return false; + }; + + const bool orLIsRedundant = checkBottomClear(orp->lhsp()); + const bool orRIsRedundant = checkBottomClear(orp->rhsp()); + + if (orLIsRedundant && orRIsRedundant) { + nodep->replaceWith( + new AstConst(nodep->fileline(), AstConst::DtypedValue(), nodep->dtypep(), 0)); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + return true; + } else if (orLIsRedundant) { + orp->replaceWith(orp->rhsp()->unlinkFrBack()); + VL_DO_DANGLING(orp->deleteTree(), orp); + return false; // input node is still valid, keep going + } else if (orRIsRedundant) { + orp->replaceWith(orp->lhsp()->unlinkFrBack()); + VL_DO_DANGLING(orp->deleteTree(), orp); + return false; // input node is still valid, keep going + } else { + return false; + } + } bool matchBitOpTree(AstNode* nodep) { if (!v3Global.opt.oConstBitOpTree()) return false; @@ -2964,9 +3006,10 @@ private: TREEOPV("AstConcat{$lhsp.castSel, $rhsp.castSel, ifAdjacentSel(VN_CAST($lhsp,,Sel),,VN_CAST($rhsp,,Sel))}", "replaceConcatSel(nodep)"); TREEOPV("AstConcat{ifConcatMergeableBiop($lhsp), concatMergeable($lhsp,,$rhsp)}", "replaceConcatMerge(nodep)"); // Common two-level operations that can be simplified - TREEOP ("AstAnd {$lhsp.castConst,matchAndCond(nodep)}", "DONE"); - TREEOP ("AstAnd {$lhsp.castOr, $rhsp.castOr, operandAndOrSame(nodep)}", "replaceAndOr(nodep)"); - TREEOP ("AstOr {$lhsp.castAnd,$rhsp.castAnd,operandAndOrSame(nodep)}", "replaceAndOr(nodep)"); + TREEOP ("AstAnd {$lhsp.castConst,matchAndCond(nodep)}", "DONE"); + TREEOP ("AstAnd {$lhsp.castConst, $rhsp.castOr, matchMaskedOr(nodep)}", "DONE"); + TREEOP ("AstAnd {$lhsp.castOr, $rhsp.castOr, operandAndOrSame(nodep)}", "replaceAndOr(nodep)"); + TREEOP ("AstOr {$lhsp.castAnd,$rhsp.castAnd,operandAndOrSame(nodep)}", "replaceAndOr(nodep)"); TREEOP ("AstOr {matchOrAndNot(nodep)}", "DONE"); TREEOP ("AstAnd {operandShiftSame(nodep)}", "replaceShiftSame(nodep)"); TREEOP ("AstOr {operandShiftSame(nodep)}", "replaceShiftSame(nodep)");