diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 0224ff574..e57d28ee5 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1341,12 +1341,6 @@ bool AstNode::isTreePureRecurse() const { return true; } -void AstNode::v3errorEndFatal(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) { - v3errorEnd(str); - assert(0); // LCOV_EXCL_LINE - VL_UNREACHABLE; -} - string AstNode::instanceStr() const { // Max iterations before giving up on location search, // in case we have some circular reference bug. @@ -1371,9 +1365,9 @@ string AstNode::instanceStr() const { return ""; } -void AstNode::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) { +void AstNode::v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) { if (!m_fileline) { - V3Error::s().v3errorEnd(str, instanceStr()); + V3Error::v3errorEnd(str, instanceStr()); } else { std::ostringstream nsstr; nsstr << str.str(); @@ -1390,6 +1384,11 @@ void AstNode::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s() nsstr, m_fileline->warnIsOff(V3Error::s().errorCode()) ? "" : instanceStr()); } } +void AstNode::v3errorEndFatal(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) { + v3errorEnd(str); + assert(0); // LCOV_EXCL_LINE + VL_UNREACHABLE; +} //====================================================================== // Data type conversion diff --git a/src/V3Ast.h b/src/V3Ast.h index 337d948a7..269197806 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1916,9 +1916,9 @@ public: static AstBasicDType* findInsertSameDType(AstBasicDType* nodep); // METHODS - dump and error - void v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex); + void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex); void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN - VL_REQUIRES(V3Error::s().m_mutex); + VL_RELEASE(V3Error::s().m_mutex); string warnContextPrimary() const VL_REQUIRES(V3Error::s().m_mutex) { return fileline()->warnContextPrimary(); } diff --git a/src/V3Dfg.h b/src/V3Dfg.h index 88c868192..e0d72335b 100644 --- a/src/V3Dfg.h +++ b/src/V3Dfg.h @@ -503,11 +503,11 @@ public: inline bool inlined() const; // Methods that allow DfgVertex to participate in error reporting/messaging - void v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) { + void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) { m_filelinep->v3errorEnd(str); } void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN - VL_REQUIRES(V3Error::s().m_mutex) { + VL_RELEASE(V3Error::s().m_mutex) { m_filelinep->v3errorEndFatal(str); } string warnContextPrimary() const VL_REQUIRES(V3Error::s().m_mutex) { diff --git a/src/V3Error.cpp b/src/V3Error.cpp index 57ec24e91..48966be77 100644 --- a/src/V3Error.cpp +++ b/src/V3Error.cpp @@ -291,3 +291,35 @@ void V3Error::vlAbort() { VL_GCOV_DUMP(); std::abort(); } +void V3Error::v3errorAcquireLock() VL_ACQUIRE(s().m_mutex) { +#ifndef V3ERROR_NO_GLOBAL_ + V3Error::s().m_mutex.lockCheckStopRequest( + []() -> void { V3ThreadPool::s().waitIfStopRequested(); }); +#else + V3Error::s().m_mutex.lock(); +#endif +} +std::ostringstream& V3Error::v3errorPrep(V3ErrorCode code) VL_ACQUIRE(s().m_mutex) { + v3errorAcquireLock(); + s().v3errorPrep(code); + return v3errorStr(); +} +std::ostringstream& V3Error::v3errorPrepFileLine(V3ErrorCode code, const char* file, int line) + VL_ACQUIRE(s().m_mutex) { + v3errorPrep(code) << file << ":" << std::dec << line << ": "; + return v3errorStr(); +} +std::ostringstream& V3Error::v3errorStr() VL_REQUIRES(s().m_mutex) { return s().v3errorStr(); } +void V3Error::v3errorEnd(std::ostringstream& sstr, const string& extra) VL_RELEASE(s().m_mutex) { + s().v3errorEnd(sstr, extra); + V3Error::s().m_mutex.unlock(); +} + +void v3errorEnd(std::ostringstream& sstr) VL_RELEASE(V3Error::s().m_mutex) { + V3Error::v3errorEnd(sstr); +} +void v3errorEndFatal(std::ostringstream& sstr) VL_RELEASE(V3Error::s().m_mutex) { + V3Error::v3errorEnd(sstr); + assert(0); // LCOV_EXCL_LINE + VL_UNREACHABLE; +} diff --git a/src/V3Error.h b/src/V3Error.h index e5450675c..f3a815775 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -286,9 +286,13 @@ inline std::ostream& operator<<(std::ostream& os, const V3ErrorCode& rhs) { } // ###################################################################### +class V3Error; + class V3ErrorGuarded final { // Should only be used by V3ErrorGuarded::m_mutex is already locked // contains guarded members + friend class V3Error; + public: using MessagesSet = std::set; using ErrorExitCb = void (*)(void); @@ -319,6 +323,16 @@ private: = MAX_ERRORS; // Option: --error-limit Number of errors before exit bool m_warnFatal VL_GUARDED_BY(m_mutex) = true; // Option: --warnFatal Warnings are fatal std::ostringstream m_errorStr VL_GUARDED_BY(m_mutex); // Error string being formed + + void v3errorPrep(V3ErrorCode code) VL_REQUIRES(m_mutex) { + m_errorStr.str(""); + m_errorCode = code; + m_errorContexted = false; + m_errorSuppressed = false; + } + std::ostringstream& v3errorStr() VL_REQUIRES(m_mutex) { return m_errorStr; } + void v3errorEnd(std::ostringstream& sstr, const string& extra = "") VL_REQUIRES(m_mutex); + public: V3RecursiveMutex m_mutex; // Make sure only single thread is in class @@ -361,13 +375,6 @@ public: int errorLimit() VL_REQUIRES(m_mutex) { return m_errorLimit; } void warnFatal(bool flag) VL_REQUIRES(m_mutex) { m_warnFatal = flag; } bool warnFatal() VL_REQUIRES(m_mutex) { return m_warnFatal; } - void v3errorPrep(V3ErrorCode code) VL_REQUIRES(m_mutex) { - m_errorStr.str(""); - m_errorCode = code; - m_errorContexted = false; - m_errorSuppressed = false; - } - std::ostringstream& v3errorStr() VL_REQUIRES(m_mutex) { return m_errorStr; } V3ErrorCode errorCode() VL_REQUIRES(m_mutex) { return m_errorCode; } bool errorContexted() VL_REQUIRES(m_mutex) { return m_errorContexted; } int warnCount() VL_REQUIRES(m_mutex) { return m_warnCount; } @@ -385,7 +392,6 @@ public: void describedWarnings(bool flag) VL_REQUIRES(m_mutex) { m_describedWarnings = flag; } int tellManual() VL_REQUIRES(m_mutex) { return m_tellManual; } void tellManual(int level) VL_REQUIRES(m_mutex) { m_tellManual = level; } - void v3errorEnd(std::ostringstream& sstr, const string& extra = "") VL_REQUIRES(m_mutex); void suppressThisWarning() VL_REQUIRES(m_mutex); string warnContextNone() VL_REQUIRES(m_mutex) { errorContexted(true); @@ -512,66 +518,32 @@ public: // Internals for v3error()/v3fatal() macros only // Error end takes the string stream to output, be careful to seek() as needed - static void v3errorPrep(V3ErrorCode code) VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const V3RecursiveLockGuard guard{s().m_mutex}; - s().v3errorPrep(code); - } - static std::ostringstream& v3errorStr() VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const V3RecursiveLockGuard guard{s().m_mutex}; - return s().v3errorStr(); - } - static void vlAbort(); + static void v3errorAcquireLock() VL_ACQUIRE(s().m_mutex); + static std::ostringstream& v3errorPrep(V3ErrorCode code) VL_ACQUIRE(s().m_mutex); + static std::ostringstream& v3errorPrepFileLine(V3ErrorCode code, const char* file, int line) + VL_ACQUIRE(s().m_mutex); + static std::ostringstream& v3errorStr() VL_REQUIRES(s().m_mutex); // static, but often overridden in classes. static void v3errorEnd(std::ostringstream& sstr, const string& extra = "") - VL_MT_SAFE_EXCLUDES(s().m_mutex) VL_MT_SAFE { - const V3RecursiveLockGuard guard{s().m_mutex}; - s().v3errorEnd(sstr, extra); - } - // We can't call 's().v3errorEnd' directly in 'v3ErrorEnd'/'v3errorEndFatal', - // due to bug in GCC (tested on 11.3.0 version with --enable-m32) - // causing internal error when backtrace is printed. - // Instead use this wrapper. - static void v3errorEndGuardedCall(std::ostringstream& sstr, const string& extra = "") - VL_REQUIRES(s().m_mutex) VL_MT_SAFE { - s().v3errorEnd(sstr, extra); - } + VL_RELEASE(s().m_mutex); + static void vlAbort(); }; -// Global versions, so that if the class doesn't define a operator, we get the functions anyways. -inline void v3errorEnd(std::ostringstream& sstr) VL_REQUIRES(V3Error::s().m_mutex) VL_MT_SAFE { - V3Error::v3errorEndGuardedCall(sstr); -} -inline void v3errorEndFatal(std::ostringstream& sstr) - VL_REQUIRES(V3Error::s().m_mutex) VL_MT_SAFE { - V3Error::v3errorEndGuardedCall(sstr); - assert(0); // LCOV_EXCL_LINE - VL_UNREACHABLE; -} - -#ifndef V3ERROR_NO_GLOBAL_ -#define V3ErrorLockAndCheckStopRequested \ - V3Error::s().m_mutex.lockCheckStopRequest( \ - []() -> void { V3ThreadPool::s().waitIfStopRequested(); }) -#else -#define V3ErrorLockAndCheckStopRequested V3Error::s().m_mutex.lock() -#endif +// Global versions, so that if the class doesn't define an operator, we get the functions anyway. +void v3errorEnd(std::ostringstream& sstr) VL_RELEASE(V3Error::s().m_mutex); +void v3errorEndFatal(std::ostringstream& sstr) VL_RELEASE(V3Error::s().m_mutex); // Theses allow errors using << operators: v3error("foo"<<"bar"); -// Careful, you can't put () around msg, as you would in most macro definitions -// Note the commas are the comma operator, not separating arguments. These are needed to ensure -// evaluation order as otherwise we couldn't ensure v3errorPrep is called first. -// Note: due to limitations of clang thread-safety analysis, we can't use -// lock guard here, instead we are locking the mutex as first operation in temporary, -// but we are unlocking the mutex after function using comma operator. -// This way macros should also work when they are in 'if' stmt without '{}'. -#define v3warnCode(code, msg) \ - v3errorEnd((V3ErrorLockAndCheckStopRequested, V3Error::s().v3errorPrep(code), \ - (V3Error::s().v3errorStr() << msg), V3Error::s().v3errorStr())), \ - V3Error::s().m_mutex.unlock() +// Careful, you can't put () around msg, as you would in most macro definitions. +// 'V3Error::v3errorPrep(code) << msg' could be more efficient but the order of function calls in a +// single statement can be arbitrary until C++17, thus make it possible to execute +// V3Error::v3errorPrep that acquires the lock after functions in the msg may require it. So we use +// the comma operator (,) to guarantee the execution order here. +#define v3errorBuildMessage(prep, msg) \ + (prep, static_cast(V3Error::v3errorStr() << msg)) +#define v3warnCode(code, msg) v3errorEnd(v3errorBuildMessage(V3Error::v3errorPrep(code), msg)) #define v3warnCodeFatal(code, msg) \ - v3errorEndFatal((V3ErrorLockAndCheckStopRequested, V3Error::s().v3errorPrep(code), \ - (V3Error::s().v3errorStr() << msg), V3Error::s().v3errorStr())), \ - V3Error::s().m_mutex.unlock() + v3errorEndFatal(v3errorBuildMessage(V3Error::v3errorPrep(code), msg)) #define v3warn(code, msg) v3warnCode(V3ErrorCode::code, msg) #define v3info(msg) v3warnCode(V3ErrorCode::EC_INFO, msg) #define v3error(msg) v3warnCode(V3ErrorCode::EC_ERROR, msg) @@ -580,14 +552,11 @@ inline void v3errorEndFatal(std::ostringstream& sstr) #define v3fatalExit(msg) v3warnCodeFatal(V3ErrorCode::EC_FATALEXIT, msg) // Use this instead of fatal() to mention the source code line. #define v3fatalSrc(msg) \ - v3warnCodeFatal(V3ErrorCode::EC_FATALSRC, \ - __FILE__ << ":" << std::dec << __LINE__ << ": " << msg) + v3errorEndFatal(v3errorBuildMessage( \ + V3Error::v3errorPrepFileLine(V3ErrorCode::EC_FATALSRC, __FILE__, __LINE__), msg)) // Use this when normal v3fatal is called in static method that overrides fileline. #define v3fatalStatic(msg) \ - (::v3errorEndFatal((V3ErrorLockAndCheckStopRequested, \ - V3Error::s().v3errorPrep(V3ErrorCode::EC_FATAL), \ - (V3Error::s().v3errorStr() << msg), V3Error::s().v3errorStr()))), \ - V3Error::s().m_mutex.unlock() + ::v3errorEndFatal(v3errorBuildMessage(V3Error::v3errorPrep(V3ErrorCode::EC_FATAL), msg)) #define UINFO(level, stmsg) \ do { \ diff --git a/src/V3FileLine.cpp b/src/V3FileLine.cpp index be4a7cd25..f3a0fb6de 100644 --- a/src/V3FileLine.cpp +++ b/src/V3FileLine.cpp @@ -379,7 +379,7 @@ bool FileLine::warnIsOff(V3ErrorCode code) const { // cppverilator-suppress constParameter void FileLine::v3errorEnd(std::ostringstream& sstr, const string& extra) - VL_REQUIRES(V3Error::s().m_mutex) { + VL_RELEASE(V3Error::s().m_mutex) { std::ostringstream nsstr; if (lastLineno()) nsstr << this; nsstr << sstr.str(); @@ -396,7 +396,7 @@ void FileLine::v3errorEnd(std::ostringstream& sstr, const string& extra) nsstr << warnContextPrimary(); } if (!m_waive) V3Waiver::addEntry(V3Error::s().errorCode(), filename(), sstr.str()); - V3Error::s().v3errorEnd(nsstr, lstr.str()); + V3Error::v3errorEnd(nsstr, lstr.str()); } string FileLine::warnMore() const VL_REQUIRES(V3Error::s().m_mutex) { diff --git a/src/V3FileLine.h b/src/V3FileLine.h index cb29f24f9..514299173 100644 --- a/src/V3FileLine.h +++ b/src/V3FileLine.h @@ -318,9 +318,9 @@ public: // OPERATORS void v3errorEnd(std::ostringstream& str, const string& extra = "") - VL_REQUIRES(V3Error::s().m_mutex); + VL_RELEASE(V3Error::s().m_mutex); void v3errorEndFatal(std::ostringstream& str) VL_ATTR_NORETURN - VL_REQUIRES(V3Error::s().m_mutex) { + VL_RELEASE(V3Error::s().m_mutex) { v3errorEnd(str); assert(0); // LCOV_EXCL_LINE VL_UNREACHABLE; diff --git a/src/V3Graph.cpp b/src/V3Graph.cpp index 27b37adf2..b2c3c4756 100644 --- a/src/V3Graph.cpp +++ b/src/V3Graph.cpp @@ -129,7 +129,7 @@ V3GraphEdge* V3GraphVertex::findConnectingEdgep(GraphWay way, const V3GraphVerte } // cppcheck-has-bug-suppress constParameter -void V3GraphVertex::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) { +void V3GraphVertex::v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) { std::ostringstream nsstr; nsstr << str.str(); if (debug()) { @@ -139,11 +139,11 @@ void V3GraphVertex::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Erro if (FileLine* const flp = fileline()) { flp->v3errorEnd(nsstr); } else { - V3Error::s().v3errorEnd(nsstr); + V3Error::v3errorEnd(nsstr); } } void V3GraphVertex::v3errorEndFatal(std::ostringstream& str) const - VL_REQUIRES(V3Error::s().m_mutex) { + VL_RELEASE(V3Error::s().m_mutex) { v3errorEnd(str); assert(0); // LCOV_EXCL_LINE VL_UNREACHABLE; diff --git a/src/V3Graph.h b/src/V3Graph.h index 27aa13908..a0482b4e3 100644 --- a/src/V3Graph.h +++ b/src/V3Graph.h @@ -248,8 +248,8 @@ public: V3GraphEdge* beginp(GraphWay way) const { return way.forward() ? outBeginp() : inBeginp(); } // METHODS /// Error reporting - void v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex); - void v3errorEndFatal(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex); + void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex); + void v3errorEndFatal(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex); /// Edges are routed around this vertex to point from "from" directly to "to" void rerouteEdges(V3Graph* graphp); /// Find the edge connecting ap and bp, where bp is wayward from ap. diff --git a/src/V3Number.cpp b/src/V3Number.cpp index cc1d22514..c44c6e17c 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -76,7 +76,7 @@ constexpr int MAX_SPRINTF_DOUBLE_SIZE //====================================================================== // Errors -void V3Number::v3errorEnd(const std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) { +void V3Number::v3errorEnd(const std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) { std::ostringstream nsstr; nsstr << str.str(); if (m_nodep) { @@ -84,12 +84,12 @@ void V3Number::v3errorEnd(const std::ostringstream& str) const VL_REQUIRES(V3Err } else if (m_fileline) { m_fileline->v3errorEnd(nsstr); } else { - V3Error::s().v3errorEnd(nsstr); + V3Error::v3errorEnd(nsstr); } } void V3Number::v3errorEndFatal(const std::ostringstream& str) const - VL_REQUIRES(V3Error::s().m_mutex) { + VL_RELEASE(V3Error::s().m_mutex) { v3errorEnd(str); assert(0); // LCOV_EXCL_LINE VL_UNREACHABLE; diff --git a/src/V3Number.h b/src/V3Number.h index 46ba16094..e76c854c4 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -566,9 +566,9 @@ private: } public: - void v3errorEnd(const std::ostringstream& sstr) const VL_REQUIRES(V3Error::s().m_mutex); + void v3errorEnd(const std::ostringstream& sstr) const VL_RELEASE(V3Error::s().m_mutex); void v3errorEndFatal(const std::ostringstream& sstr) const VL_ATTR_NORETURN - VL_REQUIRES(V3Error::s().m_mutex); + VL_RELEASE(V3Error::s().m_mutex); void width(int width, bool sized = true) { m_data.m_sized = sized; m_data.resize(width); diff --git a/src/V3PreProc.cpp b/src/V3PreProc.cpp index f59b5d1d3..28058644f 100644 --- a/src/V3PreProc.cpp +++ b/src/V3PreProc.cpp @@ -185,7 +185,7 @@ public: // For getline() string m_lineChars; ///< Characters left for next line - void v3errorEnd(std::ostringstream& str) VL_REQUIRES(V3Error::s().m_mutex) { + void v3errorEnd(std::ostringstream& str) VL_RELEASE(V3Error::s().m_mutex) { fileline()->v3errorEnd(str); } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index ab3f2267a..d5333b6f7 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -121,12 +121,11 @@ std::ostream& operator<<(std::ostream& str, const Castable& rhs) { } #define v3widthWarn(lhs, rhs, msg) \ - v3errorEnd((V3Error::s().m_mutex.lock(), \ - V3Error::s().v3errorPrep((lhs) < (rhs) ? V3ErrorCode::WIDTHTRUNC \ - : (lhs) > (rhs) ? V3ErrorCode::WIDTHEXPAND \ - : V3ErrorCode::WIDTH), \ - (V3Error::s().v3errorStr() << msg), V3Error::s().v3errorStr())), \ - V3Error::s().m_mutex.unlock() + v3errorEnd( \ + v3errorBuildMessage(V3Error::v3errorPrep((lhs) < (rhs) ? V3ErrorCode::WIDTHTRUNC \ + : (lhs) > (rhs) ? V3ErrorCode::WIDTHEXPAND \ + : V3ErrorCode::WIDTH), \ + msg)) //###################################################################### // Width state, as a visitor of each AstNode