From 686baaf2cf9ed1a1fd9ec0d0b0b4113351a21f00 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 22 Jun 2021 13:50:21 +0100 Subject: [PATCH] Internals: Streamline trace function generation Remove magic code fragments form EmitCTrace, so Emit need not be aware that a function is tracing related or not (apart from the purpose of file name generation). All necessary code is now generated via text nodes in V3TraceDecl and V3Trace. No functional change intended. --- src/V3Ast.h | 33 -------- src/V3AstNodes.h | 20 +++-- src/V3EmitC.cpp | 156 ++++++++------------------------------ src/V3EmitC.h | 1 - src/V3EmitCFunc.h | 2 - src/V3Trace.cpp | 178 ++++++++++++++++++++++---------------------- src/V3TraceDecl.cpp | 11 ++- src/Verilator.cpp | 1 - 8 files changed, 143 insertions(+), 259 deletions(-) diff --git a/src/V3Ast.h b/src/V3Ast.h index afa26f75d..b569d76a9 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -238,39 +238,6 @@ inline bool operator==(AstPragmaType::en lhs, const AstPragmaType& rhs) { return //###################################################################### -class AstCFuncType final { -public: - enum en : uint8_t { - FT_NORMAL, - TRACE_REGISTER, - TRACE_INIT, - TRACE_INIT_SUB, - TRACE_FULL, - TRACE_FULL_SUB, - TRACE_CHANGE, - TRACE_CHANGE_SUB, - TRACE_CLEANUP - }; - enum en m_e; - inline AstCFuncType() - : m_e{FT_NORMAL} {} - // cppcheck-suppress noExplicitConstructor - inline AstCFuncType(en _e) - : m_e{_e} {} - explicit inline AstCFuncType(int _e) - : m_e(static_cast(_e)) {} // Need () or GCC 4.8 false warning - operator en() const { return m_e; } - // METHODS - bool isTrace() const { return m_e != FT_NORMAL; } -}; -inline bool operator==(const AstCFuncType& lhs, const AstCFuncType& rhs) { - return lhs.m_e == rhs.m_e; -} -inline bool operator==(const AstCFuncType& lhs, AstCFuncType::en rhs) { return lhs.m_e == rhs; } -inline bool operator==(AstCFuncType::en lhs, const AstCFuncType& rhs) { return lhs == rhs.m_e; } - -//###################################################################### - class VEdgeType final { public: // REMEMBER to edit the strings below too diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 731bcc1dc..f75c67a82 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -5244,11 +5244,14 @@ class AstTraceInc final : public AstNodeStmt { private: AstTraceDecl* m_declp; // Pointer to declaration const bool m_full; // Is this a full vs incremental dump + const uint32_t m_baseCode; // Trace code base value in function containing this AstTraceInc + public: - AstTraceInc(FileLine* fl, AstTraceDecl* declp, bool full) + AstTraceInc(FileLine* fl, AstTraceDecl* declp, bool full, uint32_t baseCode = 0) : ASTGEN_SUPER_TraceInc(fl) , m_declp{declp} - , m_full{full} { + , m_full{full} + , m_baseCode{baseCode} { dtypeFrom(declp); addOp2p(declp->valuep()->cloneTree(true)); } @@ -5276,6 +5279,7 @@ public: AstNode* valuep() const { return op2p(); } AstTraceDecl* declp() const { return m_declp; } bool full() const { return m_full; } + uint32_t baseCode() const { return m_baseCode; } }; class AstActive final : public AstNode { @@ -8728,7 +8732,6 @@ class AstCFunc final : public AstNode { // Parents: MODULE/SCOPE // Children: VAR/statements private: - AstCFuncType m_funcType; AstScope* m_scopep; string m_name; string m_cname; // C name, for dpiExports @@ -8738,6 +8741,7 @@ private: string m_ifdef; // #ifdef symbol around this function VBoolOrUnknown m_isConst; // Function is declared const (*this not changed) bool m_isStatic : 1; // Function is static (no need for a 'this' pointer) + bool m_isTrace : 1; // Function is related to tracing bool m_dontCombine : 1; // V3Combine shouldn't compare this func tree, it's special bool m_declPrivate : 1; // Declare it private bool m_formCallTree : 1; // Make a global function to call entire tree of functions @@ -8759,12 +8763,12 @@ private: public: AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "") : ASTGEN_SUPER_CFunc(fl) { - m_funcType = AstCFuncType::FT_NORMAL; m_isConst = VBoolOrUnknown::BU_UNKNOWN; // Unknown until analyzed m_scopep = scopep; m_name = name; m_rtnType = rtnType; m_isStatic = false; + m_isTrace = false; m_dontCombine = false; m_declPrivate = false; m_formCallTree = false; @@ -8793,7 +8797,7 @@ public: virtual void dump(std::ostream& str = std::cout) const override; virtual bool same(const AstNode* samep) const override { const AstCFunc* asamep = static_cast(samep); - return ((funcType() == asamep->funcType()) && (rtnTypeVoid() == asamep->rtnTypeVoid()) + return ((isTrace() == asamep->isTrace()) && (rtnTypeVoid() == asamep->rtnTypeVoid()) && (argTypes() == asamep->argTypes()) && (ctorInits() == asamep->ctorInits()) && isLoose() == asamep->isLoose() && (!(dpiImportPrototype() || dpiExportImpl()) || name() == asamep->name())); @@ -8806,12 +8810,14 @@ public: void isConst(VBoolOrUnknown flag) { m_isConst = flag; } bool isStatic() const { return m_isStatic; } void isStatic(bool flag) { m_isStatic = flag; } + bool isTrace() const { return m_isTrace; } + void isTrace(bool flag) { m_isTrace = flag; } void cname(const string& name) { m_cname = name; } string cname() const { return m_cname; } AstScope* scopep() const { return m_scopep; } void scopep(AstScope* nodep) { m_scopep = nodep; } string rtnTypeVoid() const { return ((m_rtnType == "") ? "void" : m_rtnType); } - bool dontCombine() const { return m_dontCombine || funcType() != AstCFuncType::FT_NORMAL; } + bool dontCombine() const { return m_dontCombine || isTrace(); } void dontCombine(bool flag) { m_dontCombine = flag; } bool dontInline() const { return dontCombine() || slow() || funcPublic(); } bool declPrivate() const { return m_declPrivate; } @@ -8828,8 +8834,6 @@ public: string ctorInits() const { return m_ctorInits; } void ifdef(const string& str) { m_ifdef = str; } string ifdef() const { return m_ifdef; } - void funcType(AstCFuncType flag) { m_funcType = flag; } - AstCFuncType funcType() const { return m_funcType; } bool isConstructor() const { return m_isConstructor; } void isConstructor(bool flag) { m_isConstructor = flag; } bool isDestructor() const { return m_isDestructor; } diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index fc8cb08fa..4e7f7cdae 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -74,7 +74,7 @@ class EmitCImp final : EmitCFunc { using EmitCFunc::visit; // Suppress hidden overloaded virtual function warning virtual void visit(AstCFunc* nodep) override { // TRACE_* and DPI handled elsewhere - if (nodep->funcType().isTrace()) return; + if (nodep->isTrace()) return; if (nodep->dpiImportPrototype()) return; if (nodep->dpiExportDispatcher()) return; if (!(nodep->slow() ? m_slow : m_fast)) return; @@ -675,10 +675,8 @@ class EmitCTrace final : EmitCFunc { AstUser1InUse m_inuser1; // MEMBERS - AstCFunc* m_cfuncp = nullptr; // Function we're in now - bool m_slow; // Making slow file + const bool m_slow; // Making slow file int m_enumNum = 0; // Enumeration number (whole netlist) - int m_baseCode = -1; // Code of first AstTraceInc in this function // METHODS void newOutCFile(int filenum) { @@ -703,10 +701,6 @@ class EmitCTrace final : EmitCFunc { m_ofp->puts("// DESCR" "IPTION: Verilator output: Tracing implementation internals\n"); - emitTraceHeader(); - } - - void emitTraceHeader() { // Includes puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n"); puts("#include \"" + symClassName() + ".h\"\n"); @@ -895,12 +889,13 @@ class EmitCTrace final : EmitCFunc { const uint32_t offset = (arrayindex < 0) ? 0 : (arrayindex * nodep->declp()->widthWords()); const uint32_t code = nodep->declp()->code() + offset; puts(v3Global.opt.trueTraceThreads() && !nodep->full() ? "(base+" : "(oldp+"); - puts(cvtToStr(code - m_baseCode)); + puts(cvtToStr(code - nodep->baseCode())); puts(","); emitTraceValue(nodep, arrayindex); if (emitWidth) puts("," + cvtToStr(nodep->declp()->widthMin())); puts(");\n"); } + void emitTraceValue(AstTraceInc* nodep, int arrayindex) { if (AstVarRef* const varrefp = VN_CAST(nodep->valuep(), VarRef)) { AstVar* varp = varrefp->varp(); @@ -939,92 +934,20 @@ class EmitCTrace final : EmitCFunc { // VISITORS using EmitCFunc::visit; // Suppress hidden overloaded virtual function warning - virtual void visit(AstNetlist* nodep) override { - // Top module only - iterate(nodep->topModulep()); - } - virtual void visit(AstNodeModule* nodep) override { - m_modp = nodep; - iterateChildren(nodep); - m_modp = nullptr; - } virtual void visit(AstCFunc* nodep) override { + if (!nodep->isTrace()) return; if (nodep->slow() != m_slow) return; - VL_RESTORER(m_cfuncp); - VL_RESTORER(m_useSelfForThis); - if (nodep->funcType().isTrace()) { // TRACE_* - m_cfuncp = nodep; - if (splitNeeded()) { - // Splitting file, so using parallel build. - v3Global.useParallelBuild(true); - // Close old file - VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); - // Open a new file - newOutCFile(splitFilenumInc()); - } - - splitSizeInc(nodep); - - puts("\n"); - m_lazyDecls.emit(nodep); - emitCFuncHeader(nodep, m_modp, /* withScope: */ true); - puts(" {\n"); - - if (nodep->isLoose()) { - m_lazyDecls.declared(nodep); // Defined here, so no longer needs declaration - if (!nodep->isStatic()) { // Standard prologue - puts("if (false && vlSelf) {} // Prevent unused\n"); - m_useSelfForThis = true; - puts(symClassAssign()); - } - } - - if (nodep->initsp()) { - string section; - emitVarList(nodep->initsp(), EVL_FUNC_ALL, "", section /*ref*/); - iterateAndNextNull(nodep->initsp()); - } - - m_baseCode = -1; - - if (nodep->funcType() == AstCFuncType::TRACE_CHANGE_SUB) { - const AstNode* const stmtp = nodep->stmtsp(); - const AstIf* const ifp = VN_CAST_CONST(stmtp, If); - const AstTraceInc* const tracep - = VN_CAST_CONST(ifp ? ifp->ifsp() : stmtp, TraceInc); - // On rare occasions we can end up with an empty sub function - m_baseCode = tracep ? tracep->declp()->code() : 0; - if (v3Global.opt.trueTraceThreads()) { - puts("const vluint32_t base = vlSymsp->__Vm_baseCode + " + cvtToStr(m_baseCode) - + ";\n"); - puts("if (false && tracep && base) {} // Prevent unused\n"); - } else { - puts("vluint32_t* const oldp = tracep->oldp(vlSymsp->__Vm_baseCode + " - + cvtToStr(m_baseCode) + ");\n"); - puts("if (false && oldp) {} // Prevent unused\n"); - } - } else if (nodep->funcType() == AstCFuncType::TRACE_FULL_SUB) { - m_baseCode = 0; - puts("vluint32_t* const oldp = tracep->oldp(vlSymsp->__Vm_baseCode);\n"); - puts("if (false && oldp) {} // Prevent unused\n"); - } else if (nodep->funcType() == AstCFuncType::TRACE_INIT_SUB) { - puts("const int c = vlSymsp->__Vm_baseCode;\n"); - puts("if (false && tracep && c) {} // Prevent unused\n"); - } - - if (nodep->stmtsp()) { - putsDecoration("// Body\n"); - puts("{\n"); - iterateAndNextNull(nodep->stmtsp()); - puts("}\n"); - } - if (nodep->finalsp()) { - putsDecoration("// Final\n"); - iterateAndNextNull(nodep->finalsp()); - } - puts("}\n"); + if (splitNeeded()) { + // Splitting file, so using parallel build. + v3Global.useParallelBuild(true); + // Close old file + VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); + // Open a new file + newOutCFile(splitFilenumInc()); } + + EmitCFunc::visit(nodep); } virtual void visit(AstTraceDecl* nodep) override { const int enumNum = emitTraceDeclDType(nodep->dtypep()); @@ -1047,28 +970,32 @@ class EmitCTrace final : EmitCFunc { emitTraceChangeOne(nodep, -1); } } - virtual void visit(AstCoverDecl* nodep) override {} - virtual void visit(AstCoverInc* nodep) override {} -public: - explicit EmitCTrace(bool slow) - : m_slow{slow} {} - virtual ~EmitCTrace() override = default; - void main() { - // Put out the file + explicit EmitCTrace(AstNodeModule* modp, bool slow) + : m_slow{slow} { + m_modp = modp; + // Open output file newOutCFile(0); - - iterate(v3Global.rootp()); - + // Emit functions + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) { iterate(funcp); } + } + // Close output file VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); } + virtual ~EmitCTrace() override = default; + +public: + static void main(AstNodeModule* modp, bool slow) { EmitCTrace(modp, slow); } }; //###################################################################### // EmitC class functions -static void setParentClassPointers() { +void V3EmitC::emitc() { + UINFO(2, __FUNCTION__ << ": " << endl); // Set user4p in all CFunc and Var to point to the containing AstNodeModule + AstUser4InUse user4InUse; const auto setAll = [](AstNodeModule* modp) -> void { for (AstNode* nodep = VN_CAST(modp, NodeModule)->stmtsp(); nodep; nodep = nodep->nextp()) { if (VN_IS(nodep, CFunc) || VN_IS(nodep, Var)) nodep->user4p(modp); @@ -1078,13 +1005,7 @@ static void setParentClassPointers() { setAll(VN_CAST(modp, NodeModule)); } setAll(v3Global.rootp()->constPoolp()->modp()); -} -void V3EmitC::emitc() { - UINFO(2, __FUNCTION__ << ": " << endl); - // Set user4 to parent module - AstUser4InUse user4InUse; - setParentClassPointers(); // Process each module in turn for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep = VN_CAST(nodep->nextp(), NodeModule)) { @@ -1099,22 +1020,11 @@ void V3EmitC::emitc() { fast.mainImp(nodep, false); } } -} -void V3EmitC::emitcTrace() { - UINFO(2, __FUNCTION__ << ": " << endl); + // Emit trace routines (currently they can only exist in the top module) if (v3Global.opt.trace()) { - // Set user4 to parent module - AstUser4InUse user4InUse; - setParentClassPointers(); - { - EmitCTrace slow(true); - slow.main(); - } - { - EmitCTrace fast(false); - fast.main(); - } + EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true); + EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false); } } diff --git a/src/V3EmitC.h b/src/V3EmitC.h index 406a00429..e52552734 100644 --- a/src/V3EmitC.h +++ b/src/V3EmitC.h @@ -32,7 +32,6 @@ public: static void emitcInlines(); static void emitcModel(); static void emitcSyms(bool dpiHdrOnly = false); - static void emitcTrace(); static void emitcFiles(); }; diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index bee0293a4..38f18a00e 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1223,8 +1223,6 @@ public: virtual void visit(AstCell*) override {} // Handled outside the Visit class virtual void visit(AstVar*) override {} // Handled outside the Visit class virtual void visit(AstNodeText*) override {} // Handled outside the Visit class - virtual void visit(AstTraceDecl*) override {} // Handled outside the Visit class - virtual void visit(AstTraceInc*) override {} // Handled outside the Visit class virtual void visit(AstCFile*) override {} // Handled outside the Visit class virtual void visit(AstCellInline*) override {} // Handled outside visit (in EmitCSyms) virtual void visit(AstCUse*) override {} // Handled outside the Visit class diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index 062cd2ea1..a5901d1e8 100644 --- a/src/V3Trace.cpp +++ b/src/V3Trace.cpp @@ -171,6 +171,7 @@ private: AstNodeModule* m_topModp = nullptr; // Module to add variables to AstScope* m_topScopep = nullptr; // Scope to add variables to AstCFunc* m_cfuncp = nullptr; // C function adding to graph + AstCFunc* m_regFuncp = nullptr; // Trace registration function AstTraceDecl* m_tracep = nullptr; // Trace function adding to graph AstVarScope* m_activityVscp = nullptr; // Activity variable uint32_t m_activityNumber = 0; // Count of fields in activity variable @@ -472,69 +473,77 @@ private: } } - AstCFunc* newCFunc(AstCFuncType type, AstCFunc* callfromp, AstCFunc* regp, int& funcNump) { + AstCFunc* newCFunc(bool full, AstCFunc* topFuncp, int& funcNump, uint32_t baseCode = 0) { // Create new function - string name; - switch (type) { - case AstCFuncType::TRACE_FULL: name = "trace_full_top_"; break; - case AstCFuncType::TRACE_FULL_SUB: name = "trace_full_sub_"; break; - case AstCFuncType::TRACE_CHANGE: name = "trace_chg_top_"; break; - case AstCFuncType::TRACE_CHANGE_SUB: name = "trace_chg_sub_"; break; - default: m_topScopep->v3fatalSrc("Bad trace function type"); - } - name += cvtToStr(funcNump++); - FileLine* const flp = m_topScopep->fileline(); - AstCFunc* const funcp = new AstCFunc(flp, name, m_topScopep); - funcp->funcType(type); - funcp->dontCombine(true); - const bool isTopFunc - = type == AstCFuncType::TRACE_FULL || type == AstCFuncType::TRACE_CHANGE; - if (isTopFunc) { - funcp->argTypes("void* voidSelf, " + v3Global.opt.traceClassBase() + "* tracep"); - funcp->isStatic(true); - funcp->addInitsp(new AstCStmt( - flp, prefixNameProtect(m_topModp) + "* const __restrict vlSelf = static_cast<" - + prefixNameProtect(m_topModp) + "*>(voidSelf);\n")); - funcp->addInitsp(new AstCStmt(flp, symClassAssign())); + const bool isTopFunc = topFuncp == nullptr; + const string baseName = full && isTopFunc ? "trace_full_top_" + : full ? "trace_full_sub_" + : isTopFunc ? "trace_chg_top_" + : "trace_chg_sub_"; - } else { - funcp->argTypes(v3Global.opt.traceClassBase() + "* tracep"); - funcp->isStatic(false); - } + FileLine* const flp = m_topScopep->fileline(); + AstCFunc* const funcp = new AstCFunc(flp, baseName + cvtToStr(funcNump++), m_topScopep); + funcp->isTrace(true); + funcp->dontCombine(true); funcp->isLoose(true); - funcp->slow(type == AstCFuncType::TRACE_FULL || type == AstCFuncType::TRACE_FULL_SUB); + funcp->slow(full); + funcp->isStatic(isTopFunc); // Add it to top scope m_topScopep->addActivep(funcp); - // Add call to new function - if (callfromp) { + const auto addInitStr = [funcp, flp](const string& str) -> void { + funcp->addInitsp(new AstCStmt(flp, str)); + }; + if (isTopFunc) { + // Top functions + funcp->argTypes("void* voidSelf, " + v3Global.opt.traceClassBase() + "* tracep"); + addInitStr(voidSelfAssign(m_topModp)); + addInitStr(symClassAssign()); + // Add global activity check to change dump functions + if (!full) { // + addInitStr("if (VL_UNLIKELY(!vlSymsp->__Vm_activity)) return;\n"); + } + // Register function + if (full) { + m_regFuncp->addStmtsp(new AstText(flp, "tracep->addFullCb(", true)); + } else { + m_regFuncp->addStmtsp(new AstText(flp, "tracep->addChgCb(", true)); + } + m_regFuncp->addStmtsp(new AstAddrOfCFunc(flp, funcp)); + m_regFuncp->addStmtsp(new AstText(flp, ", vlSelf);\n", true)); + } else { + // Sub functions + funcp->argTypes(v3Global.opt.traceClassBase() + "* tracep"); + // Setup base references. Note in rare occasions we can end up with an empty trace + // sub function, hence the VL_ATTR_UNUSED attributes. + if (full) { + // Full dump sub function + addInitStr("vluint32_t* const oldp VL_ATTR_UNUSED = " + "tracep->oldp(vlSymsp->__Vm_baseCode);\n"); + } else { + // Change dump sub function + if (v3Global.opt.trueTraceThreads()) { + addInitStr("const vluint32_t base VL_ATTR_UNUSED = " + "vlSymsp->__Vm_baseCode + " + + cvtToStr(baseCode) + ";\n"); + addInitStr("if (false && tracep) {} // Prevent unused\n"); + } else { + addInitStr("vluint32_t* const oldp VL_ATTR_UNUSED = " + "tracep->oldp(vlSymsp->__Vm_baseCode + " + + cvtToStr(baseCode) + ");\n"); + } + } + // Add call to top function AstCCall* callp = new AstCCall(funcp->fileline(), funcp); callp->argTypes("tracep"); - callfromp->addStmtsp(callp); - } - // Register function - if (regp) { - if (type == AstCFuncType::TRACE_FULL) { - regp->addStmtsp(new AstText(flp, "tracep->addFullCb(", true)); - } else if (type == AstCFuncType::TRACE_CHANGE) { - regp->addStmtsp(new AstText(flp, "tracep->addChgCb(", true)); - } else { - funcp->v3fatalSrc("Don't know how to register this type of function"); - } - regp->addStmtsp(new AstAddrOfCFunc(flp, funcp)); - regp->addStmtsp(new AstText(flp, ", vlSelf);\n", true)); - } - // Add global activity check to TRACE_CHANGE functions - if (type == AstCFuncType::TRACE_CHANGE) { - funcp->addInitsp( - new AstCStmt(flp, string("if (VL_UNLIKELY(!vlSymsp->__Vm_activity)) return;\n"))); + topFuncp->addStmtsp(callp); } // Done UINFO(5, " newCFunc " << funcp << endl); return funcp; } - void createFullTraceFunction(const TraceVec& traces, uint32_t nAllCodes, uint32_t parallelism, - AstCFunc* regFuncp) { + void createFullTraceFunction(const TraceVec& traces, uint32_t nAllCodes, + uint32_t parallelism) { const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace() : std::numeric_limits::max(); @@ -571,20 +580,17 @@ private: ++m_statUniqSigs; // Create top function if not yet created - if (!topFuncp) { - topFuncp - = newCFunc(AstCFuncType::TRACE_FULL, nullptr, regFuncp, topFuncNum); - } + if (!topFuncp) { topFuncp = newCFunc(/* full: */ true, nullptr, topFuncNum); } // Crate new sub function if required if (!subFuncp || subStmts > splitLimit) { subStmts = 0; - subFuncp = newCFunc(AstCFuncType::TRACE_FULL_SUB, topFuncp, nullptr, - subFuncNum); + subFuncp = newCFunc(/* full: */ true, topFuncp, subFuncNum); } // Add TraceInc node - AstTraceInc* const incp = new AstTraceInc(declp->fileline(), declp, true); + AstTraceInc* const incp + = new AstTraceInc(declp->fileline(), declp, /* full: */ true); subFuncp->addStmtsp(incp); subStmts += EmitCBaseCounterVisitor(incp).count(); @@ -599,8 +605,8 @@ private: } } - void createChgTraceFunctions(const TraceVec& traces, uint32_t nAllCodes, uint32_t parallelism, - AstCFunc* regFuncp) { + void createChgTraceFunctions(const TraceVec& traces, uint32_t nAllCodes, + uint32_t parallelism) { const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace() : std::numeric_limits::max(); int topFuncNum = 0; @@ -615,6 +621,7 @@ private: uint32_t nCodes = 0; const ActCodeSet* prevActSet = nullptr; AstIf* ifp = nullptr; + uint32_t baseCode = 0; for (; nCodes < maxCodes && it != traces.end(); ++it) { const TraceTraceVertex* const vtxp = it->second; // This is a duplicate decl, no need to add it to incremental dump @@ -623,16 +630,16 @@ private: // Traced value never changes, no need to add it to incremental dump if (actSet.count(TraceActivityVertex::ACTIVITY_NEVER)) continue; - // Create top function if not yet created - if (!topFuncp) { - topFuncp = newCFunc(AstCFuncType::TRACE_CHANGE, nullptr, regFuncp, topFuncNum); - } + AstTraceDecl* const declp = vtxp->nodep(); - // Crate new sub function if required + // Create top function if not yet created + if (!topFuncp) { topFuncp = newCFunc(/* full: */ false, nullptr, topFuncNum); } + + // Create new sub function if required if (!subFuncp || subStmts > splitLimit) { + baseCode = declp->code(); subStmts = 0; - subFuncp - = newCFunc(AstCFuncType::TRACE_CHANGE_SUB, topFuncp, nullptr, subFuncNum); + subFuncp = newCFunc(/* full: */ false, topFuncp, subFuncNum, baseCode); prevActSet = nullptr; ifp = nullptr; } @@ -658,8 +665,8 @@ private: } // Add TraceInc node - AstTraceDecl* const declp = vtxp->nodep(); - AstTraceInc* const incp = new AstTraceInc(declp->fileline(), declp, VAccess::READ); + AstTraceInc* const incp + = new AstTraceInc(declp->fileline(), declp, /* full: */ false, baseCode); ifp->addIfsp(incp); subStmts += EmitCBaseCounterVisitor(incp).count(); @@ -673,25 +680,23 @@ private: } } - void createCleanupFunction(AstCFunc* regFuncp) { + void createCleanupFunction() { FileLine* const fl = m_topScopep->fileline(); AstCFunc* const cleanupFuncp = new AstCFunc(fl, "trace_cleanup", m_topScopep); cleanupFuncp->argTypes("void* voidSelf, " + v3Global.opt.traceClassBase() + "* /*unused*/"); - cleanupFuncp->funcType(AstCFuncType::TRACE_CLEANUP); + cleanupFuncp->isTrace(true); cleanupFuncp->slow(false); cleanupFuncp->isStatic(true); cleanupFuncp->isLoose(true); m_topScopep->addActivep(cleanupFuncp); - cleanupFuncp->addInitsp(new AstCStmt( - fl, prefixNameProtect(m_topModp) + "* const __restrict vlSelf = static_cast<" - + prefixNameProtect(m_topModp) + "*>(voidSelf);\n")); + cleanupFuncp->addInitsp(new AstCStmt(fl, voidSelfAssign(m_topModp))); cleanupFuncp->addInitsp(new AstCStmt(fl, symClassAssign())); // Register it - regFuncp->addStmtsp(new AstText(fl, "tracep->addCleanupCb(", true)); - regFuncp->addStmtsp(new AstAddrOfCFunc(fl, cleanupFuncp)); - regFuncp->addStmtsp(new AstText(fl, ", vlSelf);\n", true)); + m_regFuncp->addStmtsp(new AstText(fl, "tracep->addCleanupCb(", true)); + m_regFuncp->addStmtsp(new AstAddrOfCFunc(fl, cleanupFuncp)); + m_regFuncp->addStmtsp(new AstText(fl, ", vlSelf);\n", true)); // Clear global activity flag cleanupFuncp->addStmtsp( @@ -735,22 +740,21 @@ private: // last value vector is more compact // Create the trace registration function - AstCFunc* const regFuncp - = new AstCFunc(m_topScopep->fileline(), "trace_register", m_topScopep); - regFuncp->argTypes(v3Global.opt.traceClassBase() + "* tracep"); - regFuncp->funcType(AstCFuncType::TRACE_REGISTER); - regFuncp->slow(true); - regFuncp->isStatic(false); - regFuncp->isLoose(true); - m_topScopep->addActivep(regFuncp); + m_regFuncp = new AstCFunc(m_topScopep->fileline(), "trace_register", m_topScopep); + m_regFuncp->argTypes(v3Global.opt.traceClassBase() + "* tracep"); + m_regFuncp->isTrace(true); + m_regFuncp->slow(true); + m_regFuncp->isStatic(false); + m_regFuncp->isLoose(true); + m_topScopep->addActivep(m_regFuncp); const int parallelism = 1; // Note: will bump this later, code below works for any value // Create the full dump functions, also allocates signal numbers - createFullTraceFunction(traces, nFullCodes, parallelism, regFuncp); + createFullTraceFunction(traces, nFullCodes, parallelism); // Create the incremental dump functions - createChgTraceFunctions(traces, nChgCodes, parallelism, regFuncp); + createChgTraceFunctions(traces, nChgCodes, parallelism); // Remove refs to traced values from TraceDecl nodes, these have now moved under // TraceInc @@ -761,7 +765,7 @@ private: } // Create the trace cleanup function clearing the activity flags - createCleanupFunction(regFuncp); + createCleanupFunction(); } TraceCFuncVertex* getCFuncVertexp(AstCFunc* nodep) { diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index b7f61a01f..07a62629d 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -69,13 +69,13 @@ private: return nullptr; } - AstCFunc* newCFunc(AstCFuncType type, const string& name) { + AstCFunc* newCFunc(const string& name) { FileLine* const flp = m_topScopep->fileline(); AstCFunc* const funcp = new AstCFunc(flp, name, m_topScopep); string argTypes = v3Global.opt.traceClassBase() + "* tracep"; if (m_interface) argTypes += ", int scopet, const char* scopep"; funcp->argTypes(argTypes); - funcp->funcType(type); + funcp->isTrace(true); funcp->isStatic(false); funcp->isLoose(true); funcp->slow(true); @@ -94,8 +94,11 @@ private: basep->addStmtsp(callp); } AstCFunc* newCFuncSub(AstCFunc* basep) { + FileLine* const fl = basep->fileline(); const string name = "trace_init_sub_" + cvtToStr(m_funcNum++); - AstCFunc* const funcp = newCFunc(AstCFuncType::TRACE_INIT_SUB, name); + AstCFunc* const funcp = newCFunc(name); + funcp->addInitsp(new AstCStmt(fl, "const int c = vlSymsp->__Vm_baseCode;\n")); + funcp->addInitsp(new AstCStmt(fl, "if (false && tracep && c) {} // Prevent unused\n")); if (!m_interface) callCFuncSub(basep, funcp, nullptr); return funcp; } @@ -135,7 +138,7 @@ private: virtual void visit(AstTopScope* nodep) override { m_topScopep = nodep->scopep(); // Create the trace init function - m_initFuncp = newCFunc(AstCFuncType::TRACE_INIT, "trace_init_top"); + m_initFuncp = newCFunc("trace_init_top"); // Create initial sub function m_initSubFuncp = newCFuncSub(m_initFuncp); // And find variables diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 9a742cafd..842fa04a9 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -512,7 +512,6 @@ static void process() { V3EmitC::emitcSyms(); V3EmitC::emitcConstPool(); V3EmitC::emitcModel(); - V3EmitC::emitcTrace(); } else if (v3Global.opt.dpiHdrOnly()) { V3EmitC::emitcSyms(true); }