Internals: Rework self pointers (#4396)
This commit is contained in:
parent
9882ab6c67
commit
fb1fc46b06
|
|
@ -63,6 +63,18 @@ const VNTypeInfo VNType::typeInfoTable[] = {
|
|||
|
||||
std::ostream& operator<<(std::ostream& os, VNType rhs);
|
||||
|
||||
//######################################################################
|
||||
// VSelfPointerText
|
||||
|
||||
const std::shared_ptr<const string> VSelfPointerText::s_emptyp = std::make_shared<string>("");
|
||||
const std::shared_ptr<const string> VSelfPointerText::s_thisp = std::make_shared<string>("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
|
||||
|
||||
|
|
|
|||
38
src/V3Ast.h
38
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<const string> s_emptyp; // Holds ""
|
||||
static const std::shared_ptr<const string> s_thisp; // Holds "this"
|
||||
|
||||
// MEMBERS
|
||||
std::shared_ptr<const string> 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<const string>("this->" + field)} {}
|
||||
class VlSyms {}; // for creator type-overload selection
|
||||
VSelfPointerText(VlSyms, const string& field)
|
||||
: m_strp{std::make_shared<const string>("(&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
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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} {
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@ private:
|
|||
|
||||
// TYPES
|
||||
using FuncMmap = std::multimap<std::string, AstCFunc*>;
|
||||
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<const AstScope*, ScopeSelfPtr> 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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ private:
|
|||
iterateConstNull(nodep->varScopep());
|
||||
} else {
|
||||
iterateConstNull(nodep->varp());
|
||||
m_hash += nodep->selfPointer();
|
||||
m_hash += nodep->selfPointer().asString();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue