Disable conversion of impure logical expressions to bitwise expressions (#487 partial) (#4437)

This commit is contained in:
Ryszard Rozak 2023-09-18 15:21:30 +02:00 committed by GitHub
parent cd201f8eb8
commit 47b3f464a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 318 additions and 169 deletions

View File

@ -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],

View File

@ -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.

View File

@ -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.

View File

@ -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<const AstCMethodHard*>(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<const AstFScanF*>(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<const AstSScanF*>(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<const AstLogOr*>(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 {

View File

@ -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<const AstNodeReadWriteMem*>(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<const AstElabDisplay*>(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<const AstDisplay*>(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<const AstMonitorOff*>(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 ===

View File

@ -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]";

View File

@ -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<uint32_t>(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<uint32_t>(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");

View File

@ -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 {

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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 {

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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...

View File

@ -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);