Implement DPI import/export as loose functions

This commit is contained in:
Geza Lore 2021-06-10 22:41:33 +01:00
parent c207e98306
commit 7280307a39
9 changed files with 177 additions and 142 deletions

View File

@ -1843,9 +1843,10 @@ void AstCFunc::dump(std::ostream& str) const {
} else if (isStatic().trueUnknown()) { } else if (isStatic().trueUnknown()) {
str << " [STATIC]"; str << " [STATIC]";
} }
if (dpiImport()) str << " [DPII]"; if (dpiExportDispatcher()) str << " [DPIED]";
if (dpiExport()) str << " [DPIX]"; if (dpiExportImpl()) str << " [DPIEI]";
if (dpiExportWrapper()) str << " [DPIXWR]"; if (dpiImportPrototype()) str << " [DPIIP]";
if (dpiImportWrapper()) str << " [DPIIW]";
if (isConstructor()) str << " [CTOR]"; if (isConstructor()) str << " [CTOR]";
if (isDestructor()) str << " [DTOR]"; if (isDestructor()) str << " [DTOR]";
if (isVirtual()) str << " [VIRT]"; if (isVirtual()) str << " [VIRT]";

View File

@ -8749,10 +8749,10 @@ private:
bool m_isVirtual : 1; // Virtual function bool m_isVirtual : 1; // Virtual function
bool m_entryPoint : 1; // User may call into this top level function bool m_entryPoint : 1; // User may call into this top level function
bool m_pure : 1; // Pure function bool m_pure : 1; // Pure function
bool m_dpiExport : 1; // From dpi export bool m_dpiExportDispatcher : 1; // This is the DPI export entry point (i.e.: called by user)
bool m_dpiExportWrapper : 1; // From dpi export; static function with dispatch table bool m_dpiExportImpl : 1; // DPI export implementation (called from DPI dispatcher via lookup)
bool m_dpiImport : 1; // From dpi import bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user)
bool m_dpiImportWrapper : 1; // Wrapper from dpi import bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
public: public:
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "") AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "")
: ASTGEN_SUPER_CFunc(fl) { : ASTGEN_SUPER_CFunc(fl) {
@ -8775,9 +8775,9 @@ public:
m_isVirtual = false; m_isVirtual = false;
m_entryPoint = false; m_entryPoint = false;
m_pure = false; m_pure = false;
m_dpiExport = false; m_dpiExportDispatcher = false;
m_dpiExportWrapper = false; m_dpiExportImpl = false;
m_dpiImport = false; m_dpiImportPrototype = false;
m_dpiImportWrapper = false; m_dpiImportWrapper = false;
} }
ASTNODE_NODE_FUNCS(CFunc) ASTNODE_NODE_FUNCS(CFunc)
@ -8793,11 +8793,11 @@ public:
return ((funcType() == asamep->funcType()) && (rtnTypeVoid() == asamep->rtnTypeVoid()) return ((funcType() == asamep->funcType()) && (rtnTypeVoid() == asamep->rtnTypeVoid())
&& (argTypes() == asamep->argTypes()) && (ctorInits() == asamep->ctorInits()) && (argTypes() == asamep->argTypes()) && (ctorInits() == asamep->ctorInits())
&& isLoose() == asamep->isLoose() && isLoose() == asamep->isLoose()
&& (!(dpiImport() || dpiExport()) || name() == asamep->name())); && (!(dpiImportPrototype() || dpiExportImpl()) || name() == asamep->name()));
} }
// //
virtual void name(const string& name) override { m_name = 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; } VBoolOrUnknown isConst() const { return m_isConst; }
void isConst(bool flag) { m_isConst.setTrueOrFalse(flag); } void isConst(bool flag) { m_isConst.setTrueOrFalse(flag); }
void isConst(VBoolOrUnknown flag) { m_isConst = flag; } void isConst(VBoolOrUnknown flag) { m_isConst = flag; }
@ -8845,12 +8845,12 @@ public:
void entryPoint(bool flag) { m_entryPoint = flag; } void entryPoint(bool flag) { m_entryPoint = flag; }
bool pure() const { return m_pure; } bool pure() const { return m_pure; }
void pure(bool flag) { m_pure = flag; } void pure(bool flag) { m_pure = flag; }
bool dpiExport() const { return m_dpiExport; } bool dpiExportDispatcher() const { return m_dpiExportDispatcher; }
void dpiExport(bool flag) { m_dpiExport = flag; } void dpiExportDispatcher(bool flag) { m_dpiExportDispatcher = flag; }
bool dpiExportWrapper() const { return m_dpiExportWrapper; } bool dpiExportImpl() const { return m_dpiExportImpl; }
void dpiExportWrapper(bool flag) { m_dpiExportWrapper = flag; } void dpiExportImpl(bool flag) { m_dpiExportImpl = flag; }
bool dpiImport() const { return m_dpiImport; } bool dpiImportPrototype() const { return m_dpiImportPrototype; }
void dpiImport(bool flag) { m_dpiImport = flag; } void dpiImportPrototype(bool flag) { m_dpiImportPrototype = flag; }
bool dpiImportWrapper() const { return m_dpiImportWrapper; } bool dpiImportWrapper() const { return m_dpiImportWrapper; }
void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; } void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; }
// //

View File

@ -235,7 +235,7 @@ public:
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstCFunc* funcp = VN_CAST(nodep, CFunc)) { 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; continue;
if (funcp->isMethod() != inClassBody) // Only methods go inside class if (funcp->isMethod() != inClassBody) // Only methods go inside class
continue; continue;
@ -388,6 +388,9 @@ public:
iterate(ccallp->fromp()); iterate(ccallp->fromp());
putbs("->"); putbs("->");
puts(funcp->nameProtect()); puts(funcp->nameProtect());
} else if (funcp->dpiImportPrototype()) {
// Calling DPI import
puts(funcp->name());
} else if (funcp->isProperMethod() && funcp->isStatic().trueUnknown()) { } else if (funcp->isProperMethod() && funcp->isStatic().trueUnknown()) {
// Call static method via the containing class // Call static method via the containing class
AstNodeModule* modp = VN_CAST(funcp->user4p(), NodeModule); 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) bool m_needsBlankLine = false; // Emit blank line if any declarations were emitted (cosmetic)
void lazyDeclare(AstCFunc* funcp) { void lazyDeclare(AstCFunc* funcp) {
if (funcp->user2SetOnce()) return; // Already declared // Already declared in this compilation unit
if (!funcp->isMethod() || !funcp->isLoose()) return; // Not lazily declared if (funcp->user2SetOnce()) return;
if (m_emittedManually.count(funcp->nameProtect())) return; // Already declared manually // Check if this kind of function is lazily declared
m_emitter.emitCFuncDecl(funcp, VN_CAST_CONST(funcp->user4p(), NodeModule)); 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; m_needsBlankLine = true;
} }
@ -1659,7 +1667,7 @@ class EmitCImp final : EmitCStmts {
virtual void visit(AstCFunc* nodep) override { virtual void visit(AstCFunc* nodep) override {
// TRACE_* and DPI handled elsewhere // TRACE_* and DPI handled elsewhere
if (nodep->funcType().isTrace()) return; if (nodep->funcType().isTrace()) return;
if (nodep->dpiImport()) return; if (nodep->dpiImportPrototype()) return;
if (!(nodep->slow() ? m_slow : m_fast)) return; if (!(nodep->slow() ? m_slow : m_fast)) return;
VL_RESTORER(m_useSelfForThis); VL_RESTORER(m_useSelfForThis);
@ -2009,7 +2017,7 @@ class EmitCImp final : EmitCStmts {
void emitWrapFast(); void emitWrapFast();
void emitMTaskState(); void emitMTaskState();
void emitMTaskVertexCtors(bool* firstp); void emitMTaskVertexCtors(bool* firstp);
void emitIntTop(AstNodeModule* modp); void emitIntTop(const AstNodeModule* modp);
void emitInt(AstNodeModule* modp); void emitInt(AstNodeModule* modp);
void maybeSplit(); void maybeSplit();
@ -3274,7 +3282,7 @@ void EmitCImp::emitMTaskState() {
puts("bool __Vm_even_cycle;\n"); 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 // Always have this first; gcc has short circuiting if #ifdef is first in a file
ofp()->putsGuard(); ofp()->putsGuard();
puts("\n"); puts("\n");
@ -3288,10 +3296,10 @@ void EmitCImp::emitIntTop(AstNodeModule*) {
if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n"); if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n");
if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n"); if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n");
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.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 // do this before including our main .h file so that any references to
// types defined in svdpi.h are available // types defined in svdpi.h are available
puts("#include \"" + topClassName() + "__Dpi.h\"\n"); puts("#include \"svdpi.h\"\n");
} }
} }

View File

@ -58,8 +58,8 @@ public:
static string symClassAssign() { static string symClassAssign() {
return symClassName() + "* const __restrict vlSymsp VL_ATTR_UNUSED = vlSelf->vlSymsp;\n"; return symClassName() + "* const __restrict vlSymsp VL_ATTR_UNUSED = vlSelf->vlSymsp;\n";
} }
static string funcNameProtect(const AstCFunc* nodep) { static string funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp = nullptr) {
AstNodeModule* modp = VN_CAST(nodep->user4p(), NodeModule); modp = modp ? modp : VN_CAST(nodep->user4p(), NodeModule);
string name; string name;
if (nodep->isConstructor()) { if (nodep->isConstructor()) {
name += prefixNameProtect(modp); name += prefixNameProtect(modp);
@ -116,7 +116,7 @@ public:
if (const AstVar* portp = VN_CAST_CONST(stmtp, Var)) { if (const AstVar* portp = VN_CAST_CONST(stmtp, Var)) {
if (portp->isIO() && !portp->isFuncReturn()) { if (portp->isIO() && !portp->isFuncReturn()) {
if (args != "") args += ", "; if (args != "") args += ", ";
if (nodep->dpiImport() || nodep->dpiExportWrapper()) { if (nodep->dpiImportPrototype() || nodep->dpiExportDispatcher()) {
args += portp->dpiArgType(true, false); args += portp->dpiArgType(true, false);
} else if (nodep->funcPublic()) { } else if (nodep->funcPublic()) {
args += portp->cPubArgType(true, false); args += portp->cPubArgType(true, false);
@ -135,14 +135,15 @@ public:
puts(" "); puts(" ");
} }
if (withScope && funcp->isProperMethod()) puts(prefixNameProtect(modp) + "::"); if (withScope && funcp->isProperMethod()) puts(prefixNameProtect(modp) + "::");
puts(funcNameProtect(funcp)); puts(funcNameProtect(funcp, modp));
puts("(" + cFuncArgs(funcp) + ")"); puts("(" + cFuncArgs(funcp) + ")");
if (funcp->isConst().trueKnown() && funcp->isProperMethod()) puts(" const"); 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(); ensureNewLine();
if (!funcp->ifdef().empty()) puts("#ifdef " + funcp->ifdef() + "\n"); if (!funcp->ifdef().empty()) puts("#ifdef " + funcp->ifdef() + "\n");
if (cLinkage) puts("extern \"C\" ");
if (funcp->isStatic().trueUnknown() && funcp->isProperMethod()) puts("static "); if (funcp->isStatic().trueUnknown() && funcp->isProperMethod()) puts("static ");
if (funcp->isVirtual()) { if (funcp->isVirtual()) {
UASSERT_OBJ(funcp->isProperMethod(), funcp, "Virtual function is not a proper method"); UASSERT_OBJ(funcp->isProperMethod(), funcp, "Virtual function is not a proper method");

View File

@ -83,9 +83,9 @@ class EmitCSyms final : EmitCBaseVisitor {
}; };
struct CmpDpi { struct CmpDpi {
bool operator()(const AstCFunc* lhsp, const AstCFunc* rhsp) const { bool operator()(const AstCFunc* lhsp, const AstCFunc* rhsp) const {
if (lhsp->dpiImport() != rhsp->dpiImport()) { if (lhsp->dpiImportPrototype() != rhsp->dpiImportPrototype()) {
// cppcheck-suppress comparisonOfFuncReturningBoolError // cppcheck-suppress comparisonOfFuncReturningBoolError
return lhsp->dpiImport() < rhsp->dpiImport(); return lhsp->dpiImportPrototype() < rhsp->dpiImportPrototype();
} }
return lhsp->name() < rhsp->name(); return lhsp->name() < rhsp->name();
} }
@ -348,7 +348,7 @@ class EmitCSyms final : EmitCBaseVisitor {
} }
virtual void visit(AstCFunc* nodep) override { virtual void visit(AstCFunc* nodep) override {
nameCheck(nodep); 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); VL_RESTORER(m_cfuncp);
{ {
m_cfuncp = nodep; m_cfuncp = nodep;
@ -407,7 +407,7 @@ void EmitCSyms::emitSymHdr() {
std::map<const string, int> types; // Remove duplicates and sort std::map<const string, int> types; // Remove duplicates and sort
for (const auto& itr : m_scopeFuncs) { for (const auto& itr : m_scopeFuncs) {
AstCFunc* funcp = itr.second.m_cfuncp; AstCFunc* funcp = itr.second.m_cfuncp;
if (funcp->dpiExport()) { if (funcp->dpiExportImpl()) {
string cbtype = protect(v3Global.opt.prefix() + "__Vcb_" + funcp->cname() + "_t"); string cbtype = protect(v3Global.opt.prefix() + "__Vcb_" + funcp->cname() + "_t");
types["using " + cbtype + " = void (*) (" + cFuncArgs(funcp) + ");\n"] = 1; types["using " + cbtype + " = void (*) (" + cFuncArgs(funcp) + ");\n"] = 1;
} }
@ -561,6 +561,16 @@ void EmitCSyms::emitSymImpPreamble() {
if (VN_IS(nodep, Class)) continue; // Class included earlier if (VN_IS(nodep, Class)) continue; // Class included earlier
puts("#include \"" + prefixNameProtect(nodep) + ".h\"\n"); 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) { void EmitCSyms::emitScopeHier(bool destroy) {
@ -611,12 +621,7 @@ void EmitCSyms::emitSymImp() {
m_ofpBase = m_ofp; m_ofpBase = m_ofp;
emitSymImpPreamble(); emitSymImpPreamble();
// puts("\n// GLOBALS\n");
puts("\n");
if (v3Global.opt.savable()) { if (v3Global.opt.savable()) {
puts("\n");
for (int de = 0; de < 2; ++de) { for (int de = 0; de < 2; ++de) {
string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize";
string funcname = de ? "__Vdeserialize" : "__Vserialize"; string funcname = de ? "__Vdeserialize" : "__Vserialize";
@ -640,11 +645,10 @@ void EmitCSyms::emitSymImp() {
} }
puts("}\n"); puts("}\n");
} }
puts("\n");
} }
puts("\n"); puts("// FUNCTIONS\n");
puts("\n// FUNCTIONS\n");
puts(symClassName() + "::~" + symClassName() + "()\n"); puts(symClassName() + "::~" + symClassName() + "()\n");
puts("{\n"); puts("{\n");
emitScopeHier(true); emitScopeHier(true);
@ -743,13 +747,13 @@ void EmitCSyms::emitSymImp() {
AstScopeName* scopep = it->second.m_scopep; AstScopeName* scopep = it->second.m_scopep;
AstCFunc* funcp = it->second.m_cfuncp; AstCFunc* funcp = it->second.m_cfuncp;
AstNodeModule* modp = it->second.m_modp; AstNodeModule* modp = it->second.m_modp;
if (funcp->dpiExport()) { if (funcp->dpiExportImpl()) {
checkSplit(true); checkSplit(true);
puts(protect("__Vscope_" + scopep->scopeSymName()) + ".exportInsert(__Vfinal, "); puts(protect("__Vscope_" + scopep->scopeSymName()) + ".exportInsert(__Vfinal, ");
putsQuoted(funcp->cname()); // Not protected - user asked for import/export putsQuoted(funcp->cname()); // Not protected - user asked for import/export
puts(", (void*)(&"); puts(", (void*)(&");
puts(prefixNameProtect(modp)); puts(prefixNameProtect(modp));
puts("::"); puts("__");
puts(funcp->nameProtect()); puts(funcp->nameProtect());
puts("));\n"); puts("));\n");
++m_numStmts; ++m_numStmts;
@ -886,13 +890,13 @@ void EmitCSyms::emitDpiHdr() {
int firstExp = 0; int firstExp = 0;
int firstImp = 0; int firstImp = 0;
for (AstCFunc* nodep : m_dpis) { for (AstCFunc* nodep : m_dpis) {
if (nodep->dpiExportWrapper()) { if (nodep->dpiExportDispatcher()) {
if (!firstExp++) puts("\n// DPI EXPORTS\n"); if (!firstExp++) puts("\n// DPI EXPORTS\n");
putsDecoration("// DPI export" + ifNoProtect(" at " + nodep->fileline()->ascii()) putsDecoration("// DPI export" + ifNoProtect(" at " + nodep->fileline()->ascii())
+ "\n"); + "\n");
puts("extern " + nodep->rtnTypeVoid() + " " + nodep->nameProtect() + "(" puts("extern " + nodep->rtnTypeVoid() + " " + nodep->nameProtect() + "("
+ cFuncArgs(nodep) + ");\n"); + cFuncArgs(nodep) + ");\n");
} else if (nodep->dpiImport()) { } else if (nodep->dpiImportPrototype()) {
if (!firstImp++) puts("\n// DPI IMPORTS\n"); if (!firstImp++) puts("\n// DPI IMPORTS\n");
putsDecoration("// DPI import" + ifNoProtect(" at " + nodep->fileline()->ascii()) putsDecoration("// DPI import" + ifNoProtect(" at " + nodep->fileline()->ascii())
+ "\n"); + "\n");
@ -937,7 +941,7 @@ void EmitCSyms::emitDpiImp() {
puts("\n"); puts("\n");
for (AstCFunc* nodep : m_dpis) { for (AstCFunc* nodep : m_dpis) {
if (nodep->dpiExportWrapper()) { if (nodep->dpiExportDispatcher()) {
// Prevent multi-definition if used by multiple models // Prevent multi-definition if used by multiple models
puts("#ifndef VL_DPIDECL_" + nodep->name() + "_\n"); puts("#ifndef VL_DPIDECL_" + nodep->name() + "_\n");
puts("#define VL_DPIDECL_" + nodep->name() + "_\n"); puts("#define VL_DPIDECL_" + nodep->name() + "_\n");

View File

@ -414,7 +414,7 @@ private:
// UINFO(4, " CFUNC " << nodep << endl); // UINFO(4, " CFUNC " << nodep << endl);
if (!m_tracingCall && !nodep->entryPoint()) return; if (!m_tracingCall && !nodep->entryPoint()) return;
m_tracingCall = false; 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 m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
} }
iterateChildren(nodep); iterateChildren(nodep);

View File

@ -35,6 +35,7 @@
#include "V3LinkLValue.h" #include "V3LinkLValue.h"
#include <map> #include <map>
#include <tuple>
//###################################################################### //######################################################################
// Graph subclasses // Graph subclasses
@ -384,7 +385,7 @@ private:
IM_AFTER, // Pointing at last inserted stmt, insert after IM_AFTER, // Pointing at last inserted stmt, insert after
IM_WHILE_PRECOND // Pointing to for loop, add to body end IM_WHILE_PRECOND // Pointing to for loop, add to body end
}; };
using DpiNames = std::map<const string, std::pair<AstNodeFTask*, std::string>>; using DpiCFuncs = std::map<const string, std::tuple<AstNodeFTask*, std::string, AstCFunc*>>;
// STATE // STATE
TaskStateVisitor* m_statep; // Common state between visitors TaskStateVisitor* m_statep; // Common state between visitors
@ -394,7 +395,7 @@ private:
InsertMode m_insMode = IM_BEFORE; // How to insert InsertMode m_insMode = IM_BEFORE; // How to insert
AstNode* m_insStmtp = nullptr; // Where to insert statement AstNode* m_insStmtp = nullptr; // Where to insert statement
int m_modNCalls = 0; // Incrementing func # for making symbols 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 // METHODS
VL_DEBUG_FUNC; // Declare debug() VL_DEBUG_FUNC; // Declare debug()
@ -658,9 +659,9 @@ private:
return beginp; return beginp;
} }
string dpiprotoName(AstNodeFTask* nodep, AstVar* rtnvarp) const { string dpiSignature(AstNodeFTask* nodep, AstVar* rtnvarp) const {
// Return fancy export-ish name for DPI function // Return fancy signature for DPI function. Variable names are not included so differences
// Variable names are NOT included so differences in only IO names won't matter // in only argument names will not matter (as required by the standard).
string dpiproto; string dpiproto;
if (nodep->pure()) dpiproto += "pure "; if (nodep->pure()) dpiproto += "pure ";
if (nodep->dpiContext()) dpiproto += "context "; if (nodep->dpiContext()) dpiproto += "context ";
@ -757,18 +758,18 @@ private:
return newp; return newp;
} }
void makeDpiExportWrapper(AstNodeFTask* nodep, AstVar* rtnvarp) { AstCFunc* makeDpiExportDispatcher(AstNodeFTask* nodep, AstVar* rtnvarp) {
const char* const tmpSuffixp = V3Task::dpiTemporaryVarSuffix(); const char* const tmpSuffixp = V3Task::dpiTemporaryVarSuffix();
AstCFunc* dpip = new AstCFunc(nodep->fileline(), nodep->cname(), m_scopep, AstCFunc* const funcp = new AstCFunc(nodep->fileline(), nodep->cname(), m_scopep,
(rtnvarp ? rtnvarp->dpiArgType(true, true) : "")); (rtnvarp ? rtnvarp->dpiArgType(true, true) : ""));
dpip->dontCombine(true); funcp->dpiExportDispatcher(true);
dpip->entryPoint(true); funcp->dontCombine(true);
dpip->isStatic(true); funcp->entryPoint(true);
dpip->dpiExportWrapper(true); funcp->isStatic(true);
dpip->protect(false); funcp->protect(false);
dpip->cname(nodep->cname()); funcp->cname(nodep->cname());
// Add DPI reference to top, since it's a global function // Add DPI Export to top, since it's a global function
m_topScopep->scopep()->addActivep(dpip); m_topScopep->scopep()->addActivep(funcp);
{ // Create dispatch wrapper { // Create dispatch wrapper
// Note this function may dispatch to myfunc on a different class. // Note this function may dispatch to myfunc on a different class.
@ -795,7 +796,7 @@ private:
+ ")(VerilatedScope::exportFind(__Vscopep, __Vfuncnum));\n"; // Can't use + ")(VerilatedScope::exportFind(__Vscopep, __Vfuncnum));\n"; // Can't use
// static_cast // static_cast
// If __Vcb is null the exportFind function throws and error // 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 // Convert input/inout DPI arguments to Internal types
@ -813,7 +814,7 @@ private:
argnodesp = argnodesp->addNext(new AstText(portp->fileline(), args, true)); argnodesp = argnodesp->addNext(new AstText(portp->fileline(), args, true));
args = ""; 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 // No information exposure; is already visible in import/export func template
outvscp->varp()->protect(false); outvscp->varp()->protect(false);
portp->protect(false); portp->protect(false);
@ -829,7 +830,7 @@ private:
? "*" ? "*"
: ""; : "";
frName += portp->name(); 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)); argnodesp = argnodesp->addNext(new AstText(portp->fileline(), args, true));
args = ""; 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 // No information exposure; is already visible in import/export func template
outvscp->varp()->protect(false); outvscp->varp()->protect(false);
AstVarRef* refp = new AstVarRef(portp->fileline(), outvscp, AstVarRef* refp = new AstVarRef(portp->fileline(), outvscp,
@ -859,69 +860,85 @@ private:
newp->addBodysp(argnodesp); newp->addBodysp(argnodesp);
VL_DANGLING(argnodesp); VL_DANGLING(argnodesp);
newp->addBodysp(new AstText(nodep->fileline(), args, true)); newp->addBodysp(new AstText(nodep->fileline(), args, true));
dpip->addStmtsp(newp); funcp->addStmtsp(newp);
} }
// Convert output/inout arguments back to internal type // Convert output/inout arguments back to internal type
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (AstVar* portp = VN_CAST(stmtp, Var)) { if (AstVar* portp = VN_CAST(stmtp, Var)) {
if (portp->isIO() && portp->isWritable() && !portp->isFuncReturn()) { if (portp->isIO() && portp->isWritable() && !portp->isFuncReturn()) {
dpip->addStmtsp(createAssignInternalToDpi(portp, true, tmpSuffixp, "")); funcp->addStmtsp(createAssignInternalToDpi(portp, true, tmpSuffixp, ""));
} }
} }
} }
if (rtnvarp) { if (rtnvarp) {
dpip->addStmtsp(createDpiTemp(rtnvarp, "")); funcp->addStmtsp(createDpiTemp(rtnvarp, ""));
dpip->addStmtsp(createAssignInternalToDpi(rtnvarp, false, tmpSuffixp, "")); funcp->addStmtsp(createAssignInternalToDpi(rtnvarp, false, tmpSuffixp, ""));
string stmt = "return " + rtnvarp->name(); string stmt = "return " + rtnvarp->name();
stmt += rtnvarp->basicp()->isDpiPrimitive() ? ";\n" : "[0];\n"; 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())) { if (nodep->cname() != AstNode::prettyName(nodep->cname())) {
nodep->v3error("DPI function has illegal characters in C identifier name: " nodep->v3error("DPI function has illegal characters in C identifier name: "
<< AstNode::prettyNameQ(nodep->cname())); << AstNode::prettyNameQ(nodep->cname()));
} }
AstCFunc* dpip = new AstCFunc(nodep->fileline(), nodep->cname(), m_scopep, // Tasks (but not void functions) return a boolean 'int' indicating disabled
(rtnvarp ? rtnvarp->dpiArgType(true, true) const string rtnType
// Tasks (but not void functions) = rtnvarp ? rtnvarp->dpiArgType(true, true) : nodep->dpiTask() ? "int" : "";
// return bool indicating disabled AstCFunc* const funcp = new AstCFunc(nodep->fileline(), nodep->cname(), m_scopep, rtnType);
: nodep->dpiTask() ? "int" : "")); funcp->dpiImportPrototype(true);
dpip->dontCombine(true); funcp->dontCombine(true);
dpip->entryPoint(false); funcp->entryPoint(false);
dpip->funcPublic(true); funcp->isMethod(false);
dpip->isStatic(false); funcp->protect(false);
dpip->protect(false); funcp->pure(nodep->pure());
dpip->pure(nodep->pure()); // Add DPI Import to top, since it's a global function
dpip->dpiImport(true); m_topScopep->scopep()->addActivep(funcp);
// Add DPI reference to top, since it's a global function makePortList(nodep, funcp);
m_topScopep->scopep()->addActivep(dpip); return funcp;
makePortList(nodep, dpip);
} }
bool duplicatedDpiProto(AstNodeFTask* nodep, const string& dpiproto) { AstCFunc* getDpiFunc(AstNodeFTask* nodep, AstVar* rtnvarp) {
// Only create one DPI extern prototype for each specified cname UASSERT_OBJ(nodep->dpiImport() || nodep->dpiExport(), nodep, "Not a DPI function");
// as it's legal for the user to attach multiple tasks to one dpi cname // Compute unique signature of this DPI function
const auto iter = m_dpiNames.find(nodep->cname()); const string signature = dpiSignature(nodep, rtnvarp);
if (iter == m_dpiNames.end()) { // Only create one DPI Import prototype or DPI Export entry point for each unique cname as
m_dpiNames.emplace(nodep->cname(), std::make_pair(nodep, dpiproto)); // it is illegal for the user to attach multiple tasks with different signatures to one DPI
return false; // cname.
} else if (iter->second.second != dpiproto) { const auto it = m_dpiNames.find(nodep->cname());
nodep->v3error( if (it == m_dpiNames.end()) {
"Duplicate declaration of DPI function with different formal arguments: " // 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 {
// 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->prettyNameQ() << '\n'
<< nodep->warnContextPrimary() << '\n' << nodep->warnContextPrimary() << '\n'
<< nodep->warnMore() << "... New prototype: " << dpiproto << '\n' << nodep->warnMore() //
<< iter->second.first->warnOther() << "... New signature: " << signature << '\n' //
<< "... Original prototype: " << iter->second.second << '\n' << firstNodep->warnOther()
<< iter->second.first->warnContextSecondary()); << "... Original signature: " << firstSignature << '\n' //
return true; << firstNodep->warnContextSecondary());
return nullptr;
} else { } else {
return true; // 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(); const char* const tmpSuffixp = V3Task::dpiTemporaryVarSuffix();
// Convert input/inout arguments to DPI types // Convert input/inout arguments to DPI types
string args; string args;
@ -1010,15 +1028,17 @@ private:
cfuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); cfuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
} }
{ // Call the user function { // Call the imported function
string stmt;
if (rtnvscp) { // isFunction will no longer work as we unlinked the return var if (rtnvscp) { // isFunction will no longer work as we unlinked the return var
cfuncp->addStmtsp(createDpiTemp(rtnvscp->varp(), tmpSuffixp)); cfuncp->addStmtsp(createDpiTemp(rtnvscp->varp(), tmpSuffixp));
stmt = rtnvscp->varp()->name() + tmpSuffixp; string stmt = rtnvscp->varp()->name();
stmt += tmpSuffixp;
stmt += rtnvscp->varp()->basicp()->isDpiPrimitive() ? " = " : "[0] = "; stmt += rtnvscp->varp()->basicp()->isDpiPrimitive() ? " = " : "[0] = ";
cfuncp->addStmtsp(new AstText(nodep->fileline(), stmt, /* tracking: */ true));
} }
stmt += nodep->cname() + "(" + args + ");\n"; AstCCall* const callp = new AstCCall(nodep->fileline(), dpiFuncp);
cfuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); callp->argTypes(args);
cfuncp->addStmtsp(callp);
} }
// Convert output/inout arguments back to internal type // Convert output/inout arguments back to internal type
@ -1080,25 +1100,21 @@ private:
if (nodep->dpiImport() || nodep->dpiExport()) rtnvarp->protect(false); if (nodep->dpiImport() || nodep->dpiExport()) rtnvarp->protect(false);
} }
if (nodep->dpiImport()) { // Create/pick up the DPI CFunc for DPI Import/ DPI Export.
if (nodep->dpiOpenChild()) { // The parent will make the dpi proto AstCFunc* dpiFuncp = nullptr;
UASSERT_OBJ(!nodep->dpiOpenParent(), nodep, if (nodep->dpiImport() || nodep->dpiExport()) {
"DPI task should be parent or wrapper, not both"); UASSERT_OBJ(!(nodep->dpiOpenParent() && nodep->dpiOpenChild()), nodep,
} else { // Parent or not open child, make wrapper "DPI task should not be both parent and child");
string dpiproto = dpiprotoName(nodep, rtnvarp); dpiFuncp = getDpiFunc(nodep, rtnvarp);
if (!duplicatedDpiProto(nodep, dpiproto)) makeDpiImportProto(nodep, rtnvarp); if (!dpiFuncp) return nullptr; // There was an error, so bail
if (nodep->dpiOpenParent()) { if (nodep->dpiImport() && nodep->dpiOpenParent()) {
// No need to make more than just the c prototype, children will // No need to make more than just the DPI Import prototype, the children will
// create the wrapper implementations.
VL_DO_DANGLING(pushDeletep(nodep), nodep); VL_DO_DANGLING(pushDeletep(nodep), nodep);
return nullptr; return nullptr;
} }
} }
} else if (nodep->dpiExport()) {
string dpiproto = dpiprotoName(nodep, rtnvarp);
if (!duplicatedDpiProto(nodep, dpiproto)) makeDpiExportWrapper(nodep, rtnvarp);
}
AstVarScope* rtnvscp = nullptr; AstVarScope* rtnvscp = nullptr;
if (rtnvarp) { if (rtnvarp) {
rtnvscp = new AstVarScope(rtnvarp->fileline(), m_scopep, rtnvarp); rtnvscp = new AstVarScope(rtnvarp->fileline(), m_scopep, rtnvarp);
@ -1127,9 +1143,14 @@ private:
cfuncp->dontCombine(!nodep->dpiImport()); cfuncp->dontCombine(!nodep->dpiImport());
cfuncp->entryPoint(!nodep->dpiImport()); cfuncp->entryPoint(!nodep->dpiImport());
cfuncp->funcPublic(nodep->taskPublic()); cfuncp->funcPublic(nodep->taskPublic());
cfuncp->dpiExport(nodep->dpiExport()); cfuncp->dpiExportImpl(nodep->dpiExport());
cfuncp->dpiImportWrapper(nodep->dpiImport()); 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->isVirtual(nodep->isVirtual());
cfuncp->pure(nodep->pure()); cfuncp->pure(nodep->pure());
if (nodep->name() == "new") { if (nodep->name() == "new") {
@ -1140,8 +1161,7 @@ private:
+ "(vlSymsp)"); + "(vlSymsp)");
} }
} }
// cfuncp->dpiImport // Not set in the wrapper - the called function has it set if (cfuncp->dpiExportImpl()) cfuncp->cname(nodep->cname());
if (cfuncp->dpiExport()) cfuncp->cname(nodep->cname());
if (!nodep->dpiImport() && !nodep->taskPublic()) { if (!nodep->dpiImport() && !nodep->taskPublic()) {
// Need symbol table // Need symbol table
@ -1198,7 +1218,7 @@ private:
bodysp->unlinkFrBackWithNext(); bodysp->unlinkFrBackWithNext();
cfuncp->addStmtsp(bodysp); cfuncp->addStmtsp(bodysp);
} }
if (nodep->dpiImport()) bodyDpiImportFunc(nodep, rtnvscp, cfuncp); if (nodep->dpiImport()) bodyDpiImportFunc(nodep, rtnvscp, cfuncp, dpiFuncp);
// Return statement // Return statement
if (rtnvscp && nodep->taskPublic()) { if (rtnvscp && nodep->taskPublic()) {

View File

@ -834,7 +834,8 @@ private:
V3GraphVertex* const funcVtxp = getCFuncVertexp(nodep); V3GraphVertex* const funcVtxp = getCFuncVertexp(nodep);
if (!m_finding) { // If public, we need a unique activity code to allow for sets if (!m_finding) { // If public, we need a unique activity code to allow for sets
// directly in this func // 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()); V3GraphVertex* const activityVtxp = getActivityVertexp(nodep, nodep->slow());
new V3GraphEdge(&m_graph, activityVtxp, funcVtxp, 1); new V3GraphEdge(&m_graph, activityVtxp, funcVtxp, 1);
} }

View File

@ -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); 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) : ... New signature: pure int dpii_fa_bit (int, int)
t/t_dpi_dup_bad.v:12:47: ... Original prototype: int dpii_fa_bit (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); 12 | import "DPI-C" dpii_fa_bit = function int oth_f_int1(input int i);
| ^~~~~~~~~~ | ^~~~~~~~~~
%Error: Exiting due to %Error: Exiting due to