mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 10:03:44 +02:00
Emit model implementation as loose methods. (#3006)
This patch introduces the concept of 'loose' methods, which semantically are methods, but are declared as global functions, and are passed an explicit 'self' pointer. This enables these methods to be declared outside the class, only when they are needed, therefore removing the header dependency. The bulk of the emitted model implementation now uses loose methods.
This commit is contained in:
+50
-72
@@ -56,7 +56,8 @@ private:
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
static bool modIsSingleton(AstNodeModule* modp) {
|
||||
// True iff there's exactly one instance of this module in the design.
|
||||
// True iff there's exactly one instance of this module in the design (including top).
|
||||
if (modp->isTop()) return true;
|
||||
int instances = 0;
|
||||
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
if (VN_IS(stmtp, Scope)) {
|
||||
@@ -66,85 +67,68 @@ private:
|
||||
return (instances == 1);
|
||||
}
|
||||
|
||||
// Construct the best prefix to reference an object in 'scopep'
|
||||
// from a CFunc in 'm_scopep'. Result may be relative
|
||||
// ("this->[...]") or absolute ("vlTOPp->[...]").
|
||||
// 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.
|
||||
//
|
||||
// Sets 'hierThisr' true if the object is local to this scope
|
||||
// (and could be made into a function-local later in V3Localize),
|
||||
// false if the object is in another scope.
|
||||
string descopedName(bool& hierThisr, string& hierUnprot, const AstScope* scopep,
|
||||
const AstVar* varp) {
|
||||
// Using relative references allows V3Combine'ing code across multiple instances of the same
|
||||
// module.
|
||||
string descopedSelfPointer(const AstScope* scopep) {
|
||||
UASSERT(scopep, "Var/Func not scoped");
|
||||
hierThisr = (scopep == m_scopep);
|
||||
|
||||
// It's possible to disable relative references. This is a concession
|
||||
// to older compilers (gcc < 4.5.x) that don't understand __restrict__
|
||||
// well and emit extra ld/st to guard against pointer aliasing
|
||||
// when this-> and vlTOPp-> are mixed in the same function.
|
||||
//
|
||||
// "vlTOPp" is declared "restrict" so better compilers understand
|
||||
// that it won't alias with "this".
|
||||
// when this-> and vlSyms-> are mixed in the same function.
|
||||
bool relativeRefOk = v3Global.opt.relativeCFuncs();
|
||||
//
|
||||
// Static functions can't use this
|
||||
if (!m_allowThis) relativeRefOk = false;
|
||||
//
|
||||
// Use absolute refs in top-scoped routines, keep them static.
|
||||
// The DPI callback registration depends on representing top-level
|
||||
// static routines as plain function pointers. That breaks if those
|
||||
// become true OO routines.
|
||||
//
|
||||
// V3Combine wouldn't likely be able to combine top-level
|
||||
// routines anyway, so there's no harm in keeping these static.
|
||||
UASSERT_OBJ(m_modp, scopep, "Scope not under module");
|
||||
if (m_modp->isTop()) relativeRefOk = false;
|
||||
//
|
||||
// Use absolute refs if this scope is the only instance of the module.
|
||||
// Saves a bit of overhead on passing the 'this' pointer, and there's no
|
||||
// need to be nice to V3Combine when we have only a single instance.
|
||||
// The risk that this prevents combining identical logic from differently-
|
||||
// named but identical modules seems low.
|
||||
if (m_modSingleton) relativeRefOk = false;
|
||||
//
|
||||
// Class methods need relative
|
||||
if (m_modp && VN_IS(m_modp, Class)) relativeRefOk = true;
|
||||
|
||||
if (varp && varp->isFuncLocal()) {
|
||||
hierThisr = true;
|
||||
return ""; // Relative to function, not in this
|
||||
} else if (relativeRefOk && scopep == m_scopep) {
|
||||
UINFO(8, " Descope ref under " << m_scopep << endl);
|
||||
UINFO(8, " ref to " << scopep << endl);
|
||||
UINFO(8, " aboveScope " << scopep->aboveScopep() << endl);
|
||||
|
||||
if (relativeRefOk && scopep == m_scopep) {
|
||||
m_needThis = true;
|
||||
return "this->";
|
||||
return "this";
|
||||
} else if (VN_IS(scopep->modp(), Class)) {
|
||||
hierUnprot = v3Global.opt.modPrefix() + "_"; // Prefix before protected part
|
||||
return scopep->modp()->name() + "::";
|
||||
} else if (relativeRefOk && scopep->aboveScopep() && scopep->aboveScopep() == m_scopep) {
|
||||
// Reference to scope of instance directly under this module, can just "cell->"
|
||||
return "";
|
||||
} else if (!m_modSingleton && relativeRefOk && 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);
|
||||
m_needThis = true;
|
||||
return name + "->";
|
||||
return "this->" + name;
|
||||
} else {
|
||||
// Reference to something elsewhere, or relative references
|
||||
// are disabled. Use global variable
|
||||
UINFO(8, " Descope " << scopep << endl);
|
||||
UINFO(8, " to " << scopep->name() << endl);
|
||||
UINFO(8, " under " << m_scopep->name() << endl);
|
||||
if (!scopep->aboveScopep()) { // Top
|
||||
// We could also return "vlSymsp->TOPp->" here, but GCC would
|
||||
// suspect aliases.
|
||||
return "vlTOPp->";
|
||||
// Reference to something elsewhere, or relative references are disabled. Use global
|
||||
// variable
|
||||
if (scopep->isTop()) { // Top
|
||||
return "vlSymsp->TOPp";
|
||||
} else {
|
||||
return scopep->nameVlSym() + ".";
|
||||
return "(&" + scopep->nameVlSym() + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct the class prefix (as in, the part before the :: scope resolution operator) when
|
||||
// referencing an object in 'scopep' from a CFunc in 'm_scopep'.
|
||||
string descopedClassPrefix(const AstScope* scopep) {
|
||||
UASSERT(scopep, "Var/Func not scoped");
|
||||
if (VN_IS(scopep->modp(), Class) && scopep != m_scopep) {
|
||||
return scopep->modp()->name();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void makePublicFuncWrappers() {
|
||||
// We recorded all public functions in m_modFuncs.
|
||||
// If for any given name only one function exists, we can use that function directly.
|
||||
@@ -164,11 +148,6 @@ private:
|
||||
if (newfuncp->finalsp()) newfuncp->finalsp()->unlinkFrBackWithNext()->deleteTree();
|
||||
newfuncp->name(name);
|
||||
newfuncp->isStatic(false);
|
||||
newfuncp->addInitsp(
|
||||
new AstCStmt(newfuncp->fileline(),
|
||||
EmitCBaseVisitor::symClassVar() + " = this->__VlSymsp;\n"));
|
||||
newfuncp->addInitsp(
|
||||
new AstCStmt(newfuncp->fileline(), EmitCBaseVisitor::symTopAssign() + "\n"));
|
||||
topFuncp->addNextHere(newfuncp);
|
||||
// In the body, call each function if it matches the given scope
|
||||
for (FuncMmap::iterator eachIt = it;
|
||||
@@ -255,13 +234,15 @@ private:
|
||||
// Convert the hierch name
|
||||
UINFO(9, " ref-in " << nodep << endl);
|
||||
UASSERT_OBJ(m_scopep, nodep, "Node not under scope");
|
||||
bool hierThis;
|
||||
string hierUnprot;
|
||||
nodep->hiernameToProt(descopedName(hierThis /*ref*/, hierUnprot /*ref*/,
|
||||
nodep->varScopep()->scopep(),
|
||||
nodep->varScopep()->varp()));
|
||||
nodep->hiernameToUnprot(hierUnprot);
|
||||
nodep->hierThis(hierThis);
|
||||
const AstVar* const varp = nodep->varScopep()->varp();
|
||||
const AstScope* const scopep = nodep->varScopep()->scopep();
|
||||
if (varp->isFuncLocal()) {
|
||||
nodep->hierThis(true);
|
||||
} else {
|
||||
nodep->hierThis(scopep == m_scopep);
|
||||
nodep->selfPointer(descopedSelfPointer(scopep));
|
||||
nodep->classPrefix(descopedClassPrefix(scopep));
|
||||
}
|
||||
nodep->varScopep(nullptr);
|
||||
UINFO(9, " refout " << nodep << endl);
|
||||
}
|
||||
@@ -270,12 +251,9 @@ private:
|
||||
iterateChildren(nodep);
|
||||
// Convert the hierch name
|
||||
UASSERT_OBJ(m_scopep, nodep, "Node not under scope");
|
||||
UASSERT_OBJ(nodep->funcp()->scopep(), nodep, "CFunc not under scope");
|
||||
bool hierThis;
|
||||
string hierUnprot;
|
||||
nodep->hiernameToProt(
|
||||
descopedName(hierThis /*ref*/, hierUnprot /*ref*/, nodep->funcp()->scopep(), nullptr));
|
||||
nodep->hiernameToUnprot(hierUnprot);
|
||||
const AstScope* const scopep = nodep->funcp()->scopep();
|
||||
nodep->selfPointer(descopedSelfPointer(scopep));
|
||||
nodep->classPrefix(descopedClassPrefix(scopep));
|
||||
// Can't do this, as we may have more calls later
|
||||
// nodep->funcp()->scopep(nullptr);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user