Internals: Rework self pointers (#4396)

This commit is contained in:
Krzysztof Bieganski 2023-09-08 13:34:35 +02:00 committed by GitHub
parent 9882ab6c67
commit fb1fc46b06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 120 additions and 55 deletions

View File

@ -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

View File

@ -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

View File

@ -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());
}
}

View File

@ -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

View File

@ -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; }

View File

@ -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} {

View File

@ -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{

View File

@ -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);

View File

@ -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());

View File

@ -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());

View File

@ -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();
}

View File

@ -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 {

View File

@ -207,7 +207,7 @@ private:
iterateConstNull(nodep->varScopep());
} else {
iterateConstNull(nodep->varp());
m_hash += nodep->selfPointer();
m_hash += nodep->selfPointer().asString();
}
});
}