2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Rename scope references to module-local references
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2022-01-01 14:26:40 +01:00
|
|
|
// Copyright 2003-2022 by Wilson Snyder. This program is free software; you
|
2020-03-21 16:24:24 +01:00
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 23:07:57 +02:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCOPE TRANSFORMATIONS:
|
2019-05-19 22:13:13 +02:00
|
|
|
// All modules:
|
|
|
|
|
// Each VARREF/FUNCCALL
|
|
|
|
|
// Change varref name() to be relative to current module
|
|
|
|
|
// Remove varScopep()
|
|
|
|
|
// This allows for better V3Combine'ing.
|
2008-06-10 03:25:10 +02:00
|
|
|
//
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2006-12-18 20:20:45 +01:00
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
#include "V3Descope.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Ast.h"
|
2006-08-30 23:07:55 +02:00
|
|
|
#include "V3EmitCBase.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3Global.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2018-10-14 19:43:24 +02:00
|
|
|
#include <map>
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class DescopeVisitor final : public VNVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// Cleared entire netlist
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstCFunc::user() // bool. Indicates processing completed
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser1InUse m_inuser1;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// TYPES
|
2021-03-13 00:10:45 +01:00
|
|
|
using FuncMmap = std::multimap<std::string, AstCFunc*>;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// STATE
|
2020-08-15 19:11:27 +02:00
|
|
|
AstNodeModule* m_modp = nullptr; // Current module
|
2021-06-16 16:57:54 +02:00
|
|
|
const AstScope* m_scopep = nullptr; // Current scope
|
|
|
|
|
const AstCFunc* m_funcp = nullptr; // Current function
|
|
|
|
|
bool m_modSingleton = false; // m_modp is only instantiated once
|
2020-01-20 17:43:41 +01:00
|
|
|
FuncMmap m_modFuncs; // Name of public functions added
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2018-05-14 12:50:47 +02:00
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
2009-01-21 22:56:50 +01:00
|
|
|
|
2017-10-06 00:18:11 +02:00
|
|
|
static bool modIsSingleton(AstNodeModule* modp) {
|
2021-06-13 15:33:11 +02:00
|
|
|
// True iff there's exactly one instance of this module in the design (including top).
|
|
|
|
|
if (modp->isTop()) return true;
|
2017-10-06 00:18:11 +02:00
|
|
|
int instances = 0;
|
2020-01-20 17:43:41 +01:00
|
|
|
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(stmtp, Scope)) {
|
2021-02-22 03:25:21 +01:00
|
|
|
if (++instances > 1) return false;
|
2017-10-06 00:18:11 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return (instances == 1);
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-13 15:33:11 +02:00
|
|
|
// 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->[...]").
|
2017-10-06 00:18:11 +02:00
|
|
|
//
|
2021-06-13 15:33:11 +02:00
|
|
|
// Using relative references allows V3Combine'ing code across multiple instances of the same
|
|
|
|
|
// module.
|
|
|
|
|
string descopedSelfPointer(const AstScope* scopep) {
|
2018-06-14 00:05:00 +02:00
|
|
|
UASSERT(scopep, "Var/Func not scoped");
|
2021-06-16 16:57:54 +02:00
|
|
|
// Static functions can't use relative references via 'this->'
|
|
|
|
|
const bool relativeRefOk = !m_funcp->isStatic();
|
2017-10-06 00:18:11 +02:00
|
|
|
|
2021-06-13 15:33:11 +02:00
|
|
|
UINFO(8, " Descope ref under " << m_scopep << endl);
|
|
|
|
|
UINFO(8, " ref to " << scopep << endl);
|
|
|
|
|
UINFO(8, " aboveScope " << scopep->aboveScopep() << endl);
|
|
|
|
|
|
2022-01-02 21:03:57 +01:00
|
|
|
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";
|
|
|
|
|
} else if (relativeRefOk && scopep == m_scopep) {
|
2021-06-13 15:33:11 +02:00
|
|
|
return "this";
|
2021-07-13 18:42:17 +02:00
|
|
|
} else if (relativeRefOk && !m_modSingleton && scopep->aboveScopep() == m_scopep
|
2021-06-13 15:33:11 +02:00
|
|
|
&& 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.
|
2017-10-06 00:18:11 +02:00
|
|
|
string name = scopep->name();
|
|
|
|
|
string::size_type pos;
|
2020-01-20 17:43:41 +01:00
|
|
|
if ((pos = name.rfind('.')) != string::npos) name.erase(0, pos + 1);
|
2021-06-13 15:33:11 +02:00
|
|
|
return "this->" + name;
|
2017-10-06 00:18:11 +02:00
|
|
|
} else {
|
2021-06-13 15:33:11 +02:00
|
|
|
// Reference to something elsewhere, or relative references are disabled. Use global
|
|
|
|
|
// variable
|
Introduce model interface class, make $root part or Syms (#3036)
This patch implements #3032. Verilator creates a module representing the
SystemVerilog $root scope (V3LinkLevel::wrapTop). Until now, this was
called the "TOP" module, which also acted as the user instantiated model
class. Syms used to hold a pointer to this root module, but hold
instances of any submodule. This patch renames this root scope module
from "TOP" to "$root", and introduces a separate model class which is
now an interface class. As the root module is no longer the user
interface class, it can now be made an instance of Syms, just like any
other submodule. This allows absolute references into the root module to
avoid an additional pointer indirection resulting in a potential speedup
(about 1.5% on OpenTitan). The model class now also contains all non
design specific generated code (e.g.: eval loops, trace config, etc),
which additionally simplifies Verilator internals.
Please see the updated documentation for the model interface changes.
2021-06-21 16:30:20 +02:00
|
|
|
return "(&" + scopep->nameVlSym() + ")";
|
2017-10-06 00:18:11 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void makePublicFuncWrappers() {
|
2019-05-19 22:13:13 +02:00
|
|
|
// We recorded all public functions in m_modFuncs.
|
|
|
|
|
// If for any given name only one function exists, we can use that function directly.
|
|
|
|
|
// If multiple functions exist, we need to select the appropriate scope.
|
2020-01-20 17:43:41 +01:00
|
|
|
for (FuncMmap::iterator it = m_modFuncs.begin(); it != m_modFuncs.end(); ++it) {
|
2021-06-21 00:32:57 +02:00
|
|
|
const string name = it->first;
|
2021-11-13 19:50:44 +01:00
|
|
|
AstCFunc* const topFuncp = it->second;
|
2020-08-16 17:43:49 +02:00
|
|
|
auto nextIt1 = it;
|
2020-01-20 17:43:41 +01:00
|
|
|
++nextIt1;
|
2021-11-26 23:55:36 +01:00
|
|
|
const bool moreOfSame1 = (nextIt1 != m_modFuncs.end() && nextIt1->first == name);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (moreOfSame1) {
|
|
|
|
|
// Multiple functions under this name, need a wrapper function
|
2020-01-20 17:43:41 +01:00
|
|
|
UINFO(6, " Wrapping " << name << " multifuncs\n");
|
2021-11-13 19:50:44 +01:00
|
|
|
AstCFunc* const newfuncp = topFuncp->cloneTree(false);
|
2020-01-20 17:43:41 +01:00
|
|
|
if (newfuncp->initsp()) newfuncp->initsp()->unlinkFrBackWithNext()->deleteTree();
|
|
|
|
|
if (newfuncp->stmtsp()) newfuncp->stmtsp()->unlinkFrBackWithNext()->deleteTree();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (newfuncp->finalsp()) newfuncp->finalsp()->unlinkFrBackWithNext()->deleteTree();
|
|
|
|
|
newfuncp->name(name);
|
|
|
|
|
newfuncp->isStatic(false);
|
|
|
|
|
topFuncp->addNextHere(newfuncp);
|
|
|
|
|
// In the body, call each function if it matches the given scope
|
|
|
|
|
for (FuncMmap::iterator eachIt = it;
|
2020-01-20 17:43:41 +01:00
|
|
|
eachIt != m_modFuncs.end() && eachIt->first == name; ++eachIt) {
|
2019-05-19 22:13:13 +02:00
|
|
|
it = eachIt;
|
2021-11-13 19:50:44 +01:00
|
|
|
AstCFunc* const funcp = eachIt->second;
|
2020-08-16 17:43:49 +02:00
|
|
|
auto nextIt2 = eachIt;
|
2020-01-20 17:43:41 +01:00
|
|
|
++nextIt2;
|
2021-06-21 00:32:57 +02:00
|
|
|
const bool moreOfSame
|
|
|
|
|
= (nextIt2 != m_modFuncs.end() && nextIt2->first == name);
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(funcp->scopep(), funcp, "Not scoped");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-01-20 17:43:41 +01:00
|
|
|
UINFO(6, " Wrapping " << name << " " << funcp << endl);
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(6,
|
|
|
|
|
" at " << newfuncp->argTypes() << " und " << funcp->argTypes() << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
funcp->declPrivate(true);
|
2022-09-17 14:48:51 +02:00
|
|
|
AstVarRef* argsp = nullptr;
|
2020-01-20 17:43:41 +01:00
|
|
|
for (AstNode* stmtp = newfuncp->argsp(); stmtp; stmtp = stmtp->nextp()) {
|
2021-11-13 19:50:44 +01:00
|
|
|
if (AstVar* const portp = VN_CAST(stmtp, Var)) {
|
2018-10-27 23:29:00 +02:00
|
|
|
if (portp->isIO() && !portp->isFuncReturn()) {
|
2022-09-17 14:48:51 +02:00
|
|
|
AstVarRef* const newp = new AstVarRef(
|
2021-11-13 19:50:44 +01:00
|
|
|
portp->fileline(), portp,
|
|
|
|
|
portp->isWritable() ? VAccess::WRITE : VAccess::READ);
|
2022-09-17 14:48:51 +02:00
|
|
|
argsp = AstNode::addNext(argsp, newp);
|
2018-10-27 23:29:00 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNode* const returnp = new AstCReturn(
|
2020-01-20 17:43:41 +01:00
|
|
|
funcp->fileline(), new AstCCall(funcp->fileline(), funcp, argsp));
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
if (moreOfSame) {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstIf* const ifp = new AstIf(
|
2020-01-20 17:43:41 +01:00
|
|
|
funcp->fileline(),
|
|
|
|
|
new AstEq(
|
|
|
|
|
funcp->fileline(), new AstCMath(funcp->fileline(), "this", 64),
|
|
|
|
|
new AstCMath(funcp->fileline(),
|
|
|
|
|
string("&(") + funcp->scopep()->nameVlSym() + ")",
|
|
|
|
|
64)),
|
2022-04-23 15:16:19 +02:00
|
|
|
returnp);
|
2019-05-19 22:13:13 +02:00
|
|
|
newfuncp->addStmtsp(ifp);
|
|
|
|
|
} else {
|
|
|
|
|
newfuncp->addStmtsp(returnp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Not really any way the user could do this, and we'd need
|
|
|
|
|
// to come up with some return value
|
2020-01-20 17:43:41 +01:00
|
|
|
// newfuncp->addStmtsp(new AstDisplay(newfuncp->fileline(),
|
2022-01-02 19:56:40 +01:00
|
|
|
// VDisplayType::DT_WARNING,
|
2020-01-20 17:43:41 +01:00
|
|
|
// string("%%Error: ")+name+"() called with bad
|
2020-08-15 16:12:55 +02:00
|
|
|
// scope", nullptr));
|
2020-01-20 17:43:41 +01:00
|
|
|
// newfuncp->addStmtsp(new AstStop(newfuncp->fileline()));
|
|
|
|
|
if (debug() >= 9) newfuncp->dumpTree(cout, " newfunc: ");
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2022-03-31 02:17:59 +02:00
|
|
|
// Only a single function under this name, we can rename it
|
2020-01-20 17:43:41 +01:00
|
|
|
UINFO(6, " Wrapping " << name << " just one " << topFuncp << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
topFuncp->name(name);
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNetlist* nodep) override {
|
2021-08-12 22:43:32 +02:00
|
|
|
nodep->dpiExportTriggerp(nullptr);
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_modp);
|
2020-01-20 19:27:27 +01:00
|
|
|
{
|
|
|
|
|
m_modp = nodep;
|
|
|
|
|
m_modFuncs.clear();
|
|
|
|
|
m_modSingleton = modIsSingleton(m_modp);
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
makePublicFuncWrappers();
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstScope* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_scopep = nodep;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2020-08-15 16:12:55 +02:00
|
|
|
m_scopep = nullptr;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarScope* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Delete the varscope when we're finished
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
pushDeletep(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeVarRef* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2021-06-16 18:44:06 +02:00
|
|
|
if (!nodep->varScopep()) {
|
|
|
|
|
UASSERT_OBJ(nodep->varp()->isFuncLocal(), nodep,
|
|
|
|
|
"unscoped reference can only appear to function locals at this point");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
// Convert the hierch name
|
2020-11-25 04:46:02 +01:00
|
|
|
UINFO(9, " ref-in " << nodep << endl);
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_scopep, nodep, "Node not under scope");
|
2021-06-13 15:33:11 +02:00
|
|
|
const AstVar* const varp = nodep->varScopep()->varp();
|
|
|
|
|
const AstScope* const scopep = nodep->varScopep()->scopep();
|
2021-07-13 18:42:17 +02:00
|
|
|
if (varp->isFuncLocal()) {
|
|
|
|
|
// Reference to function locals need no self pointer
|
|
|
|
|
nodep->selfPointer("");
|
2021-07-22 14:33:00 +02:00
|
|
|
} else if (scopep->modp() == v3Global.rootp()->constPoolp()->modp()) {
|
|
|
|
|
// Reference to constant pool value need no self pointer
|
|
|
|
|
nodep->selfPointer("");
|
2021-07-13 18:42:17 +02:00
|
|
|
} else {
|
|
|
|
|
nodep->selfPointer(descopedSelfPointer(scopep));
|
|
|
|
|
}
|
2020-08-15 16:12:55 +02:00
|
|
|
nodep->varScopep(nullptr);
|
2022-01-02 21:03:57 +01:00
|
|
|
UINFO(9, " refout " << nodep << " selfPtr=" << nodep->selfPointer() << endl);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCCall* nodep) override {
|
2020-04-15 13:58:34 +02:00
|
|
|
// UINFO(9, " " << nodep << endl);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Convert the hierch name
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_scopep, nodep, "Node not under scope");
|
2021-06-13 15:33:11 +02:00
|
|
|
const AstScope* const scopep = nodep->funcp()->scopep();
|
2022-01-02 21:03:57 +01:00
|
|
|
nodep->selfPointer(descopedSelfPointer(scopep));
|
2019-05-19 22:13:13 +02:00
|
|
|
// Can't do this, as we may have more calls later
|
2020-08-15 16:12:55 +02:00
|
|
|
// nodep->funcp()->scopep(nullptr);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCMethodCall* nodep) override { iterateChildren(nodep); }
|
|
|
|
|
void visit(AstCNew* nodep) override { iterateChildren(nodep); }
|
|
|
|
|
void visit(AstCFunc* nodep) override {
|
2021-06-16 16:57:54 +02:00
|
|
|
VL_RESTORER(m_funcp);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!nodep->user1()) {
|
2021-06-16 16:57:54 +02:00
|
|
|
// Static functions should have been moved under the corresponding AstClassPackage
|
2022-07-13 00:02:45 +02:00
|
|
|
UASSERT_OBJ(!(nodep->isStatic() && VN_IS(m_modp, Class)), nodep,
|
|
|
|
|
"Static function under AstClass");
|
2021-06-16 16:57:54 +02:00
|
|
|
m_funcp = nodep;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-01-16 06:38:42 +01:00
|
|
|
nodep->user1(true);
|
2019-05-19 22:13:13 +02:00
|
|
|
// If it's under a scope, move it up to the top
|
|
|
|
|
if (m_scopep) {
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
m_modp->addStmtp(nodep);
|
2006-10-06 18:08:46 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->funcPublic()) {
|
|
|
|
|
// There may be multiple public functions by the same name;
|
|
|
|
|
// record for later correction or making of shells
|
2020-12-19 00:24:47 +01:00
|
|
|
m_modFuncs.emplace(nodep->name(), nodep);
|
2020-01-20 17:43:41 +01:00
|
|
|
nodep->name(m_scopep->nameDotless() + "__" + nodep->name());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVar*) override {}
|
|
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2020-01-20 17:43:41 +01:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2020-08-15 19:11:27 +02:00
|
|
|
explicit DescopeVisitor(AstNetlist* nodep) { iterate(nodep); }
|
2022-09-16 12:22:11 +02:00
|
|
|
~DescopeVisitor() override = default;
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Descope class functions
|
|
|
|
|
|
|
|
|
|
void V3Descope::descopeAll(AstNetlist* nodep) {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2021-11-26 16:52:36 +01:00
|
|
|
{ DescopeVisitor{nodep}; } // Destruct before checking
|
2017-09-18 04:52:57 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("descope", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|