From 7280307a39bc9e8beebdeddf6920b9cc87bc18b1 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 10 Jun 2021 22:41:33 +0100 Subject: [PATCH] Implement DPI import/export as loose functions --- src/V3AstNodes.cpp | 7 +- src/V3AstNodes.h | 30 ++--- src/V3EmitC.cpp | 28 +++-- src/V3EmitCBase.h | 11 +- src/V3EmitCSyms.cpp | 38 +++--- src/V3Life.cpp | 2 +- src/V3Task.cpp | 194 +++++++++++++++++-------------- src/V3Trace.cpp | 3 +- test_regress/t/t_dpi_dup_bad.out | 6 +- 9 files changed, 177 insertions(+), 142 deletions(-) diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 22bfd5a85..4160df3e1 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1843,9 +1843,10 @@ void AstCFunc::dump(std::ostream& str) const { } else if (isStatic().trueUnknown()) { str << " [STATIC]"; } - if (dpiImport()) str << " [DPII]"; - if (dpiExport()) str << " [DPIX]"; - if (dpiExportWrapper()) str << " [DPIXWR]"; + if (dpiExportDispatcher()) str << " [DPIED]"; + if (dpiExportImpl()) str << " [DPIEI]"; + if (dpiImportPrototype()) str << " [DPIIP]"; + if (dpiImportWrapper()) str << " [DPIIW]"; if (isConstructor()) str << " [CTOR]"; if (isDestructor()) str << " [DTOR]"; if (isVirtual()) str << " [VIRT]"; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 2ee8a64db..67b424aae 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -8749,10 +8749,10 @@ private: bool m_isVirtual : 1; // Virtual function bool m_entryPoint : 1; // User may call into this top level function bool m_pure : 1; // Pure function - bool m_dpiExport : 1; // From dpi export - bool m_dpiExportWrapper : 1; // From dpi export; static function with dispatch table - bool m_dpiImport : 1; // From dpi import - bool m_dpiImportWrapper : 1; // Wrapper from dpi import + bool m_dpiExportDispatcher : 1; // This is the DPI export entry point (i.e.: called by user) + bool m_dpiExportImpl : 1; // DPI export implementation (called from DPI dispatcher via lookup) + bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user) + bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code public: AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "") : ASTGEN_SUPER_CFunc(fl) { @@ -8775,9 +8775,9 @@ public: m_isVirtual = false; m_entryPoint = false; m_pure = false; - m_dpiExport = false; - m_dpiExportWrapper = false; - m_dpiImport = false; + m_dpiExportDispatcher = false; + m_dpiExportImpl = false; + m_dpiImportPrototype = false; m_dpiImportWrapper = false; } ASTNODE_NODE_FUNCS(CFunc) @@ -8793,11 +8793,11 @@ public: return ((funcType() == asamep->funcType()) && (rtnTypeVoid() == asamep->rtnTypeVoid()) && (argTypes() == asamep->argTypes()) && (ctorInits() == asamep->ctorInits()) && isLoose() == asamep->isLoose() - && (!(dpiImport() || dpiExport()) || name() == asamep->name())); + && (!(dpiImportPrototype() || dpiExportImpl()) || name() == asamep->name())); } // virtual void name(const string& name) override { m_name = name; } - virtual int instrCount() const override { return dpiImport() ? instrCountDpi() : 0; } + virtual int instrCount() const override { return dpiImportPrototype() ? instrCountDpi() : 0; } VBoolOrUnknown isConst() const { return m_isConst; } void isConst(bool flag) { m_isConst.setTrueOrFalse(flag); } void isConst(VBoolOrUnknown flag) { m_isConst = flag; } @@ -8845,12 +8845,12 @@ public: void entryPoint(bool flag) { m_entryPoint = flag; } bool pure() const { return m_pure; } void pure(bool flag) { m_pure = flag; } - bool dpiExport() const { return m_dpiExport; } - void dpiExport(bool flag) { m_dpiExport = flag; } - bool dpiExportWrapper() const { return m_dpiExportWrapper; } - void dpiExportWrapper(bool flag) { m_dpiExportWrapper = flag; } - bool dpiImport() const { return m_dpiImport; } - void dpiImport(bool flag) { m_dpiImport = flag; } + bool dpiExportDispatcher() const { return m_dpiExportDispatcher; } + void dpiExportDispatcher(bool flag) { m_dpiExportDispatcher = flag; } + bool dpiExportImpl() const { return m_dpiExportImpl; } + void dpiExportImpl(bool flag) { m_dpiExportImpl = flag; } + bool dpiImportPrototype() const { return m_dpiImportPrototype; } + void dpiImportPrototype(bool flag) { m_dpiImportPrototype = flag; } bool dpiImportWrapper() const { return m_dpiImportWrapper; } void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; } // diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 7bfe41fe9..128ff512b 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -235,7 +235,7 @@ public: for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstCFunc* funcp = VN_CAST(nodep, CFunc)) { - if (funcp->dpiImport()) // DPI import functions are declared in __Dpi.h + if (funcp->dpiImportPrototype()) // DPI import prototypes are declared in __Dpi.h continue; if (funcp->isMethod() != inClassBody) // Only methods go inside class continue; @@ -388,6 +388,9 @@ public: iterate(ccallp->fromp()); putbs("->"); puts(funcp->nameProtect()); + } else if (funcp->dpiImportPrototype()) { + // Calling DPI import + puts(funcp->name()); } else if (funcp->isProperMethod() && funcp->isStatic().trueUnknown()) { // Call static method via the containing class AstNodeModule* modp = VN_CAST(funcp->user4p(), NodeModule); @@ -1395,10 +1398,15 @@ class EmitCLazyDecls final : public AstNVisitor { bool m_needsBlankLine = false; // Emit blank line if any declarations were emitted (cosmetic) void lazyDeclare(AstCFunc* funcp) { - if (funcp->user2SetOnce()) return; // Already declared - if (!funcp->isMethod() || !funcp->isLoose()) return; // Not lazily declared - if (m_emittedManually.count(funcp->nameProtect())) return; // Already declared manually - m_emitter.emitCFuncDecl(funcp, VN_CAST_CONST(funcp->user4p(), NodeModule)); + // Already declared in this compilation unit + if (funcp->user2SetOnce()) return; + // Check if this kind of function is lazily declared + if (!(funcp->isMethod() && funcp->isLoose()) && !funcp->dpiImportPrototype()) return; + // Already declared manually + if (m_emittedManually.count(funcp->nameProtect())) return; + // Needs lazy declaration, emit one + m_emitter.emitCFuncDecl(funcp, VN_CAST_CONST(funcp->user4p(), NodeModule), + funcp->dpiImportPrototype()); m_needsBlankLine = true; } @@ -1659,7 +1667,7 @@ class EmitCImp final : EmitCStmts { virtual void visit(AstCFunc* nodep) override { // TRACE_* and DPI handled elsewhere if (nodep->funcType().isTrace()) return; - if (nodep->dpiImport()) return; + if (nodep->dpiImportPrototype()) return; if (!(nodep->slow() ? m_slow : m_fast)) return; VL_RESTORER(m_useSelfForThis); @@ -2009,7 +2017,7 @@ class EmitCImp final : EmitCStmts { void emitWrapFast(); void emitMTaskState(); void emitMTaskVertexCtors(bool* firstp); - void emitIntTop(AstNodeModule* modp); + void emitIntTop(const AstNodeModule* modp); void emitInt(AstNodeModule* modp); void maybeSplit(); @@ -3274,7 +3282,7 @@ void EmitCImp::emitMTaskState() { puts("bool __Vm_even_cycle;\n"); } -void EmitCImp::emitIntTop(AstNodeModule*) { +void EmitCImp::emitIntTop(const AstNodeModule* modp) { // Always have this first; gcc has short circuiting if #ifdef is first in a file ofp()->putsGuard(); puts("\n"); @@ -3288,10 +3296,10 @@ void EmitCImp::emitIntTop(AstNodeModule*) { if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n"); if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n"); if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n"); - if (v3Global.dpi()) { + if (v3Global.dpi() && modp->isTop()) { // do this before including our main .h file so that any references to // types defined in svdpi.h are available - puts("#include \"" + topClassName() + "__Dpi.h\"\n"); + puts("#include \"svdpi.h\"\n"); } } diff --git a/src/V3EmitCBase.h b/src/V3EmitCBase.h index 4a82b6c81..b125e180c 100644 --- a/src/V3EmitCBase.h +++ b/src/V3EmitCBase.h @@ -58,8 +58,8 @@ public: static string symClassAssign() { return symClassName() + "* const __restrict vlSymsp VL_ATTR_UNUSED = vlSelf->vlSymsp;\n"; } - static string funcNameProtect(const AstCFunc* nodep) { - AstNodeModule* modp = VN_CAST(nodep->user4p(), NodeModule); + static string funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp = nullptr) { + modp = modp ? modp : VN_CAST(nodep->user4p(), NodeModule); string name; if (nodep->isConstructor()) { name += prefixNameProtect(modp); @@ -116,7 +116,7 @@ public: if (const AstVar* portp = VN_CAST_CONST(stmtp, Var)) { if (portp->isIO() && !portp->isFuncReturn()) { if (args != "") args += ", "; - if (nodep->dpiImport() || nodep->dpiExportWrapper()) { + if (nodep->dpiImportPrototype() || nodep->dpiExportDispatcher()) { args += portp->dpiArgType(true, false); } else if (nodep->funcPublic()) { args += portp->cPubArgType(true, false); @@ -135,14 +135,15 @@ public: puts(" "); } if (withScope && funcp->isProperMethod()) puts(prefixNameProtect(modp) + "::"); - puts(funcNameProtect(funcp)); + puts(funcNameProtect(funcp, modp)); puts("(" + cFuncArgs(funcp) + ")"); if (funcp->isConst().trueKnown() && funcp->isProperMethod()) puts(" const"); } - void emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp) { + void emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp, bool cLinkage = false) { ensureNewLine(); if (!funcp->ifdef().empty()) puts("#ifdef " + funcp->ifdef() + "\n"); + if (cLinkage) puts("extern \"C\" "); if (funcp->isStatic().trueUnknown() && funcp->isProperMethod()) puts("static "); if (funcp->isVirtual()) { UASSERT_OBJ(funcp->isProperMethod(), funcp, "Virtual function is not a proper method"); diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index a2f7142d1..f5d41845b 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -83,9 +83,9 @@ class EmitCSyms final : EmitCBaseVisitor { }; struct CmpDpi { bool operator()(const AstCFunc* lhsp, const AstCFunc* rhsp) const { - if (lhsp->dpiImport() != rhsp->dpiImport()) { + if (lhsp->dpiImportPrototype() != rhsp->dpiImportPrototype()) { // cppcheck-suppress comparisonOfFuncReturningBoolError - return lhsp->dpiImport() < rhsp->dpiImport(); + return lhsp->dpiImportPrototype() < rhsp->dpiImportPrototype(); } return lhsp->name() < rhsp->name(); } @@ -348,7 +348,7 @@ class EmitCSyms final : EmitCBaseVisitor { } virtual void visit(AstCFunc* nodep) override { nameCheck(nodep); - if (nodep->dpiImport() || nodep->dpiExportWrapper()) m_dpis.push_back(nodep); + if (nodep->dpiImportPrototype() || nodep->dpiExportDispatcher()) m_dpis.push_back(nodep); VL_RESTORER(m_cfuncp); { m_cfuncp = nodep; @@ -407,7 +407,7 @@ void EmitCSyms::emitSymHdr() { std::map types; // Remove duplicates and sort for (const auto& itr : m_scopeFuncs) { AstCFunc* funcp = itr.second.m_cfuncp; - if (funcp->dpiExport()) { + if (funcp->dpiExportImpl()) { string cbtype = protect(v3Global.opt.prefix() + "__Vcb_" + funcp->cname() + "_t"); types["using " + cbtype + " = void (*) (" + cFuncArgs(funcp) + ");\n"] = 1; } @@ -561,6 +561,16 @@ void EmitCSyms::emitSymImpPreamble() { if (VN_IS(nodep, Class)) continue; // Class included earlier puts("#include \"" + prefixNameProtect(nodep) + ".h\"\n"); } + puts("\n"); + // Declarations for DPI Export implementation functions + bool needsNewLine = false; + for (const auto& pair : m_scopeFuncs) { + const AstCFunc* const funcp = pair.second.m_cfuncp; + if (!funcp->dpiExportImpl()) continue; + emitCFuncDecl(funcp, pair.second.m_modp); + needsNewLine = true; + } + if (needsNewLine) puts("\n"); } void EmitCSyms::emitScopeHier(bool destroy) { @@ -611,12 +621,7 @@ void EmitCSyms::emitSymImp() { m_ofpBase = m_ofp; emitSymImpPreamble(); - // puts("\n// GLOBALS\n"); - - puts("\n"); - if (v3Global.opt.savable()) { - puts("\n"); for (int de = 0; de < 2; ++de) { string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; string funcname = de ? "__Vdeserialize" : "__Vserialize"; @@ -640,11 +645,10 @@ void EmitCSyms::emitSymImp() { } puts("}\n"); } + puts("\n"); } - puts("\n"); - - puts("\n// FUNCTIONS\n"); + puts("// FUNCTIONS\n"); puts(symClassName() + "::~" + symClassName() + "()\n"); puts("{\n"); emitScopeHier(true); @@ -743,13 +747,13 @@ void EmitCSyms::emitSymImp() { AstScopeName* scopep = it->second.m_scopep; AstCFunc* funcp = it->second.m_cfuncp; AstNodeModule* modp = it->second.m_modp; - if (funcp->dpiExport()) { + if (funcp->dpiExportImpl()) { checkSplit(true); puts(protect("__Vscope_" + scopep->scopeSymName()) + ".exportInsert(__Vfinal, "); putsQuoted(funcp->cname()); // Not protected - user asked for import/export puts(", (void*)(&"); puts(prefixNameProtect(modp)); - puts("::"); + puts("__"); puts(funcp->nameProtect()); puts("));\n"); ++m_numStmts; @@ -886,13 +890,13 @@ void EmitCSyms::emitDpiHdr() { int firstExp = 0; int firstImp = 0; for (AstCFunc* nodep : m_dpis) { - if (nodep->dpiExportWrapper()) { + if (nodep->dpiExportDispatcher()) { if (!firstExp++) puts("\n// DPI EXPORTS\n"); putsDecoration("// DPI export" + ifNoProtect(" at " + nodep->fileline()->ascii()) + "\n"); puts("extern " + nodep->rtnTypeVoid() + " " + nodep->nameProtect() + "(" + cFuncArgs(nodep) + ");\n"); - } else if (nodep->dpiImport()) { + } else if (nodep->dpiImportPrototype()) { if (!firstImp++) puts("\n// DPI IMPORTS\n"); putsDecoration("// DPI import" + ifNoProtect(" at " + nodep->fileline()->ascii()) + "\n"); @@ -937,7 +941,7 @@ void EmitCSyms::emitDpiImp() { puts("\n"); for (AstCFunc* nodep : m_dpis) { - if (nodep->dpiExportWrapper()) { + if (nodep->dpiExportDispatcher()) { // Prevent multi-definition if used by multiple models puts("#ifndef VL_DPIDECL_" + nodep->name() + "_\n"); puts("#define VL_DPIDECL_" + nodep->name() + "_\n"); diff --git a/src/V3Life.cpp b/src/V3Life.cpp index 2b7dc0541..24c6866a6 100644 --- a/src/V3Life.cpp +++ b/src/V3Life.cpp @@ -414,7 +414,7 @@ private: // UINFO(4, " CFUNC " << nodep << endl); if (!m_tracingCall && !nodep->entryPoint()) return; m_tracingCall = false; - if (nodep->dpiImport() && !nodep->pure()) { + if (nodep->dpiImportPrototype() && !nodep->pure()) { m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment } iterateChildren(nodep); diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 99dc0a04c..963a1bc7a 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -35,6 +35,7 @@ #include "V3LinkLValue.h" #include +#include //###################################################################### // Graph subclasses @@ -384,7 +385,7 @@ private: IM_AFTER, // Pointing at last inserted stmt, insert after IM_WHILE_PRECOND // Pointing to for loop, add to body end }; - using DpiNames = std::map>; + using DpiCFuncs = std::map>; // STATE TaskStateVisitor* m_statep; // Common state between visitors @@ -394,7 +395,7 @@ private: InsertMode m_insMode = IM_BEFORE; // How to insert AstNode* m_insStmtp = nullptr; // Where to insert statement int m_modNCalls = 0; // Incrementing func # for making symbols - DpiNames m_dpiNames; // Map of all created DPI functions + DpiCFuncs m_dpiNames; // Map of all created DPI functions // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -658,9 +659,9 @@ private: return beginp; } - string dpiprotoName(AstNodeFTask* nodep, AstVar* rtnvarp) const { - // Return fancy export-ish name for DPI function - // Variable names are NOT included so differences in only IO names won't matter + string dpiSignature(AstNodeFTask* nodep, AstVar* rtnvarp) const { + // Return fancy signature for DPI function. Variable names are not included so differences + // in only argument names will not matter (as required by the standard). string dpiproto; if (nodep->pure()) dpiproto += "pure "; if (nodep->dpiContext()) dpiproto += "context "; @@ -757,18 +758,18 @@ private: return newp; } - void makeDpiExportWrapper(AstNodeFTask* nodep, AstVar* rtnvarp) { + AstCFunc* makeDpiExportDispatcher(AstNodeFTask* nodep, AstVar* rtnvarp) { const char* const tmpSuffixp = V3Task::dpiTemporaryVarSuffix(); - AstCFunc* dpip = new AstCFunc(nodep->fileline(), nodep->cname(), m_scopep, - (rtnvarp ? rtnvarp->dpiArgType(true, true) : "")); - dpip->dontCombine(true); - dpip->entryPoint(true); - dpip->isStatic(true); - dpip->dpiExportWrapper(true); - dpip->protect(false); - dpip->cname(nodep->cname()); - // Add DPI reference to top, since it's a global function - m_topScopep->scopep()->addActivep(dpip); + AstCFunc* const funcp = new AstCFunc(nodep->fileline(), nodep->cname(), m_scopep, + (rtnvarp ? rtnvarp->dpiArgType(true, true) : "")); + funcp->dpiExportDispatcher(true); + funcp->dontCombine(true); + funcp->entryPoint(true); + funcp->isStatic(true); + funcp->protect(false); + funcp->cname(nodep->cname()); + // Add DPI Export to top, since it's a global function + m_topScopep->scopep()->addActivep(funcp); { // Create dispatch wrapper // Note this function may dispatch to myfunc on a different class. @@ -795,7 +796,7 @@ private: + ")(VerilatedScope::exportFind(__Vscopep, __Vfuncnum));\n"; // Can't use // static_cast // If __Vcb is null the exportFind function throws and error - dpip->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); + funcp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); } // Convert input/inout DPI arguments to Internal types @@ -813,7 +814,7 @@ private: argnodesp = argnodesp->addNext(new AstText(portp->fileline(), args, true)); args = ""; } - AstVarScope* outvscp = createFuncVar(dpip, portp->name() + tmpSuffixp, portp); + AstVarScope* outvscp = createFuncVar(funcp, portp->name() + tmpSuffixp, portp); // No information exposure; is already visible in import/export func template outvscp->varp()->protect(false); portp->protect(false); @@ -829,7 +830,7 @@ private: ? "*" : ""; frName += portp->name(); - dpip->addStmtsp(createAssignDpiToInternal(outvscp, frName)); + funcp->addStmtsp(createAssignDpiToInternal(outvscp, frName)); } } } @@ -843,7 +844,7 @@ private: argnodesp = argnodesp->addNext(new AstText(portp->fileline(), args, true)); args = ""; } - AstVarScope* outvscp = createFuncVar(dpip, portp->name() + tmpSuffixp, portp); + AstVarScope* outvscp = createFuncVar(funcp, portp->name() + tmpSuffixp, portp); // No information exposure; is already visible in import/export func template outvscp->varp()->protect(false); AstVarRef* refp = new AstVarRef(portp->fileline(), outvscp, @@ -859,69 +860,85 @@ private: newp->addBodysp(argnodesp); VL_DANGLING(argnodesp); newp->addBodysp(new AstText(nodep->fileline(), args, true)); - dpip->addStmtsp(newp); + funcp->addStmtsp(newp); } // Convert output/inout arguments back to internal type for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { if (AstVar* portp = VN_CAST(stmtp, Var)) { if (portp->isIO() && portp->isWritable() && !portp->isFuncReturn()) { - dpip->addStmtsp(createAssignInternalToDpi(portp, true, tmpSuffixp, "")); + funcp->addStmtsp(createAssignInternalToDpi(portp, true, tmpSuffixp, "")); } } } if (rtnvarp) { - dpip->addStmtsp(createDpiTemp(rtnvarp, "")); - dpip->addStmtsp(createAssignInternalToDpi(rtnvarp, false, tmpSuffixp, "")); + funcp->addStmtsp(createDpiTemp(rtnvarp, "")); + funcp->addStmtsp(createAssignInternalToDpi(rtnvarp, false, tmpSuffixp, "")); string stmt = "return " + rtnvarp->name(); stmt += rtnvarp->basicp()->isDpiPrimitive() ? ";\n" : "[0];\n"; - dpip->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); + funcp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); } - makePortList(nodep, dpip); + makePortList(nodep, funcp); + return funcp; } - void makeDpiImportProto(AstNodeFTask* nodep, AstVar* rtnvarp) { + AstCFunc* makeDpiImportPrototype(AstNodeFTask* nodep, AstVar* rtnvarp) { if (nodep->cname() != AstNode::prettyName(nodep->cname())) { nodep->v3error("DPI function has illegal characters in C identifier name: " << AstNode::prettyNameQ(nodep->cname())); } - AstCFunc* dpip = new AstCFunc(nodep->fileline(), nodep->cname(), m_scopep, - (rtnvarp ? rtnvarp->dpiArgType(true, true) - // Tasks (but not void functions) - // return bool indicating disabled - : nodep->dpiTask() ? "int" : "")); - dpip->dontCombine(true); - dpip->entryPoint(false); - dpip->funcPublic(true); - dpip->isStatic(false); - dpip->protect(false); - dpip->pure(nodep->pure()); - dpip->dpiImport(true); - // Add DPI reference to top, since it's a global function - m_topScopep->scopep()->addActivep(dpip); - makePortList(nodep, dpip); + // Tasks (but not void functions) return a boolean 'int' indicating disabled + const string rtnType + = rtnvarp ? rtnvarp->dpiArgType(true, true) : nodep->dpiTask() ? "int" : ""; + AstCFunc* const funcp = new AstCFunc(nodep->fileline(), nodep->cname(), m_scopep, rtnType); + funcp->dpiImportPrototype(true); + funcp->dontCombine(true); + funcp->entryPoint(false); + funcp->isMethod(false); + funcp->protect(false); + funcp->pure(nodep->pure()); + // Add DPI Import to top, since it's a global function + m_topScopep->scopep()->addActivep(funcp); + makePortList(nodep, funcp); + return funcp; } - bool duplicatedDpiProto(AstNodeFTask* nodep, const string& dpiproto) { - // Only create one DPI extern prototype for each specified cname - // as it's legal for the user to attach multiple tasks to one dpi cname - const auto iter = m_dpiNames.find(nodep->cname()); - if (iter == m_dpiNames.end()) { - m_dpiNames.emplace(nodep->cname(), std::make_pair(nodep, dpiproto)); - return false; - } else if (iter->second.second != dpiproto) { - nodep->v3error( - "Duplicate declaration of DPI function with different formal arguments: " - << nodep->prettyNameQ() << '\n' - << nodep->warnContextPrimary() << '\n' - << nodep->warnMore() << "... New prototype: " << dpiproto << '\n' - << iter->second.first->warnOther() - << "... Original prototype: " << iter->second.second << '\n' - << iter->second.first->warnContextSecondary()); - return true; + AstCFunc* getDpiFunc(AstNodeFTask* nodep, AstVar* rtnvarp) { + UASSERT_OBJ(nodep->dpiImport() || nodep->dpiExport(), nodep, "Not a DPI function"); + // Compute unique signature of this DPI function + const string signature = dpiSignature(nodep, rtnvarp); + // Only create one DPI Import prototype or DPI Export entry point for each unique cname as + // it is illegal for the user to attach multiple tasks with different signatures to one DPI + // cname. + const auto it = m_dpiNames.find(nodep->cname()); + if (it == m_dpiNames.end()) { + // First time encountering this cname. Create Import prototype / Export entry point + AstCFunc* const funcp = nodep->dpiExport() ? makeDpiExportDispatcher(nodep, rtnvarp) + : makeDpiImportPrototype(nodep, rtnvarp); + m_dpiNames.emplace(nodep->cname(), std::make_tuple(nodep, signature, funcp)); + return funcp; } else { - return true; + // Seen this cname before. Check if it's the same prototype. + const AstNodeFTask* firstNodep; + string firstSignature; + AstCFunc* firstFuncp; + std::tie(firstNodep, firstSignature, firstFuncp) = it->second; + if (signature != firstSignature) { + // Different signature, so error. + nodep->v3error("Duplicate declaration of DPI function with different signature: " + << nodep->prettyNameQ() << '\n' + << nodep->warnContextPrimary() << '\n' + << nodep->warnMore() // + << "... New signature: " << signature << '\n' // + << firstNodep->warnOther() + << "... Original signature: " << firstSignature << '\n' // + << firstNodep->warnContextSecondary()); + return nullptr; + } else { + // Same signature, return the previously created CFunc + return firstFuncp; + } } } @@ -949,7 +966,8 @@ private: } } - void bodyDpiImportFunc(AstNodeFTask* nodep, AstVarScope* rtnvscp, AstCFunc* cfuncp) { + void bodyDpiImportFunc(AstNodeFTask* nodep, AstVarScope* rtnvscp, AstCFunc* cfuncp, + AstCFunc* dpiFuncp) { const char* const tmpSuffixp = V3Task::dpiTemporaryVarSuffix(); // Convert input/inout arguments to DPI types string args; @@ -1010,15 +1028,17 @@ private: cfuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); } - { // Call the user function - string stmt; + { // Call the imported function if (rtnvscp) { // isFunction will no longer work as we unlinked the return var cfuncp->addStmtsp(createDpiTemp(rtnvscp->varp(), tmpSuffixp)); - stmt = rtnvscp->varp()->name() + tmpSuffixp; + string stmt = rtnvscp->varp()->name(); + stmt += tmpSuffixp; stmt += rtnvscp->varp()->basicp()->isDpiPrimitive() ? " = " : "[0] = "; + cfuncp->addStmtsp(new AstText(nodep->fileline(), stmt, /* tracking: */ true)); } - stmt += nodep->cname() + "(" + args + ");\n"; - cfuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); + AstCCall* const callp = new AstCCall(nodep->fileline(), dpiFuncp); + callp->argTypes(args); + cfuncp->addStmtsp(callp); } // Convert output/inout arguments back to internal type @@ -1080,23 +1100,19 @@ private: if (nodep->dpiImport() || nodep->dpiExport()) rtnvarp->protect(false); } - if (nodep->dpiImport()) { - if (nodep->dpiOpenChild()) { // The parent will make the dpi proto - UASSERT_OBJ(!nodep->dpiOpenParent(), nodep, - "DPI task should be parent or wrapper, not both"); - } else { // Parent or not open child, make wrapper - string dpiproto = dpiprotoName(nodep, rtnvarp); - if (!duplicatedDpiProto(nodep, dpiproto)) makeDpiImportProto(nodep, rtnvarp); - if (nodep->dpiOpenParent()) { - // No need to make more than just the c prototype, children will - VL_DO_DANGLING(pushDeletep(nodep), nodep); - return nullptr; - } + // Create/pick up the DPI CFunc for DPI Import/ DPI Export. + AstCFunc* dpiFuncp = nullptr; + if (nodep->dpiImport() || nodep->dpiExport()) { + UASSERT_OBJ(!(nodep->dpiOpenParent() && nodep->dpiOpenChild()), nodep, + "DPI task should not be both parent and child"); + dpiFuncp = getDpiFunc(nodep, rtnvarp); + if (!dpiFuncp) return nullptr; // There was an error, so bail + if (nodep->dpiImport() && nodep->dpiOpenParent()) { + // No need to make more than just the DPI Import prototype, the children will + // create the wrapper implementations. + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return nullptr; } - - } else if (nodep->dpiExport()) { - string dpiproto = dpiprotoName(nodep, rtnvarp); - if (!duplicatedDpiProto(nodep, dpiproto)) makeDpiExportWrapper(nodep, rtnvarp); } AstVarScope* rtnvscp = nullptr; @@ -1127,9 +1143,14 @@ private: cfuncp->dontCombine(!nodep->dpiImport()); cfuncp->entryPoint(!nodep->dpiImport()); cfuncp->funcPublic(nodep->taskPublic()); - cfuncp->dpiExport(nodep->dpiExport()); + cfuncp->dpiExportImpl(nodep->dpiExport()); cfuncp->dpiImportWrapper(nodep->dpiImport()); - cfuncp->isStatic(nodep->dpiExport()); + if (nodep->dpiImport() || nodep->dpiExport()) { + cfuncp->isStatic(true); + cfuncp->isLoose(true); + } else { + cfuncp->isStatic(false); + } cfuncp->isVirtual(nodep->isVirtual()); cfuncp->pure(nodep->pure()); if (nodep->name() == "new") { @@ -1140,8 +1161,7 @@ private: + "(vlSymsp)"); } } - // cfuncp->dpiImport // Not set in the wrapper - the called function has it set - if (cfuncp->dpiExport()) cfuncp->cname(nodep->cname()); + if (cfuncp->dpiExportImpl()) cfuncp->cname(nodep->cname()); if (!nodep->dpiImport() && !nodep->taskPublic()) { // Need symbol table @@ -1198,7 +1218,7 @@ private: bodysp->unlinkFrBackWithNext(); cfuncp->addStmtsp(bodysp); } - if (nodep->dpiImport()) bodyDpiImportFunc(nodep, rtnvscp, cfuncp); + if (nodep->dpiImport()) bodyDpiImportFunc(nodep, rtnvscp, cfuncp, dpiFuncp); // Return statement if (rtnvscp && nodep->taskPublic()) { diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index 284add392..0982fbf79 100644 --- a/src/V3Trace.cpp +++ b/src/V3Trace.cpp @@ -834,7 +834,8 @@ private: V3GraphVertex* const funcVtxp = getCFuncVertexp(nodep); if (!m_finding) { // If public, we need a unique activity code to allow for sets // directly in this func - if (nodep->funcPublic() || nodep->dpiExport() || nodep == v3Global.rootp()->evalp()) { + if (nodep->funcPublic() || nodep->dpiExportImpl() + || nodep == v3Global.rootp()->evalp()) { V3GraphVertex* const activityVtxp = getActivityVertexp(nodep, nodep->slow()); new V3GraphEdge(&m_graph, activityVtxp, funcVtxp, 1); } diff --git a/test_regress/t/t_dpi_dup_bad.out b/test_regress/t/t_dpi_dup_bad.out index 88cc02fe1..0450a8504 100644 --- a/test_regress/t/t_dpi_dup_bad.out +++ b/test_regress/t/t_dpi_dup_bad.out @@ -1,8 +1,8 @@ -%Error: t/t_dpi_dup_bad.v:13:51: Duplicate declaration of DPI function with different formal arguments: 't.oth_f_int2' +%Error: t/t_dpi_dup_bad.v:13:51: Duplicate declaration of DPI function with different signature: 't.oth_f_int2' 13 | import "DPI-C" pure dpii_fa_bit = function int oth_f_int2(input int i, input int bad); | ^~~~~~~~~~ - : ... New prototype: pure int dpii_fa_bit (int, int) - t/t_dpi_dup_bad.v:12:47: ... Original prototype: int dpii_fa_bit (int) + : ... New signature: pure int dpii_fa_bit (int, int) + t/t_dpi_dup_bad.v:12:47: ... Original signature: int dpii_fa_bit (int) 12 | import "DPI-C" dpii_fa_bit = function int oth_f_int1(input int i); | ^~~~~~~~~~ %Error: Exiting due to