diff --git a/configure.ac b/configure.ac index 6ad04c29c..c508bcfb9 100644 --- a/configure.ac +++ b/configure.ac @@ -442,6 +442,7 @@ m4_foreach([cflag],[ [-mno-cet], [-Qunused-arguments], [-Wno-bool-operation], + [-Wno-constant-logical-operand], [-Wno-tautological-bitwise-compare], [-Wno-parentheses-equality], [-Wno-sign-compare], diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 8f50841ae..ddde27f08 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1343,16 +1343,6 @@ void AstNode::dumpTreeDotFile(const string& filename, bool append, bool doDump) } } -bool AstNode::isTreePureRecurse() const { - // Should memoize this if call commonly - if (!this->isPure()) return false; - if (this->op1p() && !this->op1p()->isTreePureRecurse()) return false; - if (this->op2p() && !this->op2p()->isTreePureRecurse()) return false; - if (this->op3p() && !this->op3p()->isTreePureRecurse()) return false; - if (this->op4p() && !this->op4p()->isTreePureRecurse()) return false; - return true; -} - string AstNode::instanceStr() const { // Max iterations before giving up on location search, // in case we have some circular reference bug. diff --git a/src/V3Ast.h b/src/V3Ast.h index 5de6ab906..8c42e41ba 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -167,6 +167,21 @@ inline std::ostream& operator<<(std::ostream& os, const VLifetime& rhs) { // ###################################################################### +class VPurity final { + // Used in some nodes to cache the result of isPure method +public: + enum en : uint8_t { NOT_CACHED, PURE, IMPURE }; + enum en m_e; + VPurity() + : m_e{NOT_CACHED} {} + bool isCached() const { return m_e != NOT_CACHED; } + bool isPure() const { return m_e == PURE; } + void setPurity(bool pure) { m_e = pure ? PURE : IMPURE; } + void clearCache() { m_e = NOT_CACHED; } +}; + +// ###################################################################### + class VAccess final { public: enum en : uint8_t { @@ -690,6 +705,7 @@ public: } bool isReadOnly() const VL_MT_SAFE { return m_e == INPUT || m_e == CONSTREF; } bool isWritable() const VL_MT_SAFE { return m_e == OUTPUT || m_e == INOUT || m_e == REF; } + bool isRef() const VL_MT_SAFE { return m_e == REF; } bool isRefOrConstRef() const VL_MT_SAFE { return m_e == REF || m_e == CONSTREF; } }; constexpr bool operator==(const VDirection& lhs, const VDirection& rhs) { @@ -2024,8 +2040,6 @@ public: void dumpTreeDot(std::ostream& os = std::cout) const; void dumpTreeDotFile(const string& filename, bool append = false, bool doDump = true); - bool isTreePureRecurse() const; - // METHODS - queries // Changes control flow, disable some optimizations virtual bool isBrancher() const { return false; } @@ -2034,11 +2048,11 @@ public: // GateDedupable is a slightly larger superset of GateOptimzable (eg, AstNodeIf) virtual bool isGateDedupable() const { return isGateOptimizable(); } // Else creates output or exits, etc, not unconsumed - virtual bool isOutputter() const { return false; } + virtual bool isOutputter() { return false; } // Else a AstTime etc which output can't be predicted from input virtual bool isPredictOptimizable() const { return !isTimingControl(); } // Else a $display, etc, that must be ordered with other displays - virtual bool isPure() const { return true; } + virtual bool isPure() { return true; } // Else a AstTime etc that can't be substituted out virtual bool isSubstOptimizable() const { return true; } // An event control, delay, wait, etc. diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index ec2f06693..823c26a05 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -64,11 +64,14 @@ public: // Wrap This expression into an AstStmtExpr to denote it occurs in statement position inline AstStmtExpr* makeStmt(); + virtual void clearCachedPurity(){}; // Most nodes don't cache their purity }; class AstNodeBiop VL_NOT_FINAL : public AstNodeExpr { // Binary expression // @astgen op1 := lhsp : AstNodeExpr // @astgen op2 := rhsp : AstNodeExpr + VPurity m_purity; // Pure state + protected: AstNodeBiop(VNType t, FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp) : AstNodeExpr{t, fl} { @@ -93,6 +96,12 @@ public: virtual bool stringFlavor() const { return false; } // N flavor of nodes with both flavors? int instrCount() const override { return widthInstrs(); } bool same(const AstNode*) const override { return true; } + bool isPure() override; + const char* broken() const override; + void clearCachedPurity() override; + +private: + bool getPurity() const { return lhsp()->isPure() && rhsp()->isPure(); } }; class AstNodeBiCom VL_NOT_FINAL : public AstNodeBiop { // Binary expr with commutative properties @@ -196,8 +205,8 @@ public: } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override; - bool isOutputter() const override { return !isPure(); } + bool isPure() override; + bool isOutputter() override { return !isPure(); } AstCFunc* funcp() const { return m_funcp; } void funcp(AstCFunc* funcp) { m_funcp = funcp; } void argTypes(const string& str) { m_argTypes = str; } @@ -219,6 +228,8 @@ class AstNodeFTaskRef VL_NOT_FINAL : public AstNodeExpr { string m_dotted; // Dotted part of scope the name()ed task/func is under or "" string m_inlinedDots; // Dotted hierarchy flattened out bool m_pli = false; // Pli system call ($name) + VPurity m_purity; // Pure state + protected: AstNodeFTaskRef(VNType t, FileLine* fl, AstNode* namep, AstNodeExpr* pinsp) : AstNodeExpr{t, fl} { @@ -249,11 +260,15 @@ public: void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; } bool pli() const { return m_pli; } void pli(bool flag) { m_pli = flag; } - bool isPure() const override; + bool isPure() override; string emitVerilog() final override { V3ERROR_NA_RETURN(""); } string emitC() final override { V3ERROR_NA_RETURN(""); } bool cleanOut() const final override { V3ERROR_NA_RETURN(true); } + void clearCachedPurity() override; + +private: + bool getPurity() const; }; class AstNodePreSel VL_NOT_FINAL : public AstNodeExpr { // Something that becomes an AstSel @@ -261,6 +276,8 @@ class AstNodePreSel VL_NOT_FINAL : public AstNodeExpr { // @astgen op2 := rhsp : AstNodeExpr // @astgen op3 := thsp : Optional[AstNodeExpr] // @astgen op4 := attrp : Optional[AstAttrOf] + VPurity m_purity; // Pure state + protected: AstNodePreSel(VNType t, FileLine* fl, AstNodeExpr* fromp, AstNodeExpr* rhsp, AstNodeExpr* thsp) : AstNodeExpr{t, fl} { @@ -277,6 +294,14 @@ public: string emitVerilog() final override { V3ERROR_NA_RETURN(""); } string emitC() final override { V3ERROR_NA_RETURN(""); } bool cleanOut() const final override { V3ERROR_NA_RETURN(true); } + bool isPure() override; + const char* broken() const override; + void clearCachedPurity() override; + +private: + bool getPurity() const { + return fromp()->isPure() && rhsp()->isPure() && (!thsp() || thsp()->isPure()); + } }; class AstNodeQuadop VL_NOT_FINAL : public AstNodeExpr { // 4-ary expression @@ -284,6 +309,8 @@ class AstNodeQuadop VL_NOT_FINAL : public AstNodeExpr { // @astgen op2 := rhsp : AstNodeExpr // @astgen op3 := thsp : AstNodeExpr // @astgen op4 := fhsp : AstNodeExpr + VPurity m_purity; // Pure state + protected: AstNodeQuadop(VNType t, FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* thsp, AstNodeExpr* fhsp) @@ -311,6 +338,14 @@ public: virtual bool sizeMattersFhs() const = 0; // True if output result depends on ths size int instrCount() const override { return widthInstrs(); } bool same(const AstNode*) const override { return true; } + bool isPure() override; + const char* broken() const override; + void clearCachedPurity() override; + +private: + bool getPurity() const { + return lhsp()->isPure() && rhsp()->isPure() && thsp()->isPure() && fhsp()->isPure(); + } }; class AstNodeTermop VL_NOT_FINAL : public AstNodeExpr { // Terminal operator -- an operator with no "inputs" @@ -330,6 +365,8 @@ class AstNodeTriop VL_NOT_FINAL : public AstNodeExpr { // @astgen op1 := lhsp : AstNodeExpr // @astgen op2 := rhsp : AstNodeExpr // @astgen op3 := thsp : AstNodeExpr + VPurity m_purity; // Pure state + protected: AstNodeTriop(VNType t, FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* thsp) : AstNodeExpr{t, fl} { @@ -354,6 +391,12 @@ public: virtual bool sizeMattersThs() const = 0; // True if output result depends on ths size int instrCount() const override { return widthInstrs(); } bool same(const AstNode*) const override { return true; } + bool isPure() override; + const char* broken() const override; + void clearCachedPurity() override; + +private: + bool getPurity() const { return lhsp()->isPure() && rhsp()->isPure() && thsp()->isPure(); } }; class AstNodeCond VL_NOT_FINAL : public AstNodeTriop { // @astgen alias op1 := condp @@ -403,6 +446,8 @@ public: class AstNodeUniop VL_NOT_FINAL : public AstNodeExpr { // Unary expression // @astgen op1 := lhsp : AstNodeExpr + VPurity m_purity; // Pure state + protected: AstNodeUniop(VNType t, FileLine* fl, AstNodeExpr* lhsp) : AstNodeExpr{t, fl} { @@ -424,6 +469,9 @@ public: virtual bool stringFlavor() const { return false; } // N flavor of nodes with both flavors? int instrCount() const override { return widthInstrs(); } bool same(const AstNode*) const override { return true; } + bool isPure() override; + const char* broken() const override; + void clearCachedPurity() override; }; class AstNodeSystemUniopD VL_NOT_FINAL : public AstNodeUniop { public: @@ -600,7 +648,7 @@ public: const AstCMethodHard* asamep = static_cast(samep); return (m_name == asamep->m_name); } - bool isPure() const override { return m_pure; } + bool isPure() override { return m_pure; } int instrCount() const override; string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } @@ -1121,7 +1169,7 @@ public: string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } bool cleanOut() const override { return true; } - bool isPure() const override { return false; } + bool isPure() override { return false; } bool same(const AstNode*) const override { return true; } }; class AstFError final : public AstNodeExpr { @@ -1139,7 +1187,7 @@ public: bool cleanOut() const override { return true; } int instrCount() const override { return widthInstrs() * 64; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering + bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering bool same(const AstNode* /*samep*/) const override { return true; } }; class AstFOpen final : public AstNodeExpr { @@ -1158,8 +1206,8 @@ public: string verilogKwd() const override { return "$fopen"; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } - bool isOutputter() const override { return true; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } bool isUnlikely() const override { return true; } bool same(const AstNode* /*samep*/) const override { return true; } }; @@ -1177,8 +1225,8 @@ public: string verilogKwd() const override { return "$fopen"; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } - bool isOutputter() const override { return true; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } bool isUnlikely() const override { return true; } bool same(const AstNode* /*samep*/) const override { return true; } }; @@ -1201,8 +1249,8 @@ public: string emitC() override { V3ERROR_NA_RETURN(""); } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } // SPECIAL: has 'visual' ordering - bool isOutputter() const override { return true; } // SPECIAL: makes output + bool isPure() override { return false; } // SPECIAL: has 'visual' ordering + bool isOutputter() override { return true; } // SPECIAL: makes output bool cleanOut() const override { return false; } bool same(const AstNode* /*samep*/) const override { return true; } }; @@ -1219,8 +1267,8 @@ public: string emitC() override { V3ERROR_NA_RETURN(""); } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } - bool isOutputter() const override { return true; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } bool isUnlikely() const override { return true; } bool cleanOut() const override { return false; } bool same(const AstNode* /*samep*/) const override { return true; } @@ -1244,8 +1292,8 @@ public: string emitC() override { V3ERROR_NA_RETURN(""); } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } // SPECIAL: has 'visual' ordering - bool isOutputter() const override { return true; } // SPECIAL: makes output + bool isPure() override { return false; } // SPECIAL: has 'visual' ordering + bool isOutputter() override { return true; } // SPECIAL: makes output bool cleanOut() const override { return false; } bool same(const AstNode* samep) const override { return text() == static_cast(samep)->text(); @@ -1270,8 +1318,8 @@ public: string emitC() override { V3ERROR_NA_RETURN(""); } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } // SPECIAL: has 'visual' ordering - bool isOutputter() const override { return true; } // SPECIAL: makes output + bool isPure() override { return false; } // SPECIAL: has 'visual' ordering + bool isOutputter() override { return true; } // SPECIAL: makes output bool cleanOut() const override { return false; } bool same(const AstNode* /*samep*/) const override { return true; } }; @@ -1288,8 +1336,8 @@ public: string emitC() override { V3ERROR_NA_RETURN(""); } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } - bool isOutputter() const override { return true; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } bool isUnlikely() const override { return true; } bool cleanOut() const override { return false; } bool same(const AstNode* /*samep*/) const override { return true; } @@ -1766,8 +1814,8 @@ public: string emitC() override { V3ERROR_NA_RETURN(""); } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } // SPECIAL: has 'visual' ordering - bool isOutputter() const override { return true; } // SPECIAL: makes output + bool isPure() override { return false; } // SPECIAL: has 'visual' ordering + bool isOutputter() override { return true; } // SPECIAL: makes output bool cleanOut() const override { return false; } bool same(const AstNode* samep) const override { return text() == static_cast(samep)->text(); @@ -1924,8 +1972,8 @@ public: string emitC() override { return "VL_STACKTRACE_N()"; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } - bool isOutputter() const override { return true; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } bool isUnlikely() const override { return true; } bool cleanOut() const override { return true; } bool same(const AstNode* /*samep*/) const override { return true; } @@ -1970,8 +2018,8 @@ public: string verilogKwd() const override { return "$ignored"; } bool isGateOptimizable() const override { return false; } // Though deleted before opt bool isPredictOptimizable() const override { return false; } // Though deleted before opt - bool isPure() const override { return false; } // Though deleted before opt - bool isOutputter() const override { return true; } // Though deleted before opt + bool isPure() override { return false; } // Though deleted before opt + bool isOutputter() override { return true; } // Though deleted before opt int instrCount() const override { return INSTR_COUNT_PLI; } string emitVerilog() override { V3ERROR_NA_RETURN(""); } @@ -1992,8 +2040,8 @@ public: string emitC() override { return "VL_SYSTEM_%nq(%lw, %P)"; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } - bool isOutputter() const override { return true; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } bool isUnlikely() const override { return true; } bool cleanOut() const override { return true; } bool same(const AstNode* /*samep*/) const override { return true; } @@ -2076,8 +2124,8 @@ public: bool cleanOut() const override { return false; } string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } - bool isPure() const override { return false; } // SPECIAL: User may order w/other sigs - bool isOutputter() const override { return true; } + bool isPure() override { return false; } // SPECIAL: User may order w/other sigs + bool isOutputter() override { return true; } bool isGateOptimizable() const override { return false; } bool isSubstOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } @@ -2132,7 +2180,7 @@ public: string emitC() override { V3ERROR_NA_RETURN(""); } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return !outp(); } + bool isPure() override { return !outp(); } bool cleanOut() const override { return true; } bool same(const AstNode* /*samep*/) const override { return true; } }; @@ -2239,7 +2287,7 @@ public: bool sizeMattersLhs() const override { return false; } bool sizeMattersRhs() const override { return false; } int instrCount() const override { return widthInstrs() * 20; } - bool isPure() const override { return true; } + bool isPure() override { return true; } }; class AstCompareNN final : public AstNodeBiop { // Verilog str.compare() and str.icompare() @@ -2463,7 +2511,7 @@ public: bool sizeMattersRhs() const override { return false; } int instrCount() const override { return widthInstrs() * 64; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering + bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering AstNode* filep() const { return lhsp(); } AstNode* charp() const { return rhsp(); } }; @@ -2748,11 +2796,6 @@ public: int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; } }; class AstLogOr final : public AstNodeBiop { - // LOGOR with optional side effects - // Side effects currently used in some V3Width code - // TBD if this concept is generally adopted for side-effect tracking - // versus V3Const tracking it itself - bool m_sideEffect = false; // Has side effect, relies on short-circuiting public: AstLogOr(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp) : ASTGEN_SUPER_LogOr(fl, lhsp, rhsp) { @@ -2765,11 +2808,6 @@ public: void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override { out.opLogOr(lhs, rhs); } - bool same(const AstNode* samep) const override { - const AstLogOr* const sp = static_cast(samep); - return m_sideEffect == sp->m_sideEffect; - } - void dump(std::ostream& str = std::cout) const override; string emitVerilog() override { return "%k(%l %f|| %r)"; } string emitC() override { return "VL_LOGOR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } string emitSimpleOperator() override { return "||"; } @@ -2779,9 +2817,6 @@ public: bool sizeMattersLhs() const override { return false; } bool sizeMattersRhs() const override { return false; } int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; } - bool isPure() const override { return !m_sideEffect; } - void sideEffect(bool flag) { m_sideEffect = flag; } - bool sideEffect() const { return m_sideEffect; } }; class AstLt final : public AstNodeBiop { public: @@ -4760,7 +4795,7 @@ public: bool sizeMattersLhs() const override { return false; } int instrCount() const override { return widthInstrs() * 16; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering + bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering AstNode* filep() const { return lhsp(); } }; class AstFGetC final : public AstNodeUniop { @@ -4777,7 +4812,7 @@ public: bool sizeMattersLhs() const override { return false; } int instrCount() const override { return widthInstrs() * 64; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering + bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering AstNode* filep() const { return lhsp(); } }; class AstISToRD final : public AstNodeUniop { diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index ffb7ef422..23e1b0891 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -88,6 +88,8 @@ private: bool m_virtual : 1; // Virtual method in class bool m_needProcess : 1; // Implements part of a process that allocates std::process VLifetime m_lifetime; // Lifetime + VPurity m_purity; // Pure state + protected: AstNodeFTask(VNType t, FileLine* fl, const string& name, AstNode* stmtsp) : AstNode{t, fl} @@ -180,6 +182,8 @@ public: void lifetime(const VLifetime& flag) { m_lifetime = flag; } VLifetime lifetime() const { return m_lifetime; } bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } + bool isPure() override; + const char* broken() const override; void propagateAttrFrom(const AstNodeFTask* fromp) { // Creating a wrapper with e.g. cloneType(); preserve some attributes classMethod(fromp->classMethod()); @@ -189,6 +193,9 @@ public: lifetime(fromp->lifetime()); underGenerate(fromp->underGenerate()); } + +private: + bool getPurity() const; }; class AstNodeFile VL_NOT_FINAL : public AstNode { // Emitted Output file @@ -469,8 +476,8 @@ public: ASTGEN_MEMBERS_AstNodeReadWriteMem; bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } - bool isOutputter() const override { return true; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } bool isUnlikely() const override { return true; } bool same(const AstNode* samep) const override { return isHex() == static_cast(samep)->isHex(); @@ -994,8 +1001,8 @@ public: string verilogKwd() const override { return string{"$"} + string{displayType().ascii()}; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering - bool isOutputter() const override { return true; } // SPECIAL: $display makes output + bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering + bool isOutputter() override { return true; } // SPECIAL: $display makes output bool isUnlikely() const override { return true; } bool same(const AstNode* samep) const override { return displayType() == static_cast(samep)->displayType(); @@ -1915,6 +1922,7 @@ public: bool isInoutish() const { return m_direction.isInoutish(); } bool isNonOutput() const { return m_direction.isNonOutput(); } bool isReadOnly() const VL_MT_SAFE { return m_direction.isReadOnly(); } + bool isRef() const VL_MT_SAFE { return m_direction.isRef(); } bool isWritable() const VL_MT_SAFE { return m_direction.isWritable(); } bool isTristate() const { return m_tristate; } bool isPrimaryIO() const { return m_primaryIO; } @@ -2616,7 +2624,7 @@ public: } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isOutputter() const override { return true; } + bool isOutputter() override { return true; } // but isPure() true AstCoverDecl* declp() const { return m_declp; } // Where defined }; @@ -2638,7 +2646,7 @@ public: bool same(const AstNode* /*samep*/) const override { return true; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return true; } - bool isOutputter() const override { + bool isOutputter() override { return false; // Though the AstCoverInc under this is an outputter } // but isPure() true @@ -2715,8 +2723,8 @@ public: } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering - bool isOutputter() const override { return true; } // SPECIAL: $display makes output + bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering + bool isOutputter() override { return true; } // SPECIAL: $display makes output bool isUnlikely() const override { return true; } bool same(const AstNode* samep) const override { return displayType() == static_cast(samep)->displayType(); @@ -2758,7 +2766,7 @@ public: string verilogKwd() const override { return ctlType().ascii(); } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isOutputter() const override { return true; } + bool isOutputter() override { return true; } virtual bool cleanOut() const { return true; } bool same(const AstNode* /*samep*/) const override { return true; } VDumpCtlType ctlType() const { return m_ctlType; } @@ -2790,8 +2798,8 @@ public: string verilogKwd() const override { return "$fclose"; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } - bool isOutputter() const override { return true; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } bool isUnlikely() const override { return true; } bool same(const AstNode* /*samep*/) const override { return true; } }; @@ -2807,8 +2815,8 @@ public: string verilogKwd() const override { return "$fflush"; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } - bool isOutputter() const override { return true; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } bool isUnlikely() const override { return true; } bool same(const AstNode* /*samep*/) const override { return true; } }; @@ -2819,8 +2827,8 @@ public: ASTGEN_MEMBERS_AstFinish; bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering - bool isOutputter() const override { return true; } // SPECIAL: $display makes output + bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering + bool isOutputter() override { return true; } // SPECIAL: $display makes output bool isUnlikely() const override { return true; } int instrCount() const override { return 0; } // Rarely executes bool same(const AstNode* samep) const override { return fileline() == samep->fileline(); } @@ -2942,8 +2950,8 @@ public: string verilogKwd() const override { return m_off ? "$monitoroff" : "$monitoron"; } bool isGateOptimizable() const override { return false; } // Though deleted before opt bool isPredictOptimizable() const override { return false; } // Though deleted before opt - bool isPure() const override { return false; } // Though deleted before opt - bool isOutputter() const override { return true; } // Though deleted before opt + bool isPure() override { return false; } // Though deleted before opt + bool isOutputter() override { return true; } // Though deleted before opt int instrCount() const override { return INSTR_COUNT_PLI; } bool same(const AstNode* samep) const override { return m_off == static_cast(samep)->m_off; @@ -2964,8 +2972,8 @@ public: string verilogKwd() const override { return "$printtimescale"; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } - bool isOutputter() const override { return true; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } int instrCount() const override { return INSTR_COUNT_PLI; } void timeunit(const VTimescale& flag) { m_timeunit = flag; } VTimescale timeunit() const { return m_timeunit; } @@ -3042,8 +3050,8 @@ public: string verilogKwd() const override { return "$sformat"; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return true; } - bool isPure() const override { return true; } - bool isOutputter() const override { return false; } + bool isPure() override { return true; } + bool isOutputter() override { return false; } virtual bool cleanOut() const { return false; } int instrCount() const override { return INSTR_COUNT_PLI; } bool same(const AstNode* /*samep*/) const override { return true; } @@ -3057,8 +3065,8 @@ public: string verilogKwd() const override { return "$stacktrace"; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } - bool isOutputter() const override { return true; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } bool isUnlikely() const override { return true; } bool same(const AstNode* /*samep*/) const override { return true; } }; @@ -3079,8 +3087,8 @@ public: ASTGEN_MEMBERS_AstStop; bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering - bool isOutputter() const override { return true; } // SPECIAL: $display makes output + bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering + bool isOutputter() override { return true; } // SPECIAL: $display makes output bool isUnlikely() const override { return true; } int instrCount() const override { return 0; } // Rarely executes bool same(const AstNode* samep) const override { return fileline() == samep->fileline(); } @@ -3098,8 +3106,8 @@ public: string verilogKwd() const override { return ""; } bool isGateOptimizable() const override { return true; } bool isPredictOptimizable() const override { return true; } - bool isPure() const override { return true; } - bool isOutputter() const override { return false; } + bool isPure() override { return true; } + bool isOutputter() override { return false; } int instrCount() const override { return 0; } bool same(const AstNode* /*samep*/) const override { return true; } }; @@ -3115,8 +3123,8 @@ public: string verilogKwd() const override { return "$system"; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } - bool isOutputter() const override { return true; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } bool isUnlikely() const override { return true; } bool same(const AstNode* /*samep*/) const override { return true; } }; @@ -3139,8 +3147,8 @@ public: string verilogKwd() const override { return "$timeformat"; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } - bool isOutputter() const override { return true; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } int instrCount() const override { return INSTR_COUNT_PLI; } }; class AstTraceDecl final : public AstNodeStmt { @@ -3229,7 +3237,7 @@ public: } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isOutputter() const override { return true; } + bool isOutputter() override { return true; } // but isPure() true AstTraceDecl* declp() const { return m_declp; } bool full() const { return m_full; } @@ -3266,8 +3274,8 @@ public: ASTGEN_MEMBERS_AstUCStmt; bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } - bool isOutputter() const override { return true; } + bool isPure() override { return false; } + bool isOutputter() override { return true; } bool same(const AstNode* /*samep*/) const override { return true; } }; class AstWait final : public AstNodeStmt { @@ -3557,48 +3565,48 @@ public: AstScCtor(FileLine* fl, const string& textp) : ASTGEN_SUPER_ScCtor(fl, textp) {} ASTGEN_MEMBERS_AstScCtor; - bool isPure() const override { return false; } // SPECIAL: User may order w/other sigs - bool isOutputter() const override { return true; } + bool isPure() override { return false; } // SPECIAL: User may order w/other sigs + bool isOutputter() override { return true; } }; class AstScDtor final : public AstNodeText { public: AstScDtor(FileLine* fl, const string& textp) : ASTGEN_SUPER_ScDtor(fl, textp) {} ASTGEN_MEMBERS_AstScDtor; - bool isPure() const override { return false; } // SPECIAL: User may order w/other sigs - bool isOutputter() const override { return true; } + bool isPure() override { return false; } // SPECIAL: User may order w/other sigs + bool isOutputter() override { return true; } }; class AstScHdr final : public AstNodeText { public: AstScHdr(FileLine* fl, const string& textp) : ASTGEN_SUPER_ScHdr(fl, textp) {} ASTGEN_MEMBERS_AstScHdr; - bool isPure() const override { return false; } // SPECIAL: User may order w/other sigs - bool isOutputter() const override { return true; } + bool isPure() override { return false; } // SPECIAL: User may order w/other sigs + bool isOutputter() override { return true; } }; class AstScImp final : public AstNodeText { public: AstScImp(FileLine* fl, const string& textp) : ASTGEN_SUPER_ScImp(fl, textp) {} ASTGEN_MEMBERS_AstScImp; - bool isPure() const override { return false; } // SPECIAL: User may order w/other sigs - bool isOutputter() const override { return true; } + bool isPure() override { return false; } // SPECIAL: User may order w/other sigs + bool isOutputter() override { return true; } }; class AstScImpHdr final : public AstNodeText { public: AstScImpHdr(FileLine* fl, const string& textp) : ASTGEN_SUPER_ScImpHdr(fl, textp) {} ASTGEN_MEMBERS_AstScImpHdr; - bool isPure() const override { return false; } // SPECIAL: User may order w/other sigs - bool isOutputter() const override { return true; } + bool isPure() override { return false; } // SPECIAL: User may order w/other sigs + bool isOutputter() override { return true; } }; class AstScInt final : public AstNodeText { public: AstScInt(FileLine* fl, const string& textp) : ASTGEN_SUPER_ScInt(fl, textp) {} ASTGEN_MEMBERS_AstScInt; - bool isPure() const override { return false; } // SPECIAL: User may order w/other sigs - bool isOutputter() const override { return true; } + bool isPure() override { return false; } // SPECIAL: User may order w/other sigs + bool isOutputter() override { return true; } }; // === AstNodeSimpleText === diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 9a35b190b..29fbf3902 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -51,6 +51,7 @@ AstIface* AstIfaceRefDType::ifaceViaCellp() const { const char* AstNodeFTaskRef::broken() const { BROKEN_RTN(m_taskp && !m_taskp->brokeExists()); BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists()); + BROKEN_RTN(m_purity.isCached() && m_purity.isPure() != getPurity()); return nullptr; } @@ -61,12 +62,34 @@ void AstNodeFTaskRef::cloneRelink() { } } -bool AstNodeFTaskRef::isPure() const { - // TODO: For non-DPI functions we could traverse the AST of function's body to determine - // pureness. - return this->taskp() && this->taskp()->dpiImport() && this->taskp()->dpiPure(); +bool AstNodeFTaskRef::isPure() { + if (!this->taskp()) { + // The task isn't linked yet, so it's assumed that it is impure, but the value shouldn't be + // cached. + return false; + } else { + if (!m_purity.isCached()) m_purity.setPurity(this->getPurity()); + return m_purity.isPure(); + } } +void AstNodeFTaskRef::clearCachedPurity() { + m_purity.clearCache(); + if (AstNodeExpr* const exprp = VN_CAST(backp(), NodeExpr)) exprp->clearCachedPurity(); +} + +bool AstNodeFTaskRef::getPurity() const { + AstNodeFTask* const taskp = this->taskp(); + // Unlinked yet, so treat as impure + if (!taskp) return false; + + // First compute the purity of arguments + for (AstNode* pinp = this->pinsp(); pinp; pinp = pinp->nextp()) { + if (!pinp->isPure()) return false; + } + + return taskp->isPure(); +} bool AstNodeFTaskRef::isGateOptimizable() const { return m_taskp && m_taskp->isGateOptimizable(); } const char* AstNodeVarRef::broken() const { @@ -121,7 +144,71 @@ const char* AstNodeCCall::broken() const { BROKEN_RTN(m_funcp && !m_funcp->brokeExists()); return nullptr; } -bool AstNodeCCall::isPure() const { return funcp()->dpiPure(); } +bool AstNodeCCall::isPure() { return funcp()->dpiPure(); } +bool AstNodeUniop::isPure() { + if (!m_purity.isCached()) m_purity.setPurity(lhsp()->isPure()); + return m_purity.isPure(); +} +const char* AstNodeUniop::broken() const { + BROKEN_RTN(m_purity.isCached() && m_purity.isPure() != lhsp()->isPure()); + return nullptr; +} +void AstNodeUniop::clearCachedPurity() { + m_purity.clearCache(); + if (AstNodeExpr* const exprp = VN_CAST(backp(), NodeExpr)) exprp->clearCachedPurity(); +} + +bool AstNodeBiop::isPure() { + if (!m_purity.isCached()) m_purity.setPurity(getPurity()); + return m_purity.isPure(); +} +const char* AstNodeBiop::broken() const { + BROKEN_RTN(m_purity.isCached() && m_purity.isPure() != getPurity()); + return nullptr; +} +void AstNodeBiop::clearCachedPurity() { + m_purity.clearCache(); + if (AstNodeExpr* const exprp = VN_CAST(backp(), NodeExpr)) exprp->clearCachedPurity(); +} + +bool AstNodeTriop::isPure() { + if (!m_purity.isCached()) m_purity.setPurity(getPurity()); + return m_purity.isPure(); +} +const char* AstNodeTriop::broken() const { + BROKEN_RTN(m_purity.isCached() && m_purity.isPure() != getPurity()); + return nullptr; +} +void AstNodeTriop::clearCachedPurity() { + m_purity.clearCache(); + if (AstNodeExpr* const exprp = VN_CAST(backp(), NodeExpr)) exprp->clearCachedPurity(); +} + +bool AstNodePreSel::isPure() { + if (!m_purity.isCached()) m_purity.setPurity(getPurity()); + return m_purity.isPure(); +} +const char* AstNodePreSel::broken() const { + BROKEN_RTN(m_purity.isCached() && m_purity.isPure() != getPurity()); + return nullptr; +} +void AstNodePreSel::clearCachedPurity() { + m_purity.clearCache(); + if (AstNodeExpr* const exprp = VN_CAST(backp(), NodeExpr)) exprp->clearCachedPurity(); +} + +bool AstNodeQuadop::isPure() { + if (!m_purity.isCached()) m_purity.setPurity(getPurity()); + return m_purity.isPure(); +} +const char* AstNodeQuadop::broken() const { + BROKEN_RTN(m_purity.isCached() && m_purity.isPure() != getPurity()); + return nullptr; +} +void AstNodeQuadop::clearCachedPurity() { + m_purity.clearCache(); + if (AstNodeExpr* const exprp = VN_CAST(backp(), NodeExpr)) exprp->clearCachedPurity(); +} AstNodeCond::AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep) @@ -1622,10 +1709,6 @@ void AstJumpLabel::dump(std::ostream& str) const { str << "%Error:UNLINKED"; } } -void AstLogOr::dump(std::ostream& str) const { - this->AstNodeExpr::dump(str); - if (sideEffect()) str << " [SIDE]"; -} void AstMemberDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); @@ -2218,6 +2301,31 @@ void AstNodeFTask::dump(std::ostream& str) const { if (taskPublic()) str << " [PUBLIC]"; if ((dpiImport() || dpiExport()) && cname() != name()) str << " [c=" << cname() << "]"; } +bool AstNodeFTask::isPure() { + if (!m_purity.isCached()) m_purity.setPurity(getPurity()); + return m_purity.isPure(); +} +const char* AstNodeFTask::broken() const { + BROKEN_RTN(m_purity.isCached() && m_purity.isPure() != getPurity()); + return nullptr; +} +bool AstNodeFTask::getPurity() const { + if (this->dpiImport()) return this->dpiPure(); + // Check the list of statements if it contains any impure statement + // or any write reference to a variable that isn't an automatic function local. + for (AstNode* stmtp = this->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + if (const AstVar* const varp = VN_CAST(stmtp, Var)) { + if (varp->isInoutish() || varp->isRef()) return false; + } + if (!stmtp->isPure()) return false; + if (stmtp->exists([](const AstNodeVarRef* const varrefp) { + return (!varrefp->varp()->isFuncLocal() || varrefp->varp()->lifetime().isStatic()) + && varrefp->access().isWriteOrRW(); + })) + return false; + } + return true; +} void AstNodeBlock::dump(std::ostream& str) const { this->AstNode::dump(str); if (unnamed()) str << " [UNNAMED]"; diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 888bd7780..b96b2fee8 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -1217,13 +1217,13 @@ private: bool operandHugeShiftL(const AstNodeBiop* nodep) { return (VN_IS(nodep->rhsp(), Const) && !VN_AS(nodep->rhsp(), Const)->num().isFourState() && (VN_AS(nodep->rhsp(), Const)->toUInt() >= static_cast(nodep->width())) - && isTPure(nodep->lhsp())); + && nodep->lhsp()->isPure()); } bool operandHugeShiftR(const AstNodeBiop* nodep) { return (VN_IS(nodep->rhsp(), Const) && !VN_AS(nodep->rhsp(), Const)->num().isFourState() && (VN_AS(nodep->rhsp(), Const)->toUInt() >= static_cast(nodep->lhsp()->width())) - && isTPure(nodep->lhsp())); + && nodep->lhsp()->isPure()); } bool operandIsTwo(const AstNode* nodep) { return (VN_IS(nodep, Const) && !VN_AS(nodep, Const)->num().isFourState() @@ -1377,14 +1377,6 @@ 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 masse until V3Purify takes effect. - return m_doShort || VN_IS(nodep, VarRef) || VN_IS(nodep, Const); - } - // Extraction checks bool warnSelect(AstSel* nodep) { if (m_doGenerate) { @@ -1587,7 +1579,7 @@ private: // 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)) { + if (checkp->isPure()) { VL_DO_DANGLING(replaceNum(nodep, 0), nodep); } else { AstNode* const newp = new AstAnd{nodep->fileline(), new AstConst{nodep->fileline(), 0}, @@ -2053,7 +2045,7 @@ private: VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); return true; } - } else if (m_doV && VN_IS(nodep->lhsp(), Concat) && nodep->isTreePureRecurse()) { + } else if (m_doV && VN_IS(nodep->lhsp(), Concat) && nodep->isPure()) { bool need_temp = false; if (m_warn && !VN_IS(nodep, AssignDly)) { // Is same var on LHS and RHS? // Note only do this (need user4) when m_warn, which is @@ -3014,7 +3006,7 @@ private: } VL_DO_DANGLING(nodep->deleteTree(), nodep); } else if (!afterComment(nodep->thensp()) && !afterComment(nodep->elsesp())) { - if (!nodep->condp()->isTreePureRecurse()) { + if (!nodep->condp()->isPure()) { // Condition has side effect - leave - perhaps in // future simplify to remove all but side effect terms } else { @@ -3374,7 +3366,7 @@ private: TREEOPA("AstNodeQuadop{$lhsp.castConst, $rhsp.castConst, $thsp.castConst, $fhsp.castConst}", "replaceConst(nodep)"); // Zero on one side or the other TREEOP ("AstAdd {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); - TREEOP ("AstAnd {$lhsp.isZero, $rhsp, isTPure($rhsp)}", "replaceZero(nodep)"); // Can't use replaceZeroChkPure as we make this pattern in ChkPure + TREEOP ("AstAnd {$lhsp.isZero, $rhsp, $rhsp.isPure}", "replaceZero(nodep)"); // Can't use replaceZeroChkPure as we make this pattern in ChkPure // This visit function here must allow for short-circuiting. TREEOPS("AstLogAnd {$lhsp.isZero}", "replaceZero(nodep)"); TREEOP ("AstLogAnd{$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); @@ -3414,12 +3406,12 @@ private: // Non-zero on one side or the other TREEOP ("AstAnd {$lhsp.isAllOnes, $rhsp}", "replaceWRhs(nodep)"); TREEOP ("AstLogAnd{$lhsp.isNeqZero, $rhsp}", "replaceWRhsBool(nodep)"); - TREEOP ("AstOr {$lhsp.isAllOnes, $rhsp, isTPure($rhsp)}", "replaceWLhs(nodep)"); // ->allOnes + TREEOP ("AstOr {$lhsp.isAllOnes, $rhsp, $rhsp.isPure}", "replaceWLhs(nodep)"); // ->allOnes TREEOP ("AstLogOr {$lhsp.isNeqZero, $rhsp}", "replaceNum(nodep,1)"); TREEOP ("AstAnd {$lhsp, $rhsp.isAllOnes}", "replaceWLhs(nodep)"); TREEOP ("AstLogAnd{$lhsp, $rhsp.isNeqZero}", "replaceWLhsBool(nodep)"); - TREEOP ("AstOr {$lhsp, $rhsp.isAllOnes, isTPure($lhsp)}", "replaceWRhs(nodep)"); // ->allOnes - TREEOP ("AstLogOr {$lhsp, $rhsp.isNeqZero, isTPure($lhsp), nodep->isPure()}", "replaceNum(nodep,1)"); + TREEOP ("AstOr {$lhsp, $rhsp.isAllOnes, $lhsp.isPure}", "replaceWRhs(nodep)"); // ->allOnes + TREEOP ("AstLogOr {$lhsp, $rhsp.isNeqZero, $lhsp.isPure, nodep->isPure()}", "replaceNum(nodep,1)"); TREEOP ("AstXor {$lhsp.isAllOnes, $rhsp}", "AstNot{$rhsp}"); TREEOP ("AstMul {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)"); TREEOP ("AstMulS {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)"); @@ -3591,9 +3583,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, isTPure($lhsp), isTPure($rhsp)}", "AstAnd{$lhsp,$rhsp}"); - TREEOPV("AstLogOr {$lhsp.width1, $rhsp.width1, nodep->isPure(), isTPure($lhsp), isTPure($rhsp)}", "AstOr{$lhsp,$rhsp}"); - TREEOPV("AstLogNot{$lhsp.width1, isTPure($lhsp)}", "AstNot{$lhsp}"); + TREEOPV("AstLogAnd{$lhsp.width1, $rhsp.width1, nodep->isPure()}", "AstAnd{$lhsp,$rhsp}"); + TREEOPV("AstLogOr {$lhsp.width1, $rhsp.width1, nodep->isPure()}", "AstOr{$lhsp,$rhsp}"); + TREEOPV("AstLogNot{$lhsp.width1}", "AstNot{$lhsp}"); // CONCAT(CONCAT({a},{b}),{c}) -> CONCAT({a},CONCAT({b},{c})) // CONCAT({const},CONCAT({const},{c})) -> CONCAT((constifiedCONC{const|const},{c})) TREEOPV("AstConcat{matchConcatRand(nodep)}", "DONE"); diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index e97ce6ec6..09e486a12 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -170,7 +170,10 @@ private: void visit(AstNodeFTaskRef* nodep) override { // Remove link. V3LinkDot will reestablish it after inlining. // MethodCalls not currently supported by inliner, so keep linked - if (!nodep->classOrPackagep() && !VN_IS(nodep, MethodCall)) nodep->taskp(nullptr); + if (!nodep->classOrPackagep() && !VN_IS(nodep, MethodCall)) { + nodep->taskp(nullptr); + nodep->clearCachedPurity(); + } iterateChildren(nodep); } void visit(AstAlways* nodep) override { diff --git a/src/V3LinkInc.cpp b/src/V3LinkInc.cpp index 6c0834182..164f005c2 100644 --- a/src/V3LinkInc.cpp +++ b/src/V3LinkInc.cpp @@ -222,7 +222,7 @@ private: // Currently we can't reference the target, so we just copy the AST both for read and // write, but doing so would double any side-effects, so as a safety measure all // statements which could have side-effects are banned at the moment. - if (!nodep->rhsp()->isTreePureRecurse()) { + if (!nodep->rhsp()->isPure()) { nodep->rhsp()->v3warn(E_UNSUPPORTED, "Unsupported: Inc/Dec of expression with side-effects"); return; @@ -252,7 +252,7 @@ private: // Currently we can't reference the target, so we just copy the AST both for read and // write, but doing so would double any side-effects, so as a safety measure all // statements which could have side-effects are banned at the moment. - if (!nodep->rhsp()->isTreePureRecurse()) { + if (!nodep->rhsp()->isPure()) { nodep->rhsp()->v3warn(E_UNSUPPORTED, "Unsupported: Inc/Dec of expression with side-effects"); return; diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index 9b58dc8b4..39dbdb9ad 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -346,6 +346,7 @@ private: // We're going to need the expression several times in the expanded code, // so might as well make it a common expression createDeepTemp(nodep->condp(), false); + nodep->clearCachedPurity(); } checkNode(nodep); } diff --git a/src/V3Scope.cpp b/src/V3Scope.cpp index b5a31ea51..8156ec1e3 100644 --- a/src/V3Scope.cpp +++ b/src/V3Scope.cpp @@ -394,6 +394,7 @@ private: UINFO(9, " New pkg-taskref " << nodep << endl); } else if (!VN_IS(nodep, MethodCall)) { nodep->taskp(nullptr); + nodep->clearCachedPurity(); UINFO(9, " New pkg-taskref " << nodep << endl); } iterateChildren(nodep); diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 1170f3326..246748d59 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -1437,6 +1437,9 @@ private: "funcref-like expression to non-function"); AstVarRef* const outrefp = new AstVarRef{nodep->fileline(), outvscp, VAccess::READ}; beginp = new AstExprStmt{nodep->fileline(), beginp, outrefp}; + // AstExprStmt is currently treated as impure, so clear the cached purity of its + // parents + nodep->clearCachedPurity(); nodep->replaceWith(beginp); VL_DO_DANGLING(nodep->deleteTree(), nodep); } else { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 756cf3dc9..1e2be1013 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -4613,7 +4613,6 @@ private: new AstConst{fl, AstConst::BitFalse{}}}; auto* const orp = new AstLogOr{fl, new AstVarRef{fl, first_varp, VAccess::READ}, new AstNeq{fl, new AstConst{fl, 0}, nextp}}; - orp->sideEffect(true); AstNode* const whilep = new AstWhile{fl, orp, first_clearp}; first_clearp->addNext(bodyPointp); AstNode* const ifbodyp diff --git a/test_regress/t/t_class_method_str_literal.v b/test_regress/t/t_class_method_str_literal.v index 5f6309c02..54dec5ca5 100644 --- a/test_regress/t/t_class_method_str_literal.v +++ b/test_regress/t/t_class_method_str_literal.v @@ -6,6 +6,24 @@ module t; +class uvm_reg; + function int get_1; + return 1; + endfunction + + function bit get_true; + return 1; + endfunction + + function string get_string; + if (get_1() == 1) begin + return get_true() ? "user backdoor" : "DPI backdoor"; + end else begin + return ""; + end + endfunction +endclass + class T; function automatic string return_str(input string a_string); return a_string; @@ -19,6 +37,8 @@ endclass initial begin T t_c = new; + uvm_reg u_r = new; + if (u_r.get_string() != "user backdoor") $stop; if (t_c.return_str("A") != "A") $stop; if (t_c.static_return_str("B") != "B") $stop; if (T::static_return_str("C") != "C") $stop; diff --git a/test_regress/t/t_const_opt_shortcut.pl b/test_regress/t/t_const_opt_shortcut.pl index 8955f94f0..beae879d6 100755 --- a/test_regress/t/t_const_opt_shortcut.pl +++ b/test_regress/t/t_const_opt_shortcut.pl @@ -16,10 +16,7 @@ compile( ); execute( - # Shortcut is not properly implemented yet as in https://github.com/verilator/verilator/issues/487 - # When the issue is fixed, change the following two lines. - check_finished => 0, - fails => $Self->{vlt_all}, + check_finished => 1, ); ok(1); diff --git a/test_regress/t/t_dpi_shortcircuit.out b/test_regress/t/t_dpi_shortcircuit.out deleted file mode 100644 index 2cc554bcf..000000000 --- a/test_regress/t/t_dpi_shortcircuit.out +++ /dev/null @@ -1,20 +0,0 @@ -%Error: Line 62: Bad result, got=1 expect=0 -%Error: Line 66: Bad result, got=1 expect=0 -%Error: Line 69: Bad result, got=0 expect=1 -%Error: Line 81: Bad result, got=0 expect=1 -%Error: Line 91: Bad result, got=0 expect=1 -%Error: Line 108: Bad result, got=1 expect=0 -%Error: Line 112: Bad result, got=1 expect=0 -%Error: Line 114: Bad result, got=0 expect=1 -%Error: Line 126: Bad result, got=0 expect=1 -%Error: Line 136: Bad result, got=0 expect=1 -%Error: Line 148: Bad result, got=1 expect=0 -%Error: Line 152: Bad result, got=1 expect=0 -%Error: Line 179: Bad result, got=0 expect=1 -%Error: Line 219: Bad result, got=64 expect=32 -%Error: Line 220: Bad result, got=64 expect=16 -%Error: Line 221: Bad result, got=64 expect=16 -%Error: Line 222: Bad result, got=64 expect=36 -%Error: Line 223: Bad result, got=64 expect=46 -%Error: t/t_dpi_shortcircuit.v:225: Verilog $stop -Aborting... diff --git a/test_regress/t/t_dpi_shortcircuit.pl b/test_regress/t/t_dpi_shortcircuit.pl index ee25e9d79..b109f6b46 100755 --- a/test_regress/t/t_dpi_shortcircuit.pl +++ b/test_regress/t/t_dpi_shortcircuit.pl @@ -16,10 +16,7 @@ compile( ); execute( - # Should pass, Verilator unsupported, bug413 short circuit"); - fails => $Self->{vlt}, - check_finished => !$Self->{vlt}, - expect_filename => $Self->{golden_filename}, + check_finished => 1, ); ok(1);