From a5ee8b39efb6b96e2160a5fc157755967329ec0f Mon Sep 17 00:00:00 2001 From: Mariusz Glebocki Date: Mon, 9 Oct 2023 23:43:27 +0200 Subject: [PATCH] Internals: Remove use of V3Width code from V3AstNodes (#4537). No functional change intended. `getCommonClassTypep` and its helper code has been moved to AstNode class. This is a lot better place for this functionality. Moreover, it allowed to get rid of the dependency on V3Width from generic AST-related code. --- src/V3Ast.cpp | 96 ++++++++++++++++++++++++++++ src/V3Ast.h | 43 +++++++++++++ src/V3AstNodes.cpp | 3 +- src/V3Width.cpp | 154 +++++++-------------------------------------- src/V3Width.h | 2 - 5 files changed, 163 insertions(+), 135 deletions(-) diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 3e26ad7cd..6115dabf8 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1461,6 +1461,102 @@ AstNodeDType* AstNode::findStreamDType() const { return v3Global.rootp()->typeTablep()->findStreamDType(fileline()); } +static const AstNodeDType* computeCastableBase(const AstNodeDType* nodep) { + while (true) { + if (const AstPackArrayDType* const packp = VN_CAST(nodep, PackArrayDType)) { + nodep = packp->subDTypep(); + continue; + } else if (const AstNodeDType* const refp = nodep->skipRefToEnump()) { + if (refp != nodep) { + nodep = refp; + continue; + } + } + return nodep; + } +} + +static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDType* fromDtp, + const AstNode* fromConstp) { + const VCastable castable = VCastable::UNSUPPORTED; + toDtp = toDtp->skipRefToEnump(); + fromDtp = fromDtp->skipRefToEnump(); + if (toDtp == fromDtp) return VCastable::SAMEISH; + if (toDtp->similarDType(fromDtp)) return VCastable::SAMEISH; + // UNSUP unpacked struct/unions (treated like BasicDType) + const AstNodeDType* fromBaseDtp = computeCastableBase(fromDtp); + + const bool fromNumericable = VN_IS(fromBaseDtp, BasicDType) || VN_IS(fromBaseDtp, EnumDType) + || VN_IS(fromBaseDtp, StreamDType) + || VN_IS(fromBaseDtp, NodeUOrStructDType); + + const AstNodeDType* toBaseDtp = computeCastableBase(toDtp); + const bool toNumericable + = VN_IS(toBaseDtp, BasicDType) || VN_IS(toBaseDtp, NodeUOrStructDType); + + if (toBaseDtp == fromBaseDtp) { + return VCastable::COMPATIBLE; + } else if (toNumericable) { + if (fromNumericable) return VCastable::COMPATIBLE; + } else if (VN_IS(toDtp, EnumDType)) { + if (VN_IS(fromBaseDtp, EnumDType) && toDtp->sameTree(fromDtp)) + return VCastable::ENUM_IMPLICIT; + if (fromNumericable) return VCastable::ENUM_EXPLICIT; + } else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromConstp, Const)) { + if (VN_IS(fromConstp, Const) && VN_AS(fromConstp, Const)->num().isNull()) + return VCastable::COMPATIBLE; + } else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromDtp, ClassRefDType)) { + const auto toClassp = VN_AS(toDtp, ClassRefDType)->classp(); + const auto fromClassp = VN_AS(fromDtp, ClassRefDType)->classp(); + const bool downcast = AstClass::isClassExtendedFrom(toClassp, fromClassp); + const bool upcast = AstClass::isClassExtendedFrom(fromClassp, toClassp); + if (upcast) { + return VCastable::COMPATIBLE; + } else if (downcast) { + return VCastable::DYNAMIC_CLASS; + } else { + return VCastable::INCOMPATIBLE; + } + } + return castable; +} + +VCastable AstNode::computeCastable(const AstNodeDType* toDtp, const AstNodeDType* fromDtp, + const AstNode* fromConstp) { + const auto castable = computeCastableImp(toDtp, fromDtp, fromConstp); + UINFO(9, " castable=" << castable << " for " << toDtp << endl); + UINFO(9, " =?= " << fromDtp << endl); + UINFO(9, " const= " << fromConstp << endl); + return castable; +} + +AstNodeDType* AstNode::getCommonClassTypep(AstNode* nodep1, AstNode* nodep2) { + // Return the class type that both nodep1 and nodep2 are castable to. + // If both are null, return the type of null constant. + // If one is a class and one is null, return AstClassRefDType that points to that class. + // If no common class type exists, return nullptr. + + // First handle cases with null values and when one class is a super class of the other. + if (VN_IS(nodep1, Const)) std::swap(nodep1, nodep2); + { + const VCastable castable = computeCastable(nodep1->dtypep(), nodep2->dtypep(), nodep2); + if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE) { + return nodep1->dtypep(); + } else if (castable == VCastable::DYNAMIC_CLASS) { + return nodep2->dtypep(); + } + } + + AstClassRefDType* classDtypep1 = VN_CAST(nodep1->dtypep(), ClassRefDType); + while (classDtypep1) { + const VCastable castable = computeCastable(classDtypep1, nodep2->dtypep(), nodep2); + if (castable == VCastable::COMPATIBLE) return classDtypep1; + AstClassExtends* const extendsp = classDtypep1->classp()->extendsp(); + classDtypep1 = extendsp ? VN_AS(extendsp->dtypep(), ClassRefDType) : nullptr; + } + return nullptr; +} + //###################################################################### // VNDeleter diff --git a/src/V3Ast.h b/src/V3Ast.h index a6daaa584..38dba7714 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1249,6 +1249,45 @@ inline std::ostream& operator<<(std::ostream& os, const VUseType& rhs) { // ###################################################################### +class VCastable final { +public: + enum en : uint8_t { + UNSUPPORTED, + SAMEISH, + COMPATIBLE, + ENUM_EXPLICIT, + ENUM_IMPLICIT, + DYNAMIC_CLASS, + INCOMPATIBLE, + _ENUM_MAX // Leave last + }; + enum en m_e; + const char* ascii() const { + static const char* const names[] + = {"UNSUPPORTED", "SAMEISH", "COMPATIBLE", "ENUM_EXPLICIT", + "ENUM_IMPLICIT", "DYNAMIC_CLASS", "INCOMPATIBLE"}; + return names[m_e]; + } + VCastable() + : m_e{UNSUPPORTED} {} + // cppcheck-suppress noExplicitConstructor + constexpr VCastable(en _e) + : m_e{_e} {} + explicit VCastable(int _e) + : m_e(static_cast(_e)) {} // Need () or GCC 4.8 false warning + constexpr operator en() const { return m_e; } +}; +constexpr bool operator==(const VCastable& lhs, const VCastable& rhs) { + return lhs.m_e == rhs.m_e; +} +constexpr bool operator==(const VCastable& lhs, VCastable::en rhs) { return lhs.m_e == rhs; } +constexpr bool operator==(VCastable::en lhs, const VCastable& rhs) { return lhs == rhs.m_e; } +inline std::ostream& operator<<(std::ostream& os, const VCastable& rhs) { + return os << rhs.ascii(); +} + +// ###################################################################### + class VBasicTypeKey final { public: const int m_width; // From AstNodeDType: Bit width of operation @@ -1989,6 +2028,10 @@ public: AstNodeDType* findBasicDType(VBasicDTypeKwd kwd) const; static AstBasicDType* findInsertSameDType(AstBasicDType* nodep); + static VCastable computeCastable(const AstNodeDType* toDtp, const AstNodeDType* fromDtp, + const AstNode* fromConstp); + static AstNodeDType* getCommonClassTypep(AstNode* nodep1, AstNode* nodep2); + // METHODS - dump and error void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex); void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 7dd6ffee7..507bcfbbc 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -25,7 +25,6 @@ #include "V3Hasher.h" #include "V3PartitionGraph.h" // Just for mtask dumping #include "V3String.h" -#include "V3Width.h" #include "V3Ast__gen_macros.h" // Generated by 'astgen' @@ -217,7 +216,7 @@ AstNodeCond::AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr UASSERT_OBJ(elsep, this, "No elsep expression"); if (thenp->isClassHandleValue() && elsep->isClassHandleValue()) { // Get the most-deriving class type that both arguments can be casted to. - AstNodeDType* const commonClassTypep = V3Width::getCommonClassTypep(thenp, elsep); + AstNodeDType* const commonClassTypep = getCommonClassTypep(thenp, elsep); UASSERT_OBJ(commonClassTypep, this, "No common base class exists"); dtypep(commonClassTypep); } else { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index b87fe81e4..cecd6ad1b 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -108,21 +108,6 @@ std::ostream& operator<<(std::ostream& str, const Determ& rhs) { return str << s_det[rhs]; } -enum Castable : uint8_t { - UNSUPPORTED, - SAMEISH, - COMPATIBLE, - ENUM_EXPLICIT, - ENUM_IMPLICIT, - DYNAMIC_CLASS, - INCOMPATIBLE -}; -std::ostream& operator<<(std::ostream& str, const Castable& rhs) { - static const char* const s_det[] - = {"UNSUP", "IDENT", "COMPAT", "ENUM_EXP", "ENUM_IMP", "DYN_CLS", "INCOMPAT"}; - return str << s_det[rhs]; -} - #define v3widthWarn(lhs, rhs, msg) \ v3errorEnd( \ v3errorBuildMessage(V3Error::v3errorPrep((lhs) < (rhs) ? V3ErrorCode::WIDTHTRUNC \ @@ -518,7 +503,7 @@ private: if (nodep->thenp()->isClassHandleValue() && nodep->elsep()->isClassHandleValue()) { // Get the most-deriving class type that both arguments can be casted to. commonClassTypep - = V3Width::getCommonClassTypep(nodep->thenp(), nodep->elsep()); + = AstNode::getCommonClassTypep(nodep->thenp(), nodep->elsep()); } if (commonClassTypep) { nodep->dtypep(commonClassTypep); @@ -1842,12 +1827,12 @@ private: AstNodeDType* const toDtp = nodep->top()->dtypep()->skipRefToEnump(); AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump(); FileLine* const fl = nodep->fileline(); - const auto castable = computeCastable(toDtp, fromDtp, nodep->fromp()); + const auto castable = AstNode::computeCastable(toDtp, fromDtp, nodep->fromp()); AstNode* newp; - if (castable == DYNAMIC_CLASS) { + if (castable == VCastable::DYNAMIC_CLASS) { // Keep in place, will compute at runtime return; - } else if (castable == ENUM_EXPLICIT || castable == ENUM_IMPLICIT) { + } else if (castable == VCastable::ENUM_EXPLICIT || castable == VCastable::ENUM_IMPLICIT) { // TODO is from is a constant we could simplify, though normal constant // elimination should do much the same // Form: "( ((v > size) ? false : enum_valid[v[N:0]]) @@ -1885,7 +1870,7 @@ private: nodep->fromp()->unlinkFrBack()}, new AstConst{fl, AstConst::Signed32{}, 1}}, new AstConst{fl, AstConst::Signed32{}, 0}}; - } else if (castable == SAMEISH || castable == COMPATIBLE) { + } else if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE) { nodep->v3warn(CASTCONST, "$cast will always return one as " << toDtp->prettyDTypeNameQ() << " is always castable from " @@ -1896,7 +1881,7 @@ private: new AstAssign{fl, nodep->top()->unlinkFrBack(), new AstCast{fl, nodep->fromp()->unlinkFrBack(), toDtp}}, new AstConst{fl, AstConst::Signed32{}, 1}}; - } else if (castable == INCOMPATIBLE) { + } else if (castable == VCastable::INCOMPATIBLE) { newp = new AstConst{fl, 0}; nodep->v3warn(CASTCONST, "$cast will always return zero as " << toDtp->prettyDTypeNameQ() << " is not castable from " @@ -1941,23 +1926,24 @@ private: userIterateAndNext(nodep->fromp(), WidthVP{SELF, PRELIM}.p()); AstNodeDType* const toDtp = nodep->dtypep()->skipRefToEnump(); AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump(); - const auto castable = computeCastable(toDtp, fromDtp, nodep->fromp()); + const auto castable = AstNode::computeCastable(toDtp, fromDtp, nodep->fromp()); bool bad = false; - if (castable == UNSUPPORTED) { + if (castable == VCastable::UNSUPPORTED) { nodep->v3warn(E_UNSUPPORTED, "Unsupported: static cast to " << toDtp->prettyDTypeNameQ() << " from " << fromDtp->prettyDTypeNameQ()); bad = true; - } else if (castable == SAMEISH || castable == COMPATIBLE || castable == ENUM_IMPLICIT - || castable == ENUM_EXPLICIT) { + } else if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE + || castable == VCastable::ENUM_IMPLICIT + || castable == VCastable::ENUM_EXPLICIT) { ; // Continue - } else if (castable == DYNAMIC_CLASS) { + } else if (castable == VCastable::DYNAMIC_CLASS) { nodep->v3error("Dynamic, not static cast, required to cast " << toDtp->prettyDTypeNameQ() << " from " << fromDtp->prettyDTypeNameQ() << '\n' << nodep->warnMore() << "... Suggest dynamic $cast"); bad = true; - } else if (castable == INCOMPATIBLE) { + } else if (castable == VCastable::INCOMPATIBLE) { nodep->v3error("Incompatible types to static cast to " << toDtp->prettyDTypeNameQ() << " from " << fromDtp->prettyDTypeNameQ() << '\n'); @@ -4407,7 +4393,8 @@ private: "Case(type) statement requires items that have type() items"); } else { AstNodeDType* const condDtp = VN_AS(condAttrp->fromp(), NodeDType); - if (computeCastable(exprDtp, condDtp, nodep) == SAMEISH) { + if (AstNode::computeCastable(exprDtp, condDtp, nodep) + == VCastable::SAMEISH) { hit = true; break; } @@ -6004,7 +5991,8 @@ private: UINFO(9, "==type lhsDtp " << lhsDtp << endl); UINFO(9, "==type rhsDtp " << lhsDtp << endl); const bool invert = VN_IS(nodep, NeqT); - const bool identical = computeCastable(lhsDtp, rhsDtp, nodep) == SAMEISH; + const bool identical + = AstNode::computeCastable(lhsDtp, rhsDtp, nodep) == VCastable::SAMEISH; UINFO(9, "== " << identical << endl); const bool eq = invert ^ identical; AstNode* const newp = new AstConst{nodep->fileline(), AstConst::BitTrue{}, eq}; @@ -6641,9 +6629,11 @@ private: if (expBasicp && underBasicp) { if (const AstEnumDType* const expEnump = VN_CAST(expDTypep->skipRefToEnump(), EnumDType)) { - const auto castable = computeCastable(expEnump, underp->dtypep(), underp); - if (castable != SAMEISH && castable != COMPATIBLE && castable != ENUM_IMPLICIT - && !VN_IS(underp, Cast) && !VN_IS(underp, CastDynamic) && !m_enumItemp + const auto castable + = AstNode::computeCastable(expEnump, underp->dtypep(), underp); + if (castable != VCastable::SAMEISH && castable != VCastable::COMPATIBLE + && castable != VCastable::ENUM_IMPLICIT && !VN_IS(underp, Cast) + && !VN_IS(underp, CastDynamic) && !m_enumItemp && !nodep->fileline()->warnIsOff(V3ErrorCode::ENUMVALUE) && warnOn) { underp->v3warn(ENUMVALUE, "Implicit conversion to enum " @@ -7363,68 +7353,9 @@ private: return false; } - //---------------------------------------------------------------------- - // METHODS - casting - static Castable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDType* fromDtp, - const AstNode* fromConstp) { - const Castable castable = UNSUPPORTED; - toDtp = toDtp->skipRefToEnump(); - fromDtp = fromDtp->skipRefToEnump(); - if (toDtp == fromDtp) return SAMEISH; - if (toDtp->similarDType(fromDtp)) return SAMEISH; - // UNSUP unpacked struct/unions (treated like BasicDType) - const AstNodeDType* fromBaseDtp = computeCastableBase(fromDtp); - - const bool fromNumericable - = VN_IS(fromBaseDtp, BasicDType) || VN_IS(fromBaseDtp, EnumDType) - || VN_IS(fromBaseDtp, StreamDType) || VN_IS(fromBaseDtp, NodeUOrStructDType); - - const AstNodeDType* toBaseDtp = computeCastableBase(toDtp); - const bool toNumericable - = VN_IS(toBaseDtp, BasicDType) || VN_IS(toBaseDtp, NodeUOrStructDType); - - if (toBaseDtp == fromBaseDtp) { - return COMPATIBLE; - } else if (toNumericable) { - if (fromNumericable) return COMPATIBLE; - } else if (VN_IS(toDtp, EnumDType)) { - if (VN_IS(fromBaseDtp, EnumDType) && toDtp->sameTree(fromDtp)) return ENUM_IMPLICIT; - if (fromNumericable) return ENUM_EXPLICIT; - } else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromConstp, Const)) { - if (VN_IS(fromConstp, Const) && VN_AS(fromConstp, Const)->num().isNull()) - return COMPATIBLE; - } else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromDtp, ClassRefDType)) { - const auto toClassp = VN_AS(toDtp, ClassRefDType)->classp(); - const auto fromClassp = VN_AS(fromDtp, ClassRefDType)->classp(); - const bool downcast = AstClass::isClassExtendedFrom(toClassp, fromClassp); - const bool upcast = AstClass::isClassExtendedFrom(fromClassp, toClassp); - if (upcast) { - return COMPATIBLE; - } else if (downcast) { - return DYNAMIC_CLASS; - } else { - return INCOMPATIBLE; - } - } - return castable; - } - static const AstNodeDType* computeCastableBase(const AstNodeDType* nodep) { - while (true) { - if (const AstPackArrayDType* const packp = VN_CAST(nodep, PackArrayDType)) { - nodep = packp->subDTypep(); - continue; - } else if (const AstNodeDType* const refp = nodep->skipRefToEnump()) { - if (refp != nodep) { - nodep = refp; - continue; - } - } - return nodep; - } - } - //---------------------------------------------------------------------- // METHODS - special type detection + void assertAtStatement(AstNode* nodep) { if (VL_UNCOVERABLE(m_vup && !m_vup->selfDtm())) { UINFO(1, "-: " << m_vup << endl); @@ -7529,15 +7460,6 @@ public: return userIterateSubtreeReturnEdits(nodep, WidthVP{SELF, BOTH}.p()); } ~WidthVisitor() override = default; - - static Castable computeCastable(const AstNodeDType* toDtp, const AstNodeDType* fromDtp, - const AstNode* fromConstp) { - const auto castable = computeCastableImp(toDtp, fromDtp, fromConstp); - UINFO(9, " castable=" << castable << " for " << toDtp << endl); - UINFO(9, " =?= " << fromDtp << endl); - UINFO(9, " const= " << fromConstp << endl); - return castable; - } }; //###################################################################### @@ -7555,36 +7477,6 @@ void V3Width::width(AstNetlist* nodep) { } // Destruct before checking V3Global::dumpCheckGlobalTree("width", 0, dumpTreeLevel() >= 3); } - -AstNodeDType* V3Width::getCommonClassTypep(AstNode* nodep1, AstNode* nodep2) { - // Return the class type that both nodep1 and nodep2 are castable to. - // If both are null, return the type of null constant. - // If one is a class and one is null, return AstClassRefDType that points to that class. - // If no common class type exists, return nullptr. - - // First handle cases with null values and when one class is a super class of the other. - if (VN_IS(nodep1, Const)) std::swap(nodep1, nodep2); - { - const Castable castable - = WidthVisitor::computeCastable(nodep1->dtypep(), nodep2->dtypep(), nodep2); - if (castable == SAMEISH || castable == COMPATIBLE) { - return nodep1->dtypep(); - } else if (castable == DYNAMIC_CLASS) { - return nodep2->dtypep(); - } - } - - AstClassRefDType* classDtypep1 = VN_CAST(nodep1->dtypep(), ClassRefDType); - while (classDtypep1) { - const Castable castable - = WidthVisitor::computeCastable(classDtypep1, nodep2->dtypep(), nodep2); - if (castable == COMPATIBLE) return classDtypep1; - AstClassExtends* const extendsp = classDtypep1->classp()->extendsp(); - classDtypep1 = extendsp ? VN_AS(extendsp->dtypep(), ClassRefDType) : nullptr; - } - return nullptr; -} - //! Single node parameter propagation //! Smaller step... Only do a single node for parameter propagation AstNode* V3Width::widthParamsEdit(AstNode* nodep) { diff --git a/src/V3Width.h b/src/V3Width.h index fd9f17af5..0ac3a6b25 100644 --- a/src/V3Width.h +++ b/src/V3Width.h @@ -34,8 +34,6 @@ public: static AstNode* widthParamsEdit(AstNode* nodep) VL_MT_DISABLED; static AstNode* widthGenerateParamsEdit(AstNode* nodep) VL_MT_DISABLED; - static AstNodeDType* getCommonClassTypep(AstNode* nodep1, AstNode* nodep2) VL_MT_DISABLED; - // For use only in WidthVisitor // Replace AstSelBit, etc with AstSel/AstArraySel // Returns replacement node if nodep was deleted, or null if none.