mirror of
https://github.com/verilator/verilator.git
synced 2025-01-03 21:27:35 +00:00
This commit is contained in:
parent
cd201f8eb8
commit
47b3f464a9
@ -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],
|
||||
|
@ -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.
|
||||
|
22
src/V3Ast.h
22
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.
|
||||
|
@ -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 {
|
||||
|
@ -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 ===
|
||||
|
@ -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]";
|
||||
|
@ -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");
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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...
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user