diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 679ceefec..ace91ded6 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -2967,8 +2967,8 @@ struct AstSqrtD : public AstNodeUniop { //====================================================================== // Binary ops -struct AstLogOr : public AstNodeBiComAsv { - AstLogOr(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { +struct AstLogOr : public AstNodeBiop { + AstLogOr(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { dtypeChgBool(); } ASTNODE_NODE_FUNCS(LogOr, LOGOR) virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLogOr(lhs,rhs); } @@ -2980,8 +2980,8 @@ struct AstLogOr : public AstNodeBiComAsv { virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} virtual int instrCount() const { return widthInstrs()+instrCountBranch(); } }; -struct AstLogAnd : public AstNodeBiComAsv { - AstLogAnd(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { +struct AstLogAnd : public AstNodeBiop { + AstLogAnd(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { dtypeChgBool(); } ASTNODE_NODE_FUNCS(LogAnd, LOGAND) virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLogAnd(lhs,rhs); } diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 29cc946f4..ff5589430 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -104,6 +104,7 @@ private: bool m_warn; // Output warnings bool m_doExpensive; // Enable computationally expensive optimizations bool m_doNConst; // Enable non-constant-child simplifications + bool m_doShort; // Remove expressions that short circuit bool m_doV; // Verilog, not C++ conversion AstNodeModule* m_modp; // Current module AstNode* m_scopep; // Current scope @@ -228,11 +229,13 @@ private: } bool operandHugeShiftL(AstNodeBiop* nodep) { return (nodep->rhsp()->castConst() - && nodep->rhsp()->castConst()->toUInt() >= (uint32_t)(nodep->width())); + && nodep->rhsp()->castConst()->toUInt() >= (uint32_t)(nodep->width()) + && isTPure(nodep->lhsp())); } bool operandHugeShiftR(AstNodeBiop* nodep) { return (nodep->rhsp()->castConst() - && nodep->rhsp()->castConst()->toUInt() >= (uint32_t)(nodep->lhsp()->width())); + && nodep->rhsp()->castConst()->toUInt() >= (uint32_t)(nodep->lhsp()->width()) + && isTPure(nodep->lhsp())); } bool operandIsTwo(AstNode* nodep) { return (nodep->castConst() @@ -353,6 +356,14 @@ private: return nodep; } + bool isTPure(AstNode* nodep) { + // Pure checks - if this node and all nodes under it are free of + // side effects can do this optimization + // Eventually we'll recurse through tree when unknown, memoizing results so far, + // but for now can disable en-mass until V3Purify takes effect. + return m_doShort || nodep->castVarRef() || nodep->castConst(); + } + // Extraction checks bool warnSelect(AstSel* nodep) { AstNode* basefromp = AstArraySel::baseFromp(nodep); @@ -455,6 +466,21 @@ private: void replaceZero(AstNode* nodep) { replaceNum(nodep, 0); nodep=NULL; } + void replaceZeroChkPure(AstNode* nodep, AstNode* checkp) { + // For example, "0 * n" -> 0 if n has no side effects + // Else strength reduce it to 0 & n. + // If ever change the operation note AstAnd rule specially ignores this created pattern + if (isTPure(checkp)) { + replaceNum(nodep, 0); nodep=NULL; + } else { + AstNode* newp = new AstAnd(nodep->fileline(), + new AstConst(nodep->fileline(), 0), + checkp->unlinkFrBack()); + newp->widthSignedFrom(nodep); + nodep->replaceWith(newp); + nodep->deleteTree(); nodep=NULL; + } + } void replaceAllOnes (AstNode* nodep) { V3Number ones (nodep->fileline(), nodep->width(), 0); ones.setMask(nodep->width()); @@ -1654,28 +1680,28 @@ private: TREEOPC("AstNodeUniop{$lhsp.castConst, !nodep->isOpaque()}", "replaceConst(nodep)"); // Zero on one side or the other TREEOP ("AstAdd {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); - TREEOP ("AstAnd {$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); + TREEOP ("AstAnd {$lhsp.isZero, $rhsp, isTPure($rhsp)}", "replaceZero(nodep)"); // Can't use replaceZeroChkPure as we make this pattern in ChkPure TREEOP ("AstLogAnd{$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); TREEOP ("AstLogOr {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); - TREEOP ("AstDiv {$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); - TREEOP ("AstDivS {$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); - TREEOP ("AstMul {$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); - TREEOP ("AstMulS {$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); - TREEOP ("AstPow {$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); - TREEOP ("AstPowS {$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); + TREEOP ("AstDiv {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); + TREEOP ("AstDivS {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); + TREEOP ("AstMul {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); + TREEOP ("AstMulS {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); + TREEOP ("AstPow {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); + TREEOP ("AstPowS {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); TREEOP ("AstOr {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); - TREEOP ("AstShiftL{$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); - TREEOP ("AstShiftR{$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); - TREEOP ("AstShiftRS{$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); + TREEOP ("AstShiftL{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); + TREEOP ("AstShiftR{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); + TREEOP ("AstShiftRS{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); TREEOP ("AstXor {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); TREEOP ("AstXnor {$lhsp.isZero, $rhsp}", "AstNot{$rhsp}"); TREEOP ("AstSub {$lhsp.isZero, $rhsp}", "AstNegate{$rhsp}"); TREEOP ("AstAdd {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); - TREEOP ("AstAnd {$lhsp, $rhsp.isZero}", "replaceZero(nodep)"); - TREEOP ("AstLogAnd{$lhsp, $rhsp.isZero}", "replaceZero(nodep)"); + TREEOP ("AstAnd {$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)"); + TREEOP ("AstLogAnd{$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)"); TREEOP ("AstLogOr {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); - TREEOP ("AstMul {$lhsp, $rhsp.isZero}", "replaceZero(nodep)"); - TREEOP ("AstMulS {$lhsp, $rhsp.isZero}", "replaceZero(nodep)"); + TREEOP ("AstMul {$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)"); + TREEOP ("AstMulS {$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)"); TREEOP ("AstOr {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); TREEOP ("AstShiftL{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); TREEOP ("AstShiftR{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); @@ -1686,12 +1712,12 @@ private: // Non-zero on one side or the other TREEOP ("AstAnd {$lhsp.isAllOnes, $rhsp}", "replaceWRhs(nodep)"); TREEOP ("AstLogAnd{$lhsp.isNeqZero, $rhsp}", "replaceWRhs(nodep)"); - TREEOP ("AstOr {$lhsp.isAllOnes, $rhsp}", "replaceWLhs(nodep)"); //->allOnes + TREEOP ("AstOr {$lhsp.isAllOnes, $rhsp, isTPure($rhsp)}", "replaceWLhs(nodep)"); //->allOnes TREEOP ("AstLogOr {$lhsp.isNeqZero, $rhsp}", "replaceNum(nodep,1)"); TREEOP ("AstAnd {$lhsp, $rhsp.isAllOnes}", "replaceWLhs(nodep)"); TREEOP ("AstLogAnd{$lhsp, $rhsp.isNeqZero}", "replaceWLhs(nodep)"); - TREEOP ("AstOr {$lhsp, $rhsp.isAllOnes}", "replaceWRhs(nodep)"); //->allOnes - TREEOP ("AstLogOr {$lhsp, $rhsp.isNeqZero}", "replaceNum(nodep,1)"); + TREEOP ("AstOr {$lhsp, $rhsp.isAllOnes, isTPure($lhsp)}", "replaceWRhs(nodep)"); //->allOnes + TREEOP ("AstLogOr {$lhsp, $rhsp.isNeqZero, isTPure($lhsp)}", "replaceNum(nodep,1)"); TREEOP ("AstXor {$lhsp.isAllOnes, $rhsp}", "AstNot{$rhsp}"); TREEOP ("AstXnor {$lhsp.isAllOnes, $rhsp}", "replaceWRhs(nodep)"); TREEOP ("AstMul {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)"); @@ -1710,10 +1736,10 @@ private: TREEOPC("AstNodeCond{$condp.isNeqZero, $expr1p.castConst, $expr2p.castConst}", "replaceWChild(nodep,$expr1p)"); TREEOP ("AstNodeCond{$condp, operandsSame($expr1p,,$expr2p)}","replaceWChild(nodep,$expr1p)"); TREEOP ("AstCond{$condp->castNot(), $expr1p, $expr2p}", "AstCond{$condp->op1p(), $expr2p, $expr1p}"); - TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p.isAllOnes, $expr2p}", "AstOr {$condp, $expr2p}"); // a?1:b == a|b - TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p, $expr2p.isZero}", "AstAnd{$condp, $expr1p}"); // a?b:0 == a&b - TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p, $expr2p.isAllOnes}", "AstOr {AstNot{$condp}, $expr1p}"); // a?b:1 == ~a|b - TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p.isZero, $expr2p}", "AstAnd{AstNot{$condp}, $expr2p}"); // a?0:b == ~a&b + TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p.isAllOnes, $expr2p}", "AstLogOr {$condp, $expr2p}"); // a?1:b == a||b + TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p, $expr2p.isZero}", "AstLogAnd{$condp, $expr1p}"); // a?b:0 == a&&b + TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p, $expr2p.isAllOnes}", "AstLogOr {AstNot{$condp}, $expr1p}"); // a?b:1 == ~a||b + TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p.isZero, $expr2p}", "AstLogAnd{AstNot{$condp}, $expr2p}"); // a?0:b == ~a&&b TREEOP ("AstNodeCond{!$condp.width1, operandBoolShift(nodep->condp())}", "replaceBoolShift(nodep->condp())"); // Prefer constants on left, since that often needs a shift, it lets constant red remove the shift TREEOP ("AstNodeBiCom{!$lhsp.castConst, $rhsp.castConst}", "swapSides(nodep)"); @@ -1838,9 +1864,9 @@ private: TREEOPV("AstOneHot{$lhsp.width1}", "replaceWLhs(nodep)"); TREEOPV("AstOneHot0{$lhsp.width1}", "replaceNum(nodep,1)"); // Binary AND/OR is faster than logical and/or (usually) - TREEOPV("AstLogAnd{$lhsp.width1, $rhsp.width1}", "AstAnd{$lhsp,$rhsp}"); - TREEOPV("AstLogOr {$lhsp.width1, $rhsp.width1}", "AstOr{$lhsp,$rhsp}"); - TREEOPV("AstLogNot{$lhsp.width1}", "AstNot{$lhsp}"); + TREEOPV("AstLogAnd{$lhsp.width1, $rhsp.width1, isTPure($lhsp), isTPure($rhsp)}", "AstAnd{$lhsp,$rhsp}"); + TREEOPV("AstLogOr {$lhsp.width1, $rhsp.width1, isTPure($lhsp), isTPure($rhsp)}", "AstOr{$lhsp,$rhsp}"); + TREEOPV("AstLogNot{$lhsp.width1, isTPure($lhsp)}", "AstNot{$lhsp}"); // CONCAT(CONCAT({a},{b}),{c}) -> CONCAT({a},CONCAT({b},{c})) // CONCAT({const},CONCAT({const},{c})) -> CONCAT((constifiedCONC{const|const},{c})) TREEOPV("AstConcat{operandConcatMove(nodep)}", "moveConcat(nodep)"); @@ -1928,6 +1954,7 @@ public: m_required = false; m_doExpensive = false; m_doNConst = false; + m_doShort = true; // Presently always done m_doV = false; m_warn = false; m_wremove = true; // Overridden in visitors diff --git a/src/V3Simulate.h b/src/V3Simulate.h index d2600022f..0f4249423 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -354,23 +354,69 @@ private: *fetchNumber(nodep->thsp())); } } + virtual void visit(AstLogAnd* nodep, AstNUser*) { + // Need to short circuit + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); + if (m_checkOnly) { + nodep->iterateChildren(*this); + } else { + nodep->lhsp()->accept(*this); + if (fetchNumber(nodep->lhsp())->isNeqZero()) { + nodep->rhsp()->accept(*this); + newNumber(nodep)->opAssign(*fetchNumber(nodep->rhsp())); + } else { + newNumber(nodep)->opAssign(*fetchNumber(nodep->lhsp())); // a zero + } + } + } + virtual void visit(AstLogOr* nodep, AstNUser*) { + // Need to short circuit + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); + if (m_checkOnly) { + nodep->iterateChildren(*this); + } else { + nodep->lhsp()->accept(*this); + if (fetchNumber(nodep->lhsp())->isNeqZero()) { + newNumber(nodep)->opAssign(*fetchNumber(nodep->lhsp())); // a one + } else { + nodep->rhsp()->accept(*this); + newNumber(nodep)->opAssign(*fetchNumber(nodep->rhsp())); + } + } + } + virtual void visit(AstLogIf* nodep, AstNUser*) { + // Need to short circuit, same as (!A || B) + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); + if (m_checkOnly) { + nodep->iterateChildren(*this); + } else { + nodep->lhsp()->accept(*this); + if (fetchNumber(nodep->lhsp())->isEqZero()) { + newNumber(nodep)->opAssign(V3Number(nodep->fileline(), 1, 1)); // a one + } else { + nodep->rhsp()->accept(*this); + newNumber(nodep)->opAssign(*fetchNumber(nodep->rhsp())); + } + } + } virtual void visit(AstNodeCond* nodep, AstNUser*) { - // We could use above visit(AstNodeTriop), but it's slower even O(n^2) to evaluate - // both sides when we really only need to evaluate one side. + // We could use above visit(AstNodeTriop), but need to do short circuiting. + // It's also slower even O(n^2) to evaluate both sides when we really only need to evaluate one side. if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); if (m_checkOnly) { nodep->iterateChildren(*this); } else { nodep->condp()->accept(*this); - if (optimizable()) { - if (fetchNumber(nodep->condp())->isNeqZero()) { - nodep->expr1p()->accept(*this); - newNumber(nodep)->opAssign(*fetchNumber(nodep->expr1p())); - } else { - nodep->expr2p()->accept(*this); - newNumber(nodep)->opAssign(*fetchNumber(nodep->expr2p())); - } + if (fetchNumber(nodep->condp())->isNeqZero()) { + nodep->expr1p()->accept(*this); + newNumber(nodep)->opAssign(*fetchNumber(nodep->expr1p())); + } else { + nodep->expr2p()->accept(*this); + newNumber(nodep)->opAssign(*fetchNumber(nodep->expr2p())); } } } diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp index 074f899f8..ac7260a82 100644 --- a/src/V3Slice.cpp +++ b/src/V3Slice.cpp @@ -161,10 +161,10 @@ class SliceCloneVisitor : public AstNVisitor { if (lhsp && rhsp) { switch (redOpType) { case REDOP_OR: - lhsp = new AstLogOr(nodep->fileline(), lhsp, rhsp); + lhsp = new AstOr(nodep->fileline(), lhsp, rhsp); break; case REDOP_AND: - lhsp = new AstLogAnd(nodep->fileline(), lhsp, rhsp); + lhsp = new AstAnd(nodep->fileline(), lhsp, rhsp); break; case REDOP_XOR: lhsp = new AstXor(nodep->fileline(), lhsp, rhsp);