diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index e57d28ee5..67dba756d 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -63,6 +63,18 @@ const VNTypeInfo VNType::typeInfoTable[] = { std::ostream& operator<<(std::ostream& os, VNType rhs); +//###################################################################### +// VSelfPointerText + +const std::shared_ptr VSelfPointerText::s_emptyp = std::make_shared(""); +const std::shared_ptr VSelfPointerText::s_thisp = std::make_shared("this"); + +string VSelfPointerText::protect(bool useSelfForThis, bool protect) const { + const string& sp + = useSelfForThis ? VString::replaceWord(asString(), "this", "vlSelf") : asString(); + return VIdProtect::protectWordsIf(sp, protect); +} + //###################################################################### // AstNode diff --git a/src/V3Ast.h b/src/V3Ast.h index dbe2b02a0..dc2a6b523 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1247,6 +1247,44 @@ public: ~VBasicTypeKey() = default; }; +// ###################################################################### +// VSelfPointerText - Represents text to be emitted before a given var reference, call, etc. to +// serve as a pointer to a 'self' object. For example, it could be empty (no self pointer), or the +// string 'this', or 'vlSymsp->...' + +class VSelfPointerText final { +private: + // STATIC MEMBERS + // Keep these in shared pointers to avoid branching for special cases + static const std::shared_ptr s_emptyp; // Holds "" + static const std::shared_ptr s_thisp; // Holds "this" + + // MEMBERS + std::shared_ptr m_strp; + +public: + // CONSTRUCTORS + class Empty {}; // for creator type-overload selection + VSelfPointerText(Empty) + : m_strp{s_emptyp} {} + class This {}; // for creator type-overload selection + VSelfPointerText(This) + : m_strp{s_thisp} {} + VSelfPointerText(This, const string& field) + : m_strp{std::make_shared("this->" + field)} {} + class VlSyms {}; // for creator type-overload selection + VSelfPointerText(VlSyms, const string& field) + : m_strp{std::make_shared("(&vlSymsp->" + field + ')')} {} + + // METHODS + bool isEmpty() const { return m_strp == s_emptyp; } + bool isVlSym() const { return m_strp->find("vlSymsp") != string::npos; } + bool hasThis() const { return m_strp == s_thisp || VString::startsWith(*m_strp, "this"); } + string protect(bool useSelfForThis, bool protect) const; + const std::string& asString() const { return *m_strp; } + bool operator==(const VSelfPointerText& other) const { return *m_strp == *other.m_strp; } +}; + //###################################################################### // AstNUser - Generic base class for AST User nodes. // - Also used to allow parameter passing up/down iterate calls diff --git a/src/V3AstInlines.h b/src/V3AstInlines.h index cff4fd9e5..3c40963b2 100644 --- a/src/V3AstInlines.h +++ b/src/V3AstInlines.h @@ -176,7 +176,7 @@ bool AstVarRef::sameNoLvalue(AstVarRef* samep) const { return (varScopep() == samep->varScopep()); } else { return (selfPointer() == samep->selfPointer() - && (!selfPointer().empty() || !samep->selfPointer().empty()) + && (!selfPointer().isEmpty() || !samep->selfPointer().isEmpty()) && varp()->name() == samep->varp()->name()); } } diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 75694f61d..1a421e928 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -444,8 +444,9 @@ class AstNodeVarRef VL_NOT_FINAL : public AstNodeExpr { AstVar* m_varp; // [AfterLink] Pointer to variable itself AstVarScope* m_varScopep = nullptr; // Varscope for hierarchy AstNodeModule* m_classOrPackagep = nullptr; // Class/package of the variable - string m_selfPointer; // Output code object pointer (e.g.: 'this') - + VSelfPointerText m_selfPointer + = VSelfPointerText{VSelfPointerText::Empty()}; // Output code object + // pointer (e.g.: 'this') protected: AstNodeVarRef(VNType t, FileLine* fl, const VAccess& access) : AstNodeExpr{t, fl} @@ -474,9 +475,11 @@ public: } AstVarScope* varScopep() const { return m_varScopep; } void varScopep(AstVarScope* varscp) { m_varScopep = varscp; } - string selfPointer() const { return m_selfPointer; } - void selfPointer(const string& value) { m_selfPointer = value; } - string selfPointerProtect(bool useSelfForThis) const; + const VSelfPointerText& selfPointer() const { return m_selfPointer; } + void selfPointer(const VSelfPointerText& selfPointer) { m_selfPointer = selfPointer; } + string selfPointerProtect(bool useSelfForThis) const { + return selfPointer().protect(useSelfForThis, protect()); + } AstNodeModule* classOrPackagep() const { return m_classOrPackagep; } void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; } // Know no children, and hot function, so skip iterator for speed @@ -4048,16 +4051,19 @@ public: // === AstNodeCCall === class AstCCall final : public AstNodeCCall { // C++ function call - string m_selfPointer; // Output code object pointer (e.g.: 'this') - + VSelfPointerText m_selfPointer + = VSelfPointerText{VSelfPointerText::Empty()}; // Output code object + // pointer (e.g.: 'this') public: AstCCall(FileLine* fl, AstCFunc* funcp, AstNodeExpr* argsp = nullptr) : ASTGEN_SUPER_CCall(fl, funcp, argsp) {} ASTGEN_MEMBERS_AstCCall; - string selfPointer() const { return m_selfPointer; } - void selfPointer(const string& value) { m_selfPointer = value; } - string selfPointerProtect(bool useSelfForThis) const; + const VSelfPointerText& selfPointer() const { return m_selfPointer; } + void selfPointer(const VSelfPointerText& selfPointer) { m_selfPointer = selfPointer; } + string selfPointerProtect(bool useSelfForThis) const { + return selfPointer().protect(useSelfForThis, protect()); + } }; class AstCMethodCall final : public AstNodeCCall { // C++ method call diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index db6fd3b14..e677af491 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1390,7 +1390,6 @@ public: void dump(std::ostream& str) const override; bool same(const AstNode* samep) const override; string nameDotless() const; - string nameVlSym() const { return string{"vlSymsp->"} + nameDotless(); } AstNodeModule* modp() const { return m_modp; } // AstScope* aboveScopep() const VL_MT_SAFE { return m_aboveScopep; } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 65eed3af0..5a411d606 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -84,12 +84,6 @@ void AstNodeVarRef::cloneRelink() { } } -string AstNodeVarRef::selfPointerProtect(bool useSelfForThis) const { - const string& sp - = useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer(); - return VIdProtect::protectWordsIf(sp, protect()); -} - void AstAddrOfCFunc::cloneRelink() { if (m_funcp && m_funcp->clonep()) m_funcp = m_funcp->clonep(); } @@ -129,12 +123,6 @@ const char* AstNodeCCall::broken() const { } bool AstNodeCCall::isPure() const { return funcp()->dpiPure(); } -string AstCCall::selfPointerProtect(bool useSelfForThis) const { - const string& sp - = useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer(); - return VIdProtect::protectWordsIf(sp, protect()); -} - AstNodeCond::AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep) : AstNodeTriop{t, fl, condp, thenp, elsep} { diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index 3c1265c67..0cec01ab1 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -117,7 +117,7 @@ public: callp->argTypes("vlSymsp"); } else { if (m_type.isCoverage()) callp->argTypes("first"); - callp->selfPointer("this"); + callp->selfPointer(VSelfPointerText{VSelfPointerText::This()}); } rootFuncp->addStmtsp(callp->makeStmt()); } @@ -229,7 +229,7 @@ void V3CCtors::evalAsserts() { // if (signal & CONST(upper_non_clean_mask)) { fail; } AstVarRef* const vrefp = new AstVarRef{varp->fileline(), varp, VAccess::READ}; - vrefp->selfPointer("this"); + vrefp->selfPointer(VSelfPointerText{VSelfPointerText::This()}); AstNodeExpr* newp = vrefp; if (varp->isWide()) { newp = new AstWordSel{ diff --git a/src/V3Descope.cpp b/src/V3Descope.cpp index 1d4866faf..fe5fe15f9 100644 --- a/src/V3Descope.cpp +++ b/src/V3Descope.cpp @@ -46,6 +46,10 @@ private: // TYPES using FuncMmap = std::multimap; + struct ScopeSelfPtr final { + VSelfPointerText thisPtr = VSelfPointerText{VSelfPointerText::Empty()}; + VSelfPointerText vlSymsPtr = VSelfPointerText{VSelfPointerText::Empty()}; + }; // STATE AstNodeModule* m_modp = nullptr; // Current module @@ -53,6 +57,7 @@ private: const AstCFunc* m_funcp = nullptr; // Current function bool m_modSingleton = false; // m_modp is only instantiated once FuncMmap m_modFuncs; // Name of public functions added + std::map m_scopeToSelf; // Scope to self pointers // METHODS @@ -68,12 +73,32 @@ private: return (instances == 1); } + // Construct a 'this' self pointer for the given scope + VSelfPointerText scopeThis(const AstScope* scopep) { + auto& ret = m_scopeToSelf[scopep]; + if (ret.thisPtr.isEmpty()) { + string name = scopep->name(); + string::size_type pos; + if ((pos = name.rfind('.')) != string::npos) name.erase(0, pos + 1); + ret.thisPtr = VSelfPointerText{VSelfPointerText::This(), name}; + } + return ret.thisPtr; + } + // Construct a 'vlSyms' self pointer for the given scope + VSelfPointerText scopeVlSyms(const AstScope* scopep) { + auto& ret = m_scopeToSelf[scopep]; + if (ret.vlSymsPtr.isEmpty()) { + ret.vlSymsPtr = VSelfPointerText{VSelfPointerText::VlSyms(), scopep->nameDotless()}; + } + return ret.vlSymsPtr; + } + // Construct the best self pointer to reference an object in 'scopep' from a CFunc in // 'm_scopep'. Result may be relative ("this->[...]") or absolute ("vlSyms->[...]"). // // Using relative references allows V3Combine'ing code across multiple instances of the same // module. - string descopedSelfPointer(const AstScope* scopep) { + VSelfPointerText descopedSelfPointer(const AstScope* scopep) { UASSERT(scopep, "Var/Func not scoped"); // Static functions can't use relative references via 'this->' const bool relativeRefOk = !m_funcp->isStatic(); @@ -85,23 +110,20 @@ private: if (VN_IS(scopep->modp(), Class)) { // Direct reference to class members are from within the class itself, references from // outside the class must go via AstMemberSel - return "this"; + return VSelfPointerText{VSelfPointerText::This()}; } else if (relativeRefOk && scopep == m_scopep) { - return "this"; + return VSelfPointerText{VSelfPointerText::This()}; } else if (relativeRefOk && !m_modSingleton && scopep->aboveScopep() == m_scopep && VN_IS(scopep->modp(), Module)) { // Reference to scope of instance directly under this module, can just "this->cell", // which can potentially be V3Combined, but note this requires one extra pointer // dereference which is slower, so we only use it if the source scope is not a // singleton. - string name = scopep->name(); - string::size_type pos; - if ((pos = name.rfind('.')) != string::npos) name.erase(0, pos + 1); - return "this->" + name; + return scopeThis(scopep); } else { // Reference to something elsewhere, or relative references are disabled. Use global // variable - return "(&" + scopep->nameVlSym() + ")"; + return scopeVlSyms(scopep); } } @@ -163,11 +185,10 @@ private: if (moreOfSame) { AstIf* const ifp = new AstIf{ funcp->fileline(), - new AstEq{ - funcp->fileline(), new AstCExpr{funcp->fileline(), "this", 64}, - new AstCExpr{funcp->fileline(), - string{"&("} + funcp->scopep()->nameVlSym() + ")", - 64}}, + new AstEq{funcp->fileline(), + new AstCExpr{funcp->fileline(), "this", 64}, + new AstCExpr{funcp->fileline(), + scopeVlSyms(funcp->scopep()).asString(), 64}}, returnp}; newfuncp->addStmtsp(ifp); } else { @@ -225,15 +246,15 @@ private: const AstScope* const scopep = nodep->varScopep()->scopep(); if (varp->isFuncLocal()) { // Reference to function locals need no self pointer - nodep->selfPointer(""); + nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()}); } else if (scopep->modp() == v3Global.rootp()->constPoolp()->modp()) { // Reference to constant pool value need no self pointer - nodep->selfPointer(""); + nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()}); } else { nodep->selfPointer(descopedSelfPointer(scopep)); } nodep->varScopep(nullptr); - UINFO(9, " refout " << nodep << " selfPtr=" << nodep->selfPointer() << endl); + UINFO(9, " refout " << nodep << " selfPtr=" << nodep->selfPointer().asString() << endl); } void visit(AstCCall* nodep) override { // UINFO(9, " " << nodep << endl); diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index cbb894671..8fd63dcf8 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -108,7 +108,7 @@ void EmitCFunc::emitOpName(AstNode* nodep, const string& format, AstNode* lhsp, UASSERT_OBJ(m_wideTempRefp, nodep, "Wide Op w/ no temp, perhaps missing op in V3EmitC?"); COMMA; - if (!m_wideTempRefp->selfPointer().empty()) { + if (!m_wideTempRefp->selfPointer().isEmpty()) { emitDereference(m_wideTempRefp->selfPointerProtect(m_useSelfForThis)); } puts(m_wideTempRefp->varp()->nameProtect()); @@ -516,7 +516,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string if (!assigntop) { puts(assignString); } else { - if (!assigntop->selfPointer().empty()) { + if (!assigntop->selfPointer().isEmpty()) { emitDereference(assigntop->selfPointerProtect(m_useSelfForThis)); } puts(assigntop->varp()->nameProtect()); @@ -538,7 +538,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string if (!assigntop) { puts(assignString); } else { - if (!assigntop->selfPointer().empty()) { + if (!assigntop->selfPointer().isEmpty()) { emitDereference(assigntop->selfPointerProtect(m_useSelfForThis)); } puts(assigntop->varp()->nameProtect()); diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index a1c1cd515..6481ea2c4 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -494,7 +494,7 @@ public: puts(funcNameProtect(funcp)); } else { // Calling regular method/function - if (!nodep->selfPointer().empty()) { + if (!nodep->selfPointer().isEmpty()) { emitDereference(nodep->selfPointerProtect(m_useSelfForThis)); } puts(funcp->nameProtect()); @@ -1265,7 +1265,7 @@ public: } else if (varp->isIfaceRef()) { puts(nodep->selfPointerProtect(m_useSelfForThis)); return; - } else if (!nodep->selfPointer().empty()) { + } else if (!nodep->selfPointer().isEmpty()) { emitDereference(nodep->selfPointerProtect(m_useSelfForThis)); } puts(nodep->varp()->nameProtect()); diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index 18f75acc4..f84e9442c 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -59,18 +59,18 @@ class EmitCGatherDependencies final : VNVisitorConst { } } } - void addSelfDependency(const string& selfPointer, AstNode* nodep) { - if (selfPointer.empty()) { + void addSelfDependency(VSelfPointerText selfPointer, AstNode* nodep) { + if (selfPointer.isEmpty()) { // No self pointer (e.g.: function locals, const pool values, loose static methods), // so no dependency - } else if (VString::startsWith(selfPointer, "this")) { + } else if (selfPointer.hasThis()) { // Dereferencing 'this', we need the definition of this module, which is also the // module that contains the variable. addModDependency(EmitCParentModule::get(nodep)); } else { // Must be an absolute reference - UASSERT_OBJ(selfPointer.find("vlSymsp") != string::npos, nodep, - "Unknown self pointer: '" << selfPointer << "'"); + UASSERT_OBJ(selfPointer.isVlSym(), nodep, + "Unknown self pointer: '" << selfPointer.asString() << "'"); // Dereferencing vlSymsp, so we need it's definition... addSymsDependency(); } diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 953a3d937..2f0e40351 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -682,10 +682,11 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { putfs(nodep, nodep->varScopep()->prettyName()); } else { if (nodep->varp()) { - if (nodep->selfPointer().empty()) { + if (nodep->selfPointer().isEmpty()) { putfs(nodep, nodep->varp()->prettyName()); } else { - putfs(nodep, nodep->selfPointer() + "->"); + putfs(nodep, nodep->selfPointer().asString()); + putfs(nodep, "->"); puts(nodep->varp()->prettyName()); } } else { diff --git a/src/V3Hasher.cpp b/src/V3Hasher.cpp index 432f62b17..2ecfaaa69 100644 --- a/src/V3Hasher.cpp +++ b/src/V3Hasher.cpp @@ -207,7 +207,7 @@ private: iterateConstNull(nodep->varScopep()); } else { iterateConstNull(nodep->varp()); - m_hash += nodep->selfPointer(); + m_hash += nodep->selfPointer().asString(); } }); }