Internals: Refactor text based Ast constructs (#6280) (#6571)

Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.

There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
  text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
  AstCStmt, as it's largely equivalent. We should never create this
  internally unless used to represent user input. It is used for $c,
  statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
  AstCExpr. It is largely equivalent, but also has more optimizations
  disabled. This should never be created internally, it is only used for
  $c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
  wrappers.

Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.

Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.

Remove subsequently redundant AstNodeSimpleText and AstNodeText types.

This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.

With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
This commit is contained in:
Geza Lore 2025-10-21 13:41:29 +02:00 committed by GitHub
parent 14e3448ba6
commit cf275b6e58
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 1443 additions and 1409 deletions

View File

@ -152,7 +152,8 @@ class AssertVisitor final : public VNVisitor {
case VAssertDirectiveType::INTRINSIC: return new AstConst{fl, AstConst::BitTrue{}};
case VAssertDirectiveType::VIOLATION_CASE: {
if (v3Global.opt.assertCase()) {
return new AstCExpr{fl, "vlSymsp->_vm_contextp__->assertOn()", 1};
return new AstCExpr{fl, AstCExpr::Pure{}, "vlSymsp->_vm_contextp__->assertOn()",
1};
}
// If assertions are off, have constant propagation rip them out later
// This allows syntax errors and such to be detected normally.
@ -162,7 +163,7 @@ class AssertVisitor final : public VNVisitor {
case VAssertDirectiveType::COVER:
case VAssertDirectiveType::ASSUME: {
if (v3Global.opt.assertOn()) {
return new AstCExpr{fl,
return new AstCExpr{fl, AstCExpr::Pure{},
"vlSymsp->_vm_contextp__->assertOnGet("s + std::to_string(type)
+ ", "s + std::to_string(directiveType) + ")"s,
1};
@ -173,7 +174,8 @@ class AssertVisitor final : public VNVisitor {
case VAssertDirectiveType::VIOLATION_IF:
case VAssertDirectiveType::RESTRICT: {
if (v3Global.opt.assertOn()) {
return new AstCExpr{fl, "vlSymsp->_vm_contextp__->assertOn()", 1};
return new AstCExpr{fl, AstCExpr::Pure{}, "vlSymsp->_vm_contextp__->assertOn()",
1};
}
return new AstConst{fl, AstConst::BitFalse{}};
}

View File

@ -900,8 +900,6 @@ public:
virtual bool isPure() { return true; }
// Iff isPure on current node and any nextp()
bool isPureAndNext() { return isPure() && (!nextp() || nextp()->isPure()); }
// Else a AstTime etc that can't be substituted out
virtual bool isSubstOptimizable() const { return true; }
// An event control, delay, wait, etc.
virtual bool isTimingControl() const { return false; }
// isUnlikely handles $stop or similar statement which means an above IF
@ -1556,11 +1554,13 @@ AstNode* VNVisitor::iterateSubtreeReturnEdits(AstNode* nodep) {
// For precise details please read the generated macros.
#include "V3Ast__gen_macros.h"
// AstNode subclasses
// AstNode subclasses - dependency order matters, so fmt off
// clang-format off
#include "V3AstNodeDType.h"
#include "V3AstNodeExpr.h"
#include "V3AstNodeOther.h"
#include "V3AstNodeExpr.h"
#include "V3AstNodeStmt.h"
// clang-format on
// Inline function definitions need to go last
#include "V3AstInlines.h"

View File

@ -154,18 +154,6 @@ AstElabDisplay::AstElabDisplay(FileLine* fl, VDisplayType dispType, AstNodeExpr*
m_displayType = dispType;
}
AstCStmt::AstCStmt(FileLine* fl, const string& textStmt)
: ASTGEN_SUPER_CStmt(fl) {
addExprsp(new AstText{fl, textStmt, true});
}
AstCExpr::AstCExpr(FileLine* fl, const string& textStmt, int setwidth)
: ASTGEN_SUPER_CExpr(fl)
, m_pure{true} {
addExprsp(new AstText{fl, textStmt, true});
if (setwidth) dtypeSetLogicSized(setwidth, VSigning::UNSIGNED);
}
bool AstVar::sameNode(const AstNode* samep) const {
const AstVar* const asamep = VN_DBG_AS(samep, Var);
return m_name == asamep->m_name && varType() == asamep->varType();

View File

@ -563,27 +563,67 @@ public:
bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
};
class AstCExpr final : public AstNodeExpr {
// @astgen op1 := exprsp : List[AstNode] // Expressions to print
bool m_pure; // Pure optimizable
// C expression emitted into output, with some arbitrary nodes interspersed
// @astgen op1 := nodesp : List[AstNode<AstNodeExpr|AstText>]
const bool m_pure; // Pure optimizable
void init(const string& text, int setwidth) {
if (!text.empty()) add(text);
if (setwidth) {
dtypeSetLogicSized(setwidth, VSigning::UNSIGNED);
} else {
dtypeSetVoid(); // Caller to override if necessary
}
}
public:
// Emit C textual expr function (like AstUCFunc)
AstCExpr(FileLine* fl, AstNode* exprsp)
class Pure {};
AstCExpr(FileLine* fl, const string& text = "", int setwidth = 0)
: ASTGEN_SUPER_CExpr(fl)
, m_pure{false} {
addExprsp(exprsp);
dtypeFrom(exprsp);
init(text, setwidth);
}
AstCExpr(FileLine* fl, Pure, const string& text = "", int setwidth = 0)
: ASTGEN_SUPER_CExpr(fl)
, m_pure{true} {
init(text, setwidth);
}
inline AstCExpr(FileLine* fl, const string& textStmt, int setwidth);
ASTGEN_MEMBERS_AstCExpr;
bool isGateOptimizable() const override { return m_pure; }
bool isPredictOptimizable() const override { return m_pure; }
// METHODS
bool cleanOut() const override { return true; }
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
std::string emitC() override { V3ERROR_NA_RETURN(""); }
std::string emitVerilog() override { V3ERROR_NA_RETURN(""); }
bool isGateOptimizable() const override { return m_pure; }
bool isOutputter() override { return true; }
bool isPredictOptimizable() const override { return m_pure; }
bool isPure() override { return m_pure; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isPure() override { return pure(); }
bool pure() const { return m_pure; }
void pure(bool flag) { m_pure = flag; }
// Add some text, or a node to this expression
void add(const std::string& text) { addNodesp(new AstText{fileline(), text}); }
void add(AstNode* nodep) { addNodesp(nodep); }
};
class AstCExprUser final : public AstNodeExpr {
// User '$c' statement - Like AstCStmt, with text tracking and optimizations disabled.
//
// Use AstCExpr instead, unless the text is from user input.
//
// @astgen op1 := nodesp : List[AstNode<AstNodeExpr|AstText>]
public:
AstCExprUser(FileLine* fl)
: ASTGEN_SUPER_CExprUser(fl) {}
ASTGEN_MEMBERS_AstCExprUser;
// METHODS
bool cleanOut() const override { return false; }
std::string emitC() override { V3ERROR_NA_RETURN(""); }
std::string emitVerilog() override { V3ERROR_NA_RETURN(""); }
bool isGateOptimizable() const override { return false; }
bool isOutputter() override { return true; }
bool isPredictOptimizable() const override { return false; }
bool isPure() override { return false; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
// Add some text, or a node to this expression
void add(const std::string& text) { addNodesp(new AstText{fileline(), text}); }
void add(AstNode* nodep) { addNodesp(nodep); }
};
class AstCMethodHard final : public AstNodeExpr {
// A reference to a "C" hardcoded member task (or function)
@ -2354,26 +2394,6 @@ public:
VTimescale timeunit() const { return m_timeunit; }
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
};
class AstUCFunc final : public AstNodeExpr {
// User's $c function
// @astgen op1 := exprsp : List[AstNode] // Expressions to print (some are AstText)
public:
AstUCFunc(FileLine* fl, AstNode* exprsp)
: ASTGEN_SUPER_UCFunc(fl) {
addExprsp(exprsp);
}
ASTGEN_MEMBERS_AstUCFunc;
bool cleanOut() const override { return false; }
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool isPure() override { return false; } // SPECIAL: User may order w/other sigs
bool isOutputter() override { return true; }
bool isGateOptimizable() const override { return false; }
bool isSubstOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
int instrCount() const override { return INSTR_COUNT_PLI; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstUnbounded final : public AstNodeExpr {
// A $ in the parser, used for unbounded and queues
// Due to where is used, treated as Signed32

View File

@ -385,37 +385,6 @@ public:
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
class AstNodeText VL_NOT_FINAL : public AstNode {
string m_text;
// METHODS
string shortText() const;
protected:
// Node that puts text into the output stream
AstNodeText(VNType t, FileLine* fl, const string& text)
: AstNode{t, fl}
, m_text{text} {}
public:
ASTGEN_MEMBERS_AstNodeText;
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
bool sameNode(const AstNode* samep) const override {
const AstNodeText* asamep = VN_DBG_AS(samep, NodeText);
return text() == asamep->text();
}
const string& text() const VL_MT_SAFE { return m_text; }
void text(const string& value) { m_text = value; }
};
class AstNodeSimpleText VL_NOT_FINAL : public AstNodeText {
const bool m_tracking; // When emit, it's ok to parse the string to do indentation
public:
AstNodeSimpleText(VNType t, FileLine* fl, const string& textp, bool tracking = false)
: AstNodeText{t, fl, textp}
, m_tracking{tracking} {}
ASTGEN_MEMBERS_AstNodeSimpleText;
bool tracking() const { return m_tracking; }
};
// === Concrete node types =====================================================
@ -1666,6 +1635,49 @@ public:
bool isPure() override { return false; }
bool isOutputter() override { return true; }
};
class AstText final : public AstNode {
// Represents a piece of text to be emitted into the output
//
// Avoid using this directly, internally usually want
// AstCStmt::add("text") or AstCExpr::add("text") instead
//
std::string m_text; // The text to emit
public:
AstText(FileLine* fl, const std::string& text)
: ASTGEN_SUPER_Text(fl)
, m_text{text} {}
ASTGEN_MEMBERS_AstText;
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
bool sameNode(const AstNode* samep) const override {
return text() == VN_DBG_AS(samep, Text)->text();
}
const std::string& text() const VL_MT_SAFE { return m_text; }
void text(const string& value) { m_text = value; }
};
class AstTextBlock final : public AstNode {
// Text block emitted into output, with some arbitrary nodes interspersed
// @astgen op1 := nodesp : List[AstNode] // Nodes to print
const std::string m_prefix; // Prefix to print before first element in 'nodesp'
const std::string m_separator; // Separator to print between each element in 'nodesp'
const std::string m_suffix; // Suffix to pring after last element in 'nodesp'
public:
AstTextBlock(FileLine* fl, //
const std::string& prefix = "", //
const std::string& separator = "", //
const std::string& suffix = "")
: ASTGEN_SUPER_TextBlock(fl)
, m_prefix{prefix}
, m_separator{separator}
, m_suffix{suffix} {}
ASTGEN_MEMBERS_AstTextBlock;
const std::string& prefix() const { return m_prefix; }
const std::string& separator() const { return m_separator; }
const std::string& suffix() const { return m_suffix; }
// Add some text, or a node to this block
void add(const string& text) { addNodesp(new AstText{fileline(), text}); }
void add(AstNode* nodep) { addNodesp(nodep); }
};
class AstTopScope final : public AstNode {
// A singleton, held under the top level AstModule. Holds the top level
// AstScope, and after V3ActiveTop, the global list of AstSenTrees (list of
@ -2831,27 +2843,4 @@ public:
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
// === AstNodeSimpleText ===
class AstText final : public AstNodeSimpleText {
public:
AstText(FileLine* fl, const string& textp, bool tracking = false)
: ASTGEN_SUPER_Text(fl, textp, tracking) {}
ASTGEN_MEMBERS_AstText;
};
class AstTextBlock final : public AstNodeSimpleText {
// @astgen op1 := nodesp : List[AstNode]
bool m_commas; // Comma separate emitted children
public:
explicit AstTextBlock(FileLine* fl, const string& textp = "", bool tracking = false,
bool commas = false)
: ASTGEN_SUPER_TextBlock(fl, textp, tracking)
, m_commas{commas} {}
ASTGEN_MEMBERS_AstTextBlock;
bool commas() const { return m_commas; }
void commas(bool flag) { m_commas = flag; }
void addText(FileLine* fl, const string& textp, bool tracking = false) {
addNodesp(new AstText{fl, textp, tracking});
}
};
#endif // Guard

View File

@ -289,18 +289,49 @@ public:
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstCStmt final : public AstNodeStmt {
// Emit C statement
// @astgen op1 := exprsp : List[AstNode]
// C statement emitted into output, with some arbitrary nodes interspersed
// @astgen op1 := nodesp : List[AstNode<AstNodeStmt|AstNodeExpr|AstText>]
public:
AstCStmt(FileLine* fl, AstNode* exprsp)
AstCStmt(FileLine* fl, const std::string& text = "")
: ASTGEN_SUPER_CStmt(fl) {
addExprsp(exprsp);
if (!text.empty()) add(text);
}
inline AstCStmt(FileLine* fl, const string& textStmt);
ASTGEN_MEMBERS_AstCStmt;
bool isGateOptimizable() const override { return false; }
bool isOutputter() override { return true; }
bool isPredictOptimizable() const override { return false; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isPure() override { return false; }
bool sameNode(const AstNode*) const override { return true; }
// Add some text, or a node to this statement
void add(const std::string& text) { addNodesp(new AstText{fileline(), text}); }
void add(AstNode* nodep) { addNodesp(nodep); }
};
class AstCStmtUser final : public AstNodeStmt {
// User '$c' statement, also used for handling some AstSystemCSection.
// Same as AstCStmt, with text tracking disabled.
//
// Note this cannot be modelled as AstStmtExpr(AstCExprUser) because the
// latter would have an extra semicolon emitted, which might be undesirable.
//
// Use AstCStmt instead, unless the text is from user input.
//
// @astgen op1 := nodesp : List[AstNode<AstNodeExpr|AstText>]
const bool m_fromDollarC; // Is from source '$c', emit decoration
public:
AstCStmtUser(FileLine* fl, bool fromDollarC = false)
: ASTGEN_SUPER_CStmtUser(fl)
, m_fromDollarC{fromDollarC} {}
ASTGEN_MEMBERS_AstCStmtUser;
// METHODS
bool isGateOptimizable() const override { return false; }
bool isOutputter() override { return true; }
bool isPredictOptimizable() const override { return false; }
bool isPure() override { return false; }
bool sameNode(const AstNode*) const override { return true; }
bool fromDollarC() const { return m_fromDollarC; }
// Add some text, or a node to this statement
void add(const std::string& text) { addNodesp(new AstText{fileline(), text}); }
void add(AstNode* nodep) { addNodesp(nodep); }
};
class AstCase final : public AstNodeStmt {
// Case statement
@ -1201,21 +1232,6 @@ public:
string prefix() const { return m_prefix; }
VTracePrefixType prefixType() const { return m_prefixType; }
};
class AstUCStmt final : public AstNodeStmt {
// User $c statement
// @astgen op1 := exprsp : List[AstNode] // (some are AstText)
public:
AstUCStmt(FileLine* fl, AstNode* exprsp)
: ASTGEN_SUPER_UCStmt(fl) {
addExprsp(exprsp);
}
ASTGEN_MEMBERS_AstUCStmt;
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool isPure() override { return false; }
bool isOutputter() override { return true; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstWait final : public AstNodeStmt {
// @astgen op1 := condp : AstNodeExpr
// @astgen op2 := stmtsp : List[AstNode]

View File

@ -3071,21 +3071,21 @@ void AstTraceInc::dump(std::ostream& str) const {
}
}
void AstTraceInc::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
string AstNodeText::shortText() const {
string out = text();
string::size_type pos;
if ((pos = out.find('\n')) != string::npos) {
out.erase(pos, out.length() - pos);
out += "...";
}
return out;
}
void AstNodeText::dump(std::ostream& str) const {
void AstText::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " \"" << shortText() << "\"";
std::string txt = text();
if (txt.size() > 120) {
txt.resize(120);
txt += " ... omitted ...";
}
txt = VString::replaceSubstr(txt, "\\", "\\\\");
txt = VString::replaceSubstr(txt, "\"", "\\\"");
txt = VString::replaceSubstr(txt, "\n", "\\n");
txt = VString::replaceSubstr(txt, "\t", "\\t");
str << " \"" << txt << "\"";
}
void AstNodeText::dumpJson(std::ostream& str) const {
dumpJsonStrFunc(str, shortText);
void AstText::dumpJson(std::ostream& str) const {
dumpJsonStrFunc(str, text);
dumpJsonGen(str);
}

View File

@ -139,12 +139,12 @@ class CCtorsVisitor final : public VNVisitor {
// METHODS
static void insertSc(AstCFunc* cfuncp, const AstNodeModule* modp, VSystemCSectionType type) {
auto textAndFileline = EmitCBaseVisitorConst::scSection(modp, type);
if (!textAndFileline.first.empty()) {
AstTextBlock* const newp
= new AstTextBlock{textAndFileline.second, textAndFileline.first, false, false};
cfuncp->addStmtsp(newp);
}
const auto txtAndFlp = EmitCBaseVisitorConst::scSection(modp, type);
if (txtAndFlp.first.empty()) return;
// Use an AstCStmtUser as this is from user input
AstCStmtUser* const cstmtp = new AstCStmtUser{txtAndFlp.second};
cstmtp->add(txtAndFlp.first);
cfuncp->addStmtsp(cstmtp);
}
// VISITORS

View File

@ -219,9 +219,6 @@ class CleanVisitor final : public VNVisitor {
computeCppWidth(nodep);
if (nodep->cleanRhs()) ensureClean(nodep->rhsp());
}
void visit(AstText* nodep) override { //
setClean(nodep, true);
}
void visit(AstScopeName* nodep) override { //
setClean(nodep, true);
}
@ -238,13 +235,13 @@ class CleanVisitor final : public VNVisitor {
operandBiop(nodep);
setClean(nodep, nodep->cleanOut());
}
void visit(AstUCFunc* nodep) override {
void visit(AstCExprUser* nodep) override {
iterateChildren(nodep);
computeCppWidth(nodep);
setClean(nodep, false);
// We always clean, as we don't trust those pesky users.
if (!VN_IS(nodep->backp(), And)) insertClean(nodep);
for (AstNode* argp = nodep->exprsp(); argp; argp = argp->nextp()) {
for (AstNode* argp = nodep->nodesp(); argp; argp = argp->nextp()) {
if (AstNodeExpr* const exprp = VN_CAST(argp, NodeExpr)) ensureClean(exprp);
}
}
@ -282,9 +279,9 @@ class CleanVisitor final : public VNVisitor {
ensureCleanAndNext(nodep->exprsp());
setClean(nodep, true); // generates a string, so not relevant
}
void visit(AstUCStmt* nodep) override {
void visit(AstCStmtUser* nodep) override {
iterateChildren(nodep);
for (AstNode* argp = nodep->exprsp(); argp; argp = argp->nextp()) {
for (AstNode* argp = nodep->nodesp(); argp; argp = argp->nextp()) {
if (AstNodeExpr* const exprp = VN_CAST(argp, NodeExpr)) ensureClean(exprp);
}
}

View File

@ -58,7 +58,7 @@ static void makeVlToString(AstClass* nodep) {
funcp->isStatic(false);
funcp->protect(false);
AstNodeExpr* const exprp
= new AstCExpr{nodep->fileline(), "obj ? obj->to_string() : \"null\"", 0};
= new AstCExpr{nodep->fileline(), "obj ? obj->to_string() : \"null\""};
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
nodep->addStmtsp(funcp);
@ -71,7 +71,7 @@ static void makeVlToString(AstIface* nodep) {
funcp->isConst(false);
funcp->isStatic(false);
funcp->protect(false);
AstNodeExpr* const exprp = new AstCExpr{nodep->fileline(), "obj ? obj->name() : \"null\"", 0};
AstNodeExpr* const exprp = new AstCExpr{nodep->fileline(), "obj ? obj->name() : \"null\""};
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
nodep->addStmtsp(funcp);
@ -102,7 +102,7 @@ static void makeVlToString(AstNodeUOrStructDType* nodep) {
}
funcp->addStmtsp(new AstCStmt{nodep->fileline(), "out += \"}\";"});
AstCExpr* const exprp = new AstCExpr{nodep->fileline(), "out", 0};
AstCExpr* const exprp = new AstCExpr{nodep->fileline(), "out"};
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
@ -113,8 +113,7 @@ static void makeToString(AstClass* nodep) {
funcp->isConst(true);
funcp->isStatic(false);
funcp->protect(false);
AstCExpr* const exprp
= new AstCExpr{nodep->fileline(), R"("'{"s + to_string_middle() + "}")", 0};
AstCExpr* const exprp = new AstCExpr{nodep->fileline(), R"("'{"s + to_string_middle() + "}")"};
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
nodep->addStmtsp(funcp);
@ -155,7 +154,7 @@ static void makeToStringMiddle(AstClass* nodep) {
funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
}
AstCExpr* const exprp = new AstCExpr{nodep->fileline(), "out", 0};
AstCExpr* const exprp = new AstCExpr{nodep->fileline(), "out"};
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});

View File

@ -1159,12 +1159,12 @@ class DelayedVisitor final : public VNVisitor {
AstNodeExpr* const eventp = nodep->operandp()->unlinkFrBack();
// Enqueue for clearing 'triggered' state on next eval
AstTextBlock* const blockp = new AstTextBlock{flp};
blockp->addText(flp, "vlSymsp->fireEvent(", true);
blockp->addNodesp(eventp);
blockp->addText(flp, ");", true);
AstCStmt* const cstmtp = new AstCStmt{flp};
cstmtp->add("vlSymsp->fireEvent(");
cstmtp->add(eventp);
cstmtp->add(");");
AstNode* newp = new AstCStmt{flp, blockp};
AstNode* newp = cstmtp;
if (nodep->isDelayed()) {
const AstVarRef* const vrefp = VN_AS(eventp, VarRef);
const std::string newvarname = "__Vdly__" + vrefp->varp()->shortName();

View File

@ -131,11 +131,11 @@ class DepthVisitor final : public VNVisitor {
m_cfuncp->isStatic(false);
}
}
void visit(AstUCFunc* nodep) override {
void visit(AstCExprUser* nodep) override {
needNonStaticFunc(nodep);
iterateChildren(nodep);
}
void visit(AstUCStmt* nodep) override {
void visit(AstCStmtUser* nodep) override {
needNonStaticFunc(nodep);
visitStmt(nodep);
}

View File

@ -90,7 +90,6 @@ public:
V3OutCFile* m_ofp = nullptr;
AstCFile* m_outFileNodep = nullptr;
int m_splitSize = 0; // # of cfunc nodes placed into output file
bool m_trackText = false; // Always track AstText nodes
// METHODS
// Returns pointer to current output file object.

View File

@ -119,7 +119,6 @@ class EmitCFunc VL_NOT_FINAL : public EmitCConstInit {
VMemberMap m_memberMap;
AstVarRef* m_wideTempRefp = nullptr; // Variable that _WW macros should be setting
std::unordered_map<AstJumpBlock*, size_t> m_labelNumbers; // Label numbers for AstJumpBlocks
bool m_inUC = false; // Inside an AstUCStmt or AstUCExpr
bool m_createdScopeHash = false; // Already created a scope hash
// State associated with processing $display style string formatting
@ -320,6 +319,24 @@ public:
}
}
void emitNodesWithText(AstNode* nodesp, bool useSelfForThis, bool tracking,
const std::string& separator) {
for (AstNode* nodep = nodesp; nodep; nodep = nodep->nextp()) {
if (const AstText* const textp = VN_CAST(nodep, Text)) {
const std::string text
= VSelfPointerText::replaceThis(useSelfForThis, textp->text());
if (tracking) {
puts(text);
} else {
ofp()->putsNoTracking(text);
}
} else {
iterateConst(nodep);
}
if (nodep->nextp()) puts(separator);
}
}
void putConstructorSubinit(const AstClass* classp, AstCFunc* cfuncp) {
// Virtual bases in depth-first left-to-right order
std::vector<AstClass*> virtualBases;
@ -433,8 +450,8 @@ public:
return true;
});
if (m_instantiatesOwnProcess) {
AstNode* const vlprocp = new AstCStmt{
nodep->fileline(), "VlProcessRef vlProcess = std::make_shared<VlProcess>();"};
AstCStmt* const vlprocp = new AstCStmt{nodep->fileline()};
vlprocp->add("VlProcessRef vlProcess = std::make_shared<VlProcess>();");
nodep->stmtsp()->addHereThisAsNext(vlprocp);
}
@ -1330,48 +1347,43 @@ public:
void visit(AstTimePrecision* nodep) override {
putns(nodep, "vlSymsp->_vm_contextp__->timeprecision()");
}
void visit(AstNodeSimpleText* nodep) override {
const string text
= VSelfPointerText::replaceThis(m_inUC && m_useSelfForThis, nodep->text());
if (nodep->tracking() || m_trackText) {
puts(text);
} else {
ofp()->putsNoTracking(text);
}
// Nodes involing AstText
void visit(AstText* nodep) override {
// All Text should be under TextBlock/CStmt/CStmtUser/CExpr/CExprUser
nodep->v3fatalSrc("Text node in unexpected position");
}
void visit(AstTextBlock* nodep) override {
visit(static_cast<AstNodeSimpleText*>(nodep));
for (AstNode* childp = nodep->nodesp(); childp; childp = childp->nextp()) {
iterateConst(childp);
if (nodep->commas() && childp->nextp()) puts(", ");
}
putnbs(nodep, "");
puts(nodep->prefix());
emitNodesWithText(nodep->nodesp(), false, true, nodep->separator());
puts(nodep->suffix());
}
void visit(AstCStmt* nodep) override {
putnbs(nodep, "");
iterateAndNextConstNull(nodep->exprsp());
puts("\n");
emitNodesWithText(nodep->nodesp(), false, true, "");
ensureNewLine();
}
void visit(AstCExpr* nodep) override {
putnbs(nodep, "");
iterateAndNextConstNull(nodep->exprsp());
emitNodesWithText(nodep->nodesp(), false, true, "");
}
void visit(AstUCStmt* nodep) override {
VL_RESTORER(m_inUC);
m_inUC = true;
void visit(AstCStmtUser* nodep) override {
putnbs(nodep, "");
putsDecoration(nodep, VIdProtect::ifNoProtect("// $c statement at "
+ nodep->fileline()->ascii() + "\n"));
iterateAndNextConstNull(nodep->exprsp());
ofp()->putsNoTracking("\n");
if (nodep->fromDollarC() && v3Global.opt.decoration() && !v3Global.opt.protectIds()) {
ofp()->putsNoTracking("// $c statement at " + nodep->fileline()->ascii() + "\n");
}
emitNodesWithText(nodep->nodesp(), m_useSelfForThis, false, "");
puts("\n");
}
void visit(AstUCFunc* nodep) override {
VL_RESTORER(m_inUC);
m_inUC = true;
puts("\n");
void visit(AstCExprUser* nodep) override {
putnbs(nodep, "");
putsDecoration(nodep, VIdProtect::ifNoProtect("// $c function at "
+ nodep->fileline()->ascii() + "\n"));
iterateAndNextConstNull(nodep->exprsp());
ofp()->putsNoTracking("\n");
if (/* is always from $c */ v3Global.opt.decoration() && !v3Global.opt.protectIds()) {
ofp()->putsNoTracking("// $c expression at " + nodep->fileline()->ascii() + "\n");
}
emitNodesWithText(nodep->nodesp(), m_useSelfForThis, false, "");
puts("\n");
}
@ -1729,10 +1741,9 @@ public:
EmitCFunc()
: m_lazyDecls{*this} {}
EmitCFunc(AstNode* nodep, V3OutCFile* ofp, AstCFile* cfilep, bool trackText = false)
EmitCFunc(AstNode* nodep, V3OutCFile* ofp, AstCFile* cfilep)
: EmitCFunc{} {
setOutputFile(ofp, cfilep);
m_trackText = trackText;
iterateConst(nodep);
}
~EmitCFunc() override = default;

View File

@ -928,14 +928,14 @@ void V3EmitC::emitcImp() {
void V3EmitC::emitcFiles() {
UINFO(2, __FUNCTION__ << ":");
for (AstNodeFile* filep = v3Global.rootp()->filesp(); filep;
filep = VN_AS(filep->nextp(), NodeFile)) {
for (AstNodeFile *filep = v3Global.rootp()->filesp(), *nextp; filep; filep = nextp) {
nextp = VN_AS(filep->nextp(), NodeFile);
AstCFile* const cfilep = VN_CAST(filep, CFile);
if (cfilep && cfilep->tblockp()) {
V3OutCFile of{cfilep->name()};
of.puts("// DESCR"
"IPTION: Verilator generated C++\n");
const EmitCFunc visitor{cfilep->tblockp(), &of, cfilep, true};
EmitCFunc{cfilep->tblockp(), &of, cfilep};
}
}
}

View File

@ -30,7 +30,6 @@ VL_DEFINE_DEBUG_FUNCTIONS;
class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
// STATE - across all visitors
const bool m_alwaysTrackText; // Always track all NodeSimpleText
const bool m_suppressUnknown; // Do not error on unknown node
// STATE - for current visit position (use VL_RESTORER)
@ -71,6 +70,20 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
}
m_packedps.clear();
}
void emitNodesWithText(AstNode* nodesp, bool tracking, const std::string& separator) {
for (AstNode* nodep = nodesp; nodep; nodep = nodep->nextp()) {
if (const AstText* const textp = VN_CAST(nodep, Text)) {
if (tracking) {
puts(textp->text());
} else {
putsNoTracking(textp->text());
}
} else {
iterateConst(nodep);
}
if (nodep->nextp()) puts(separator);
}
}
// VISITORS
void visit(AstNetlist* nodep) override { iterateAndNextConstNull(nodep->modulesp()); }
@ -561,43 +574,41 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
iterateConst(nodep->exprp());
puts(";\n");
}
void visit(AstNodeSimpleText* nodep) override {
if (nodep->tracking() || m_alwaysTrackText) {
puts(nodep->text());
} else {
putsNoTracking(nodep->text());
}
// Nodes involing AstText
void visit(AstText* nodep) override {
// All Text should be under TextBlock/CStmt/CStmtUser/CExpr/CExprUser
nodep->v3fatalSrc("Text node in unexpected position");
}
void visit(AstTextBlock* nodep) override {
visit(static_cast<AstNodeSimpleText*>(nodep));
VL_RESTORER(m_suppressVarSemi);
m_suppressVarSemi = nodep->commas();
for (AstNode* childp = nodep->nodesp(); childp; childp = childp->nextp()) {
iterateConst(childp);
if (nodep->commas() && childp->nextp()) puts(", ");
}
m_suppressVarSemi = !nodep->separator().empty();
puts(nodep->prefix());
emitNodesWithText(nodep->nodesp(), true, nodep->separator());
puts(nodep->suffix());
}
void visit(AstScopeName* nodep) override {}
void visit(AstCStmt* nodep) override {
putfs(nodep, "$_CSTMT(");
iterateAndCommaConstNull(nodep->exprsp());
emitNodesWithText(nodep->nodesp(), true, "");
puts(");\n");
}
void visit(AstCExpr* nodep) override {
putfs(nodep, "$_CEXPR(");
iterateAndCommaConstNull(nodep->exprsp());
emitNodesWithText(nodep->nodesp(), true, "");
puts(")");
}
void visit(AstUCStmt* nodep) override {
void visit(AstCStmtUser* nodep) override {
putfs(nodep, "$c(");
iterateAndCommaConstNull(nodep->exprsp());
emitNodesWithText(nodep->nodesp(), false, "");
puts(");\n");
}
void visit(AstUCFunc* nodep) override {
void visit(AstCExprUser* nodep) override {
putfs(nodep, "$c(");
iterateAndNextConstNull(nodep->exprsp());
emitNodesWithText(nodep->nodesp(), false, "");
puts(")");
}
void visit(AstScopeName* nodep) override {}
void visit(AstExprStmt* nodep) override {
putfs(nodep, "$_EXPRSTMT(\n");
iterateAndNextConstNull(nodep->stmtsp());
@ -1129,7 +1140,6 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
iterateConst(methodp->pinsp());
}
void visit(AstParseRef* nodep) override { puts(nodep->prettyName()); }
void visit(AstNodeText*) override {}
void visit(AstVarScope*) override {}
void visit(AstTraceDecl*) override {}
void visit(AstTraceInc*) override {}
@ -1148,9 +1158,8 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
}
public:
explicit EmitVBaseVisitorConst(bool alwaysTrackText, bool suppressUnknown)
: m_alwaysTrackText{alwaysTrackText}
, m_suppressUnknown{suppressUnknown} {}
explicit EmitVBaseVisitorConst(bool suppressUnknown)
: m_suppressUnknown{suppressUnknown} {}
~EmitVBaseVisitorConst() override = default;
};
@ -1168,8 +1177,8 @@ class EmitVFileVisitor final : public EmitVBaseVisitorConst {
void putqs(AstNode*, const string& str) override { putbs(str); }
public:
EmitVFileVisitor(AstNode* nodep, V3OutVFile& of, bool alwaysTrackText, bool suppressUnknown)
: EmitVBaseVisitorConst{alwaysTrackText, suppressUnknown}
EmitVFileVisitor(AstNode* nodep, V3OutVFile& of, bool suppressUnknown)
: EmitVBaseVisitorConst{suppressUnknown}
, m_of{of} {
iterateConst(nodep);
}
@ -1182,7 +1191,7 @@ public:
class EmitVStreamVisitor final : public EmitVBaseVisitorConst {
// STATE
V3OutStream m_os; // The output stream formatter
bool m_tracking; // Use line tracking
const bool m_tracking; // Use line tracking
// METHODS
void putsNoTracking(const string& str) override { m_os.putsNoTracking(str); }
void puts(const string& str) override {
@ -1196,7 +1205,7 @@ class EmitVStreamVisitor final : public EmitVBaseVisitorConst {
public:
EmitVStreamVisitor(const AstNode* nodep, std::ostream& os, bool tracking, bool suppressUnknown)
: EmitVBaseVisitorConst{false, suppressUnknown}
: EmitVBaseVisitorConst{suppressUnknown}
, m_os{os, V3OutFormatter::LA_VERILOG}
, m_tracking{tracking} {
iterateConst(const_cast<AstNode*>(nodep));
@ -1223,7 +1232,7 @@ void V3EmitV::emitvFiles() {
if (vfilep && vfilep->tblockp()) {
V3OutVFile of{vfilep->name()};
of.puts("// DESCRIPTION: Verilator generated Verilog\n");
{ EmitVFileVisitor{vfilep->tblockp(), of, true, false}; }
EmitVFileVisitor{vfilep->tblockp(), of, false};
}
}
}
@ -1231,5 +1240,5 @@ void V3EmitV::emitvFiles() {
void V3EmitV::debugEmitV(const string& filename) {
UINFO(2, __FUNCTION__ << ":");
V3OutVFile of{filename};
{ EmitVFileVisitor{v3Global.rootp(), of, true, true}; }
EmitVFileVisitor{v3Global.rootp(), of, true};
}

View File

@ -911,7 +911,7 @@ void addMTaskToFunction(const ThreadSchedule& schedule, const uint32_t threadId,
FileLine* const fl = modp->fileline();
// Helper function to make the code a bit more legible
const auto addStrStmt = [=](const string& stmt) -> void { //
const auto addCStmt = [=](const string& stmt) -> void { //
funcp->addStmtsp(new AstCStmt{fl, stmt});
};
@ -923,21 +923,21 @@ void addMTaskToFunction(const ThreadSchedule& schedule, const uint32_t threadId,
= v3Global.rootp()->typeTablep()->findBasicDType(fl, VBasicDTypeKwd::MTASKSTATE);
AstVar* const varp = new AstVar{fl, VVarType::MODULETEMP, name, mtaskStateDtypep};
varp->valuep(new AstConst{fl, nDependencies});
varp->protect(false); // Do not protect as we still have references in AstText
varp->protect(false); // Do not protect as we have references in text
modp->addStmtsp(varp);
// For now, reference is still via text bashing
if (v3Global.opt.profExec()) {
addStrStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).threadScheduleWaitBegin();");
addCStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).threadScheduleWaitBegin();");
}
addStrStmt("vlSelf->" + name + +".waitUntilUpstreamDone(even_cycle);");
addCStmt("vlSelf->" + name + +".waitUntilUpstreamDone(even_cycle);");
if (v3Global.opt.profExec()) {
addStrStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).threadScheduleWaitEnd();");
addCStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).threadScheduleWaitEnd();");
}
}
if (v3Global.opt.profPgo()) {
// No lock around startCounter, as counter numbers are unique per thread
addStrStmt("vlSymsp->_vm_pgoProfiler.startCounter(" + std::to_string(mtaskp->id()) + ");");
addCStmt("vlSymsp->_vm_pgoProfiler.startCounter(" + std::to_string(mtaskp->id()) + ");");
}
// Move the actual body into this function
@ -945,15 +945,15 @@ void addMTaskToFunction(const ThreadSchedule& schedule, const uint32_t threadId,
if (v3Global.opt.profPgo()) {
// No lock around stopCounter, as counter numbers are unique per thread
addStrStmt("vlSymsp->_vm_pgoProfiler.stopCounter(" + std::to_string(mtaskp->id()) + ");");
addCStmt("vlSymsp->_vm_pgoProfiler.stopCounter(" + std::to_string(mtaskp->id()) + ");");
}
// For any dependent mtask that's on another thread, signal one dependency completion.
for (const V3GraphEdge& edge : mtaskp->outEdges()) {
const ExecMTask* const nextp = edge.top()->as<ExecMTask>();
if (schedule.threadId(nextp) != threadId && schedule.contains(nextp)) {
addStrStmt("vlSelf->__Vm_mtaskstate_" + cvtToStr(nextp->id())
+ ".signalUpstreamDone(even_cycle);");
addCStmt("vlSelf->__Vm_mtaskstate_" + cvtToStr(nextp->id())
+ ".signalUpstreamDone(even_cycle);");
}
}
}
@ -1001,7 +1001,7 @@ const std::vector<AstCFunc*> createThreadFunctions(const ThreadSchedule& schedul
= new AstVar{fl, VVarType::MODULETEMP,
"__Vm_mtaskstate_final__" + cvtToStr(schedule.id()) + tag, mtaskStateDtypep};
varp->valuep(new AstConst(fl, funcps.size()));
varp->protect(false); // Do not protect as we still have references in AstText
varp->protect(false); // Do not protect as we have references in text
modp->addStmtsp(varp);
return funcps;
@ -1013,30 +1013,28 @@ void addThreadStartWrapper(AstExecGraph* const execGraphp) {
const string& tag = execGraphp->name();
// Add thread function invocations to execGraph
const auto addStrStmt = [=](const string& stmt) -> void { //
const auto addCStmt = [=](const string& stmt) -> void { //
execGraphp->addStmtsp(new AstCStmt{fl, stmt});
};
if (v3Global.opt.profExec()) {
addStrStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).execGraphBegin();");
addCStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).execGraphBegin();");
}
addStrStmt("vlSymsp->__Vm_even_cycle__" + tag + " = !vlSymsp->__Vm_even_cycle__" + tag + ";");
addCStmt("vlSymsp->__Vm_even_cycle__" + tag + " = !vlSymsp->__Vm_even_cycle__" + tag + ";");
if (!v3Global.opt.hierBlocks().empty()) addStrStmt("std::vector<size_t> indexes;");
if (!v3Global.opt.hierBlocks().empty()) addCStmt("std::vector<size_t> indexes;");
}
void addThreadEndWrapper(AstExecGraph* const execGraphp) {
// Add thread function invocations to execGraph
const auto addStrStmt = [=](const string& stmt) -> void { //
const auto addCStmt = [=](const string& stmt) -> void { //
FileLine* const flp = v3Global.rootp()->fileline();
execGraphp->addStmtsp(new AstCStmt{flp, stmt});
};
addStrStmt("Verilated::mtaskId(0);");
if (v3Global.opt.profExec()) {
addStrStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).execGraphEnd();");
}
addCStmt("Verilated::mtaskId(0);");
if (v3Global.opt.profExec()) { addCStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).execGraphEnd();"); }
}
void addThreadStartToExecGraph(AstExecGraph* const execGraphp,
const std::vector<AstCFunc*>& funcps, uint32_t scheduleId) {
@ -1045,30 +1043,31 @@ void addThreadStartToExecGraph(AstExecGraph* const execGraphp,
const string& tag = execGraphp->name();
// Add thread function invocations to execGraph
const auto addStrStmt = [=](const string& stmt) -> void { //
const auto addCStmt = [=](const string& stmt) -> void { //
execGraphp->addStmtsp(new AstCStmt{fl, stmt});
};
const auto addTextStmt = [=](const string& text) -> void {
execGraphp->addStmtsp(new AstText{fl, text, /* tracking: */ true});
};
const uint32_t last = funcps.size() - 1;
if (!v3Global.opt.hierBlocks().empty() && last > 0) {
addStrStmt("for (size_t i = 0; i < " + cvtToStr(last)
+ "; ++i) indexes.push_back(vlSymsp->__Vm_threadPoolp->assignWorkerIndex());");
addCStmt("for (size_t i = 0; i < " + std::to_string(last) + "; ++i) {\n" //
+ "indexes.push_back(vlSymsp->__Vm_threadPoolp->assignWorkerIndex());\n" //
+ "}");
}
uint32_t i = 0;
for (AstCFunc* const funcp : funcps) {
if (i != last) {
// The first N-1 will run on the thread pool.
AstCStmt* const cstmtp = new AstCStmt{fl};
execGraphp->addStmtsp(cstmtp);
cstmtp->add("vlSymsp->__Vm_threadPoolp->workerp(");
if (v3Global.opt.hierChild() || !v3Global.opt.hierBlocks().empty()) {
addTextStmt("vlSymsp->__Vm_threadPoolp->workerp(indexes[" + cvtToStr(i)
+ "])->addTask(");
cstmtp->add("indexes[" + std::to_string(i) + "]");
} else {
addTextStmt("vlSymsp->__Vm_threadPoolp->workerp(" + cvtToStr(i) + ")->addTask(");
cstmtp->add(std::to_string(i));
}
execGraphp->addStmtsp(new AstAddrOfCFunc{fl, funcp});
addTextStmt(", vlSelf, vlSymsp->__Vm_even_cycle__" + tag + ");\n");
cstmtp->add(")->addTask(");
cstmtp->add(new AstAddrOfCFunc{fl, funcp});
cstmtp->add(", vlSelf, vlSymsp->__Vm_even_cycle__" + tag + ");");
} else {
// The last will run on the main thread.
AstCCall* const callp = new AstCCall{fl, funcp};
@ -1081,16 +1080,16 @@ void addThreadStartToExecGraph(AstExecGraph* const execGraphp,
V3Stats::addStatSum("Optimizations, Thread schedule total tasks", i);
if (v3Global.opt.profExec()) {
addStrStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).threadScheduleWaitBegin();");
addCStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).threadScheduleWaitBegin();");
}
addStrStmt("vlSelf->__Vm_mtaskstate_final__" + std::to_string(scheduleId) + tag
+ ".waitUntilUpstreamDone(vlSymsp->__Vm_even_cycle__" + tag + ");");
addCStmt("vlSelf->__Vm_mtaskstate_final__" + std::to_string(scheduleId) + tag
+ ".waitUntilUpstreamDone(vlSymsp->__Vm_even_cycle__" + tag + ");");
if (v3Global.opt.profExec()) {
addStrStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).threadScheduleWaitEnd();");
addCStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).threadScheduleWaitEnd();");
}
// Free all assigned worker indices in this section
if (!v3Global.opt.hierBlocks().empty() && last > 0) {
addStrStmt("vlSymsp->__Vm_threadPoolp->freeWorkerIndexes(indexes);");
addCStmt("vlSymsp->__Vm_threadPoolp->freeWorkerIndexes(indexes);");
}
}
@ -1108,35 +1107,35 @@ void wrapMTaskBodies(AstExecGraph* const execGraphp) {
modp->addStmtsp(funcp);
// Helper function to make the code a bit more legible
const auto addStrStmt = [=](const string& stmt) -> void { //
const auto addCStmt = [=](const string& stmt) -> void { //
funcp->addStmtsp(new AstCStmt{flp, stmt});
};
addStrStmt("static constexpr unsigned taskId = " + cvtToStr(mtaskp->id()) + ";");
addCStmt("static constexpr unsigned taskId = " + cvtToStr(mtaskp->id()) + ";");
if (v3Global.opt.profExec()) {
const string& predictStart = std::to_string(mtaskp->predictStart());
if (v3Global.opt.hierChild()) {
addStrStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).mtaskBegin(taskId, " + predictStart
+ ", \"" + v3Global.opt.topModule() + "\");");
addCStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).mtaskBegin(taskId, " + predictStart
+ ", \"" + v3Global.opt.topModule() + "\");");
} else {
addStrStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).mtaskBegin(taskId, " + predictStart
+ ");");
addCStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).mtaskBegin(taskId, " + predictStart
+ ");");
}
}
// Set mtask ID in the run-time system
addStrStmt("Verilated::mtaskId(taskId);");
addCStmt("Verilated::mtaskId(taskId);");
// Run body
funcp->addStmtsp(mtaskBodyp->stmtsp()->unlinkFrBackWithNext());
// Flush message queue
addStrStmt("Verilated::endOfThreadMTask(vlSymsp->__Vm_evalMsgQp);");
addCStmt("Verilated::endOfThreadMTask(vlSymsp->__Vm_evalMsgQp);");
if (v3Global.opt.profExec()) {
const string& predictCost = std::to_string(mtaskp->cost());
addStrStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).mtaskEnd(" + predictCost + ");");
addCStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).mtaskEnd(" + predictCost + ");");
}
// AstMTask will simply contain a call

View File

@ -702,7 +702,7 @@ int V3OutFormatter::endLevels(const char* strg) {
case '\n': // Newlines.. No need for whitespace before it
return 0;
case '#': // Preproc directive
return 0;
if (m_lang == LA_C) return 0;
}
{
// label/public/private: Deindent by 2 spaces
@ -738,20 +738,25 @@ void V3OutFormatter::putns(const AstNode* nodep, const char* strg) {
return;
}
if (m_prependIndent && strg[0] != '\n') {
const bool putNodeDecoration = //
nodep //
&& v3Global.opt.decorationNodes() //
&& !v3Global.opt.protectIds() //
&& (m_sourceLastFilenameno != nodep->fileline()->filenameno() //
|| m_sourceLastLineno != nodep->fileline()->firstLineno()) //
&& FileLine::builtInFilename() != nodep->fileline()->filename();
if (m_prependIndent && ((strg[0] && strg[0] != '\n') || putNodeDecoration)) {
putsNoTracking(indentSpaces(endLevels(strg)));
m_prependIndent = false;
}
if (nodep && v3Global.opt.decorationNodes() && !v3Global.opt.protectIds()
&& (m_sourceLastFilenameno != nodep->fileline()->filenameno()
|| m_sourceLastLineno != nodep->fileline()->firstLineno())
&& FileLine::builtInFilename() != nodep->fileline()->filename()) {
m_sourceLastLineno = nodep->fileline()->firstLineno();
m_sourceLastFilenameno = nodep->fileline()->filenameno();
putsNoTracking("/*" + nodep->fileline()->filename() + ":"
+ cvtToStr(nodep->fileline()->lineno()) + " " + cvtToStr((void*)nodep)
+ "*/");
if (putNodeDecoration) {
FileLine* const flp = nodep->fileline();
m_sourceLastLineno = flp->firstLineno();
m_sourceLastFilenameno = flp->filenameno();
const std::string lineno = std::to_string(flp->lineno());
putsNoTracking("/*" + flp->filename() + ":" + lineno + " " + cvtToHex(nodep) + "*/");
}
bool notstart = false;

View File

@ -275,11 +275,6 @@ class HasherVisitor final : public VNVisitorConst {
void visit(AstNodeStmt* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, []() {});
}
void visit(AstNodeText* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->text();
});
}
void visit(AstNodeCCall* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
iterateConstNull(nodep->funcp());
@ -535,6 +530,30 @@ class HasherVisitor final : public VNVisitorConst {
m_hash += nodep->pinNum();
});
}
void visit(AstText* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->text();
});
}
void visit(AstTextBlock* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->prefix();
m_hash += nodep->separator();
m_hash += nodep->suffix();
});
}
void visit(AstCStmt* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, []() {});
}
void visit(AstCStmtUser* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, []() {});
}
void visit(AstCExpr* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
void visit(AstCExprUser* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
public:
// CONSTRUCTORS

View File

@ -135,26 +135,25 @@ public:
// METHODS
void checkRemoveAssign(AstVarScope* vscp, LifeVarEntry& entr) {
const AstVar* const varp = vscp->varp();
if (!varp->isSigPublic() && !varp->sensIfacep()) {
// Rather than track what sigs AstUCFunc/AstUCStmt may change,
// we just don't optimize any public sigs
// Check the var entry, and remove if appropriate
if (AstNodeStmt* const oldassp = entr.assignp()) {
UINFO(7, " PREV: " << oldassp);
// Redundant assignment, in same level block
// Don't delete it now as it will confuse iteration since it maybe WAY
// above our current iteration point.
UINFOTREE(7, oldassp, "", "REMOVE/SAMEBLK");
entr.complexAssign();
oldassp->unlinkFrBack();
if (VN_IS(oldassp, CReset)) {
++m_statep->m_statCResetDel;
} else {
++m_statep->m_statAssnDel;
}
VL_DO_DANGLING(m_deleter.pushDeletep(oldassp), oldassp);
}
// We don't optimize any public sigs
if (varp->isSigPublic()) return;
if (varp->sensIfacep()) return;
// Check the var entry, and remove if appropriate
AstNodeStmt* const oldassp = entr.assignp();
if (!oldassp) return;
UINFO(7, " PREV: " << oldassp);
// Redundant assignment, in same level block
// Don't delete it now as it will confuse iteration since it maybe WAY
// above our current iteration point.
UINFOTREE(7, oldassp, "", "REMOVE/SAMEBLK");
entr.complexAssign();
oldassp->unlinkFrBack();
if (VN_IS(oldassp, CReset)) {
++m_statep->m_statCResetDel;
} else {
++m_statep->m_statAssnDel;
}
VL_DO_DANGLING(m_deleter.pushDeletep(oldassp), oldassp);
}
void resetStatement(AstVarScope* nodep, AstCReset* rstp) {
// Do we have a old assignment we can nuke?
@ -434,7 +433,7 @@ class LifeVisitor final : public VNVisitor {
}
iterateChildren(nodep);
}
void visit(AstUCFunc* nodep) override {
void visit(AstCExprUser* nodep) override {
m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
iterateChildren(nodep);
}

View File

@ -74,9 +74,8 @@ public:
AstVar* createVariable(FileLine* fileline, const string& name, AstNodeRange* arrayp,
AstNode* attrsp) VL_MT_DISABLED;
AstAssignW* createSupplyExpr(FileLine* fileline, const string& name, int value) VL_MT_DISABLED;
AstText* createTextQuoted(FileLine* fileline, const string& text) {
string newtext = singletonp()->unquoteString(fileline, text);
return new AstText{fileline, newtext};
std::string textQuoted(FileLine* fileline, const std::string& text) {
return singletonp()->unquoteString(fileline, text);
}
AstNode* createCell(FileLine* fileline, const string& name, AstPin* pinlistp,
AstNodeRange* rangelistp) {

View File

@ -257,7 +257,7 @@ class PremitVisitor final : public VNVisitor {
iterateChildren(nodep);
checkNode(nodep);
}
void visit(AstUCFunc* nodep) override {
void visit(AstCExprUser* nodep) override {
iterateChildren(nodep);
checkNode(nodep);
}

View File

@ -89,41 +89,39 @@ class ProtectVisitor final : public VNVisitor {
// cppcheck-suppress unreadVariable
const V3Hash hash = V3Hasher::uncachedHash(m_cfilep);
m_hashValuep->addText(fl, cvtToStr(hash.value()) + ";\n");
m_cHashValuep->addText(fl, cvtToStr(hash.value()) + "U;\n");
m_hashValuep->add(cvtToStr(hash.value()) + ";\n");
m_cHashValuep->add(cvtToStr(hash.value()) + "U;\n");
m_foundTop = true;
}
void addComment(AstTextBlock* txtp, FileLine* fl, const string& comment) {
txtp->addNodesp(new AstComment{fl, comment});
txtp->add(new AstComment{fl, comment});
}
void configSection(AstNodeModule* modp, AstTextBlock* txtp, FileLine* fl) {
txtp->addText(fl, "\n`ifdef VERILATOR\n");
txtp->addText(fl, "`verilator_config\n");
txtp->add("\n`ifdef VERILATOR\n");
txtp->add("`verilator_config\n");
txtp->addText(fl, "profile_data -hier-dpi \"" + m_libName
+ "_protectlib_combo_update\" -cost 64'd"
+ std::to_string(v3Global.currentHierBlockCost()) + "\n");
txtp->addText(fl, "profile_data -hier-dpi \"" + m_libName
+ "_protectlib_seq_update\" -cost 64'd"
+ std::to_string(v3Global.currentHierBlockCost()) + "\n");
txtp->add("profile_data -hier-dpi \"" + m_libName + "_protectlib_combo_update\" -cost 64'd"
+ std::to_string(v3Global.currentHierBlockCost()) + "\n");
txtp->add("profile_data -hier-dpi \"" + m_libName + "_protectlib_seq_update\" -cost 64'd"
+ std::to_string(v3Global.currentHierBlockCost()) + "\n");
// Mark remaining NBA protectlib wrapper DPIs as non-hazardous by deliberately forwarding
// them with non-zero cost.
// Also, specify hierarchical workers for those tasks for scheduling.
txtp->addText(fl, "profile_data -hier-dpi \"" + m_libName
+ "_protectlib_combo_ignore\" -cost 64'd1\n");
txtp->add("profile_data -hier-dpi \"" + m_libName
+ "_protectlib_combo_ignore\" -cost 64'd1\n");
txtp->addText(fl, "hier_workers -hier-dpi \"" + m_libName
+ "_protectlib_combo_update\" -workers 16'd"
+ std::to_string(V3Control::getHierWorkers(m_libName)) + "\n");
txtp->addText(fl, "hier_workers -hier-dpi \"" + m_libName
+ "_protectlib_seq_update\" -workers 16'd"
+ std::to_string(V3Control::getHierWorkers(m_libName)) + "\n");
txtp->add("hier_workers -hier-dpi \"" + m_libName
+ "_protectlib_combo_update\" -workers 16'd"
+ std::to_string(V3Control::getHierWorkers(m_libName)) + "\n");
txtp->add("hier_workers -hier-dpi \"" + m_libName
+ "_protectlib_seq_update\" -workers 16'd"
+ std::to_string(V3Control::getHierWorkers(m_libName)) + "\n");
// No workers for combo_ignore
txtp->addText(fl, "`verilog\n");
txtp->addText(fl, "`endif\n");
txtp->add("`verilog\n");
txtp->add("`endif\n");
}
void hashComment(AstTextBlock* txtp, FileLine* fl) {
@ -165,16 +163,14 @@ class ProtectVisitor final : public VNVisitor {
" to use DPI libraries\n");
// Module declaration
m_modPortsp = new AstTextBlock{fl, "module " + m_libName + " (\n", false, true};
txtp->addNodesp(m_modPortsp);
txtp->addText(fl, ");\n\n");
m_modPortsp = new AstTextBlock{fl, "module " + m_libName + " (\n", ", ", ");\n\n"};
txtp->add(m_modPortsp);
// Timescale
if (v3Global.opt.hierChild() && v3Global.rootp()->timescaleSpecified()) {
// Emit timescale for hierarchical Verilation if input HDL specifies timespec
txtp->addText(fl, "timeunit "s + modp->timeunit().ascii() + ";\n");
txtp->addText(fl,
"timeprecision "s + +v3Global.rootp()->timeprecision().ascii() + ";\n");
txtp->add("timeunit "s + modp->timeunit().ascii() + ";\n");
txtp->add("timeprecision "s + +v3Global.rootp()->timeprecision().ascii() + ";\n");
} else {
addComment(txtp, fl,
"Precision of submodule"
@ -186,131 +182,129 @@ class ProtectVisitor final : public VNVisitor {
// DPI declarations
hashComment(txtp, fl);
txtp->addText(fl, "import \"DPI-C\" function void " + m_libName
+ "_protectlib_check_hash(int protectlib_hash__V);\n\n");
txtp->add("import \"DPI-C\" function void " + m_libName
+ "_protectlib_check_hash(int protectlib_hash__V);\n\n");
initialComment(txtp, fl);
txtp->addText(fl, "import \"DPI-C\" function chandle " + m_libName
+ "_protectlib_create(string scope__V);\n\n");
txtp->add("import \"DPI-C\" function chandle " + m_libName
+ "_protectlib_create(string scope__V);\n\n");
comboComment(txtp, fl);
m_comboPortsp = new AstTextBlock{fl,
m_comboPortsp = new AstTextBlock{fl, //
"import \"DPI-C\" function longint " + m_libName
+ "_protectlib_combo_update "
"(\n",
false, true};
m_comboPortsp->addText(fl, "chandle handle__V\n");
txtp->addNodesp(m_comboPortsp);
txtp->addText(fl, ");\n\n");
+ "_protectlib_combo_update(\n", //
", ", //
");\n\n"};
m_comboPortsp->add("chandle handle__V\n");
txtp->add(m_comboPortsp);
seqComment(txtp, fl);
if (m_hasClk) {
m_seqPortsp = new AstTextBlock{fl,
m_seqPortsp = new AstTextBlock{fl, //
"import \"DPI-C\" function longint " + m_libName
+ "_protectlib_seq_update"
"(\n",
false, true};
m_seqPortsp->addText(fl, "chandle handle__V\n");
txtp->addNodesp(m_seqPortsp);
txtp->addText(fl, ");\n\n");
+ "_protectlib_seq_update(\n", //
", ", //
");\n\n"};
m_seqPortsp->add("chandle handle__V\n");
txtp->add(m_seqPortsp);
}
comboIgnoreComment(txtp, fl);
m_comboIgnorePortsp = new AstTextBlock{fl,
m_comboIgnorePortsp = new AstTextBlock{fl, //
"import \"DPI-C\" function void " + m_libName
+ "_protectlib_combo_ignore"
"(\n",
false, true};
m_comboIgnorePortsp->addText(fl, "chandle handle__V\n");
txtp->addNodesp(m_comboIgnorePortsp);
txtp->addText(fl, ");\n\n");
+ "_protectlib_combo_ignore(\n", //
", ", //
");\n\n"};
m_comboIgnorePortsp->add("chandle handle__V\n");
txtp->add(m_comboIgnorePortsp);
finalComment(txtp, fl);
txtp->addText(fl, "import \"DPI-C\" function void " + m_libName
+ "_protectlib_final(chandle handle__V);\n\n");
txtp->add("import \"DPI-C\" function void " + m_libName
+ "_protectlib_final(chandle handle__V);\n\n");
// Local variables
// Avoid tracing handle, as it is not a stable value, so breaks vcddiff
// Likewise other internals aren't interesting to the user
txtp->addText(fl, "// verilator tracing_off\n");
txtp->add("// verilator tracing_off\n");
txtp->addText(fl, "chandle handle__V;\n");
txtp->addText(fl, "time last_combo_seqnum__V;\n");
if (m_hasClk) txtp->addText(fl, "time last_seq_seqnum__V;\n");
txtp->addText(fl, "\n");
txtp->add("chandle handle__V;\n");
txtp->add("time last_combo_seqnum__V;\n");
if (m_hasClk) txtp->add("time last_seq_seqnum__V;\n");
txtp->add("\n");
m_comboDeclsp = new AstTextBlock{fl};
txtp->addNodesp(m_comboDeclsp);
txtp->add(m_comboDeclsp);
m_seqDeclsp = new AstTextBlock{fl};
txtp->addNodesp(m_seqDeclsp);
txtp->add(m_seqDeclsp);
m_tmpDeclsp = new AstTextBlock{fl};
txtp->addNodesp(m_tmpDeclsp);
txtp->add(m_tmpDeclsp);
// CPP hash value
addComment(txtp, fl, "Hash value to make sure this file and the corresponding");
addComment(txtp, fl, "library agree");
m_hashValuep = new AstTextBlock{fl, "localparam int protectlib_hash__V = 32'd"};
txtp->addNodesp(m_hashValuep);
txtp->addText(fl, "\n");
m_hashValuep = new AstTextBlock{fl, "localparam int protectlib_hash__V = 32'd", "", "\n"};
txtp->add(m_hashValuep);
// Initial
txtp->addText(fl, "initial begin\n");
txtp->addText(fl, m_libName + "_protectlib_check_hash(protectlib_hash__V);\n");
txtp->addText(fl, "handle__V = " + m_libName
+ "_protectlib_create"
"($sformatf(\"%m\"));\n");
txtp->addText(fl, "end\n\n");
txtp->add("initial begin\n");
txtp->add(m_libName + "_protectlib_check_hash(protectlib_hash__V);\n");
txtp->add("handle__V = " + m_libName
+ "_protectlib_create"
"($sformatf(\"%m\"));\n");
txtp->add("end\n\n");
// Combinatorial process
addComment(txtp, fl, "Combinatorially evaluate changes to inputs");
m_comboParamsp = new AstTextBlock{fl,
"always_comb begin\n"
"last_combo_seqnum__V = "
+ m_libName + "_protectlib_combo_update(\n",
false, true};
m_comboParamsp->addText(fl, "handle__V\n");
txtp->addNodesp(m_comboParamsp);
txtp->addText(fl, ");\n");
txtp->addText(fl, "end\n\n");
txtp->add("always_comb begin\n");
m_comboParamsp = new AstTextBlock{fl, //
"last_combo_seqnum__V = " + m_libName
+ "_protectlib_combo_update(\n", //
",\n", //
"\n);\n"};
m_comboParamsp->add("handle__V");
txtp->add(m_comboParamsp);
txtp->add("end\n\n");
// Sequential process
if (m_hasClk) {
addComment(txtp, fl, "Evaluate clock edges");
m_clkSensp = new AstTextBlock{fl, "always @(", false, true};
txtp->addNodesp(m_clkSensp);
txtp->addText(fl, ") begin\n");
m_clkSensp = new AstTextBlock{fl, "always @(", " or ", ")"};
txtp->add(m_clkSensp);
txtp->add(" begin\n");
m_comboIgnoreParamsp
= new AstTextBlock{fl, m_libName + "_protectlib_combo_ignore(\n", false, true};
m_comboIgnoreParamsp->addText(fl, "handle__V\n");
txtp->addNodesp(m_comboIgnoreParamsp);
txtp->addText(fl, ");\n");
m_seqParamsp = new AstTextBlock{
fl, "last_seq_seqnum__V <= " + m_libName + "_protectlib_seq_update(\n", false,
true};
m_seqParamsp->addText(fl, "handle__V\n");
txtp->addNodesp(m_seqParamsp);
txtp->addText(fl, ");\n");
= new AstTextBlock{fl, //
m_libName + "_protectlib_combo_ignore(\n", ",\n", "\n);\n"};
m_comboIgnoreParamsp->add("handle__V");
txtp->add(m_comboIgnoreParamsp);
m_seqParamsp = new AstTextBlock{fl, //
"last_seq_seqnum__V <= " + m_libName
+ "_protectlib_seq_update(\n", //
",\n", //
"\n);\n"};
m_seqParamsp->add("handle__V");
txtp->add(m_seqParamsp);
m_nbAssignsp = new AstTextBlock{fl};
txtp->addNodesp(m_nbAssignsp);
txtp->addText(fl, "end\n\n");
txtp->add(m_nbAssignsp);
txtp->add("end\n\n");
}
// Select between combinatorial and sequential results
addComment(txtp, fl, "Select between combinatorial and sequential results");
txtp->addText(fl, "always_comb begin\n");
txtp->add("always_comb begin\n");
if (m_hasClk) {
m_seqAssignsp = new AstTextBlock{fl, "if (last_seq_seqnum__V > "
"last_combo_seqnum__V) begin\n"};
txtp->addNodesp(m_seqAssignsp);
m_comboAssignsp = new AstTextBlock{fl, "end\nelse begin\n"};
txtp->addNodesp(m_comboAssignsp);
txtp->addText(fl, "end\n");
txtp->add("if (last_seq_seqnum__V > last_combo_seqnum__V) begin\n");
m_seqAssignsp = new AstTextBlock{fl};
txtp->add(m_seqAssignsp);
txtp->add("end else begin\n");
m_comboAssignsp = new AstTextBlock{fl};
txtp->add(m_comboAssignsp);
txtp->add("end\n");
} else {
m_comboAssignsp = new AstTextBlock{fl, ""};
txtp->addNodesp(m_comboAssignsp);
m_comboAssignsp = new AstTextBlock{fl};
txtp->add(m_comboAssignsp);
}
txtp->addText(fl, "end\n\n");
txtp->add("end\n\n");
// Final
txtp->addText(fl, "final " + m_libName + "_protectlib_final(handle__V);\n\n");
txtp->add("final " + m_libName + "_protectlib_final(handle__V);\n\n");
txtp->addText(fl, "endmodule\n");
txtp->add("endmodule\n");
configSection(modp, txtp, fl);
@ -318,10 +312,10 @@ class ProtectVisitor final : public VNVisitor {
}
void castPtr(FileLine* fl, AstTextBlock* txtp) {
txtp->addText(fl, m_topName
+ "_container* const handlep__V = " // LCOV_EXCL_LINE // lcov bug
"static_cast<"
+ m_topName + "_container*>(vhandlep__V);\n");
txtp->add(m_topName
+ "_container* const handlep__V = " // LCOV_EXCL_LINE // lcov bug
"static_cast<"
+ m_topName + "_container*>(vhandlep__V);\n");
}
void createCppFile(FileLine* fl) {
@ -330,95 +324,93 @@ class ProtectVisitor final : public VNVisitor {
addComment(txtp, fl, "Wrapper functions for DPI protected library\n");
// Includes
txtp->addText(fl, "#include \"" + m_topName + ".h\"\n");
txtp->addText(fl, "#include \"verilated_dpi.h\"\n\n");
txtp->addText(fl, "#include <cstdio>\n");
txtp->addText(fl, "#include <cstdlib>\n\n");
txtp->add("#include \"" + m_topName + ".h\"\n");
txtp->add("#include \"verilated_dpi.h\"\n\n");
txtp->add("#include <cstdio>\n");
txtp->add("#include <cstdlib>\n\n");
// Verilated module plus sequence number
addComment(txtp, fl, "Container class to house verilated object and sequence number");
txtp->addText(fl, "class " + m_topName + "_container: public " + m_topName + " {\n");
txtp->addText(fl, "public:\n");
txtp->addText(fl, "long long m_seqnum;\n");
txtp->addText(fl, m_topName + "_container(const char* scopep__V):\n");
txtp->addText(fl, m_topName + "(scopep__V) {}\n");
txtp->addText(fl, "};\n\n");
txtp->add("class " + m_topName + "_container: public " + m_topName + " {\n");
txtp->add("public:\n");
txtp->add("long long m_seqnum;\n");
txtp->add(m_topName + "_container(const char* scopep__V):\n");
txtp->add(m_topName + "(scopep__V) {}\n");
txtp->add("};\n\n");
// Extern C
txtp->addText(fl, "extern \"C\" {\n\n");
txtp->add("extern \"C\" {\n\n");
// Hash check
hashComment(txtp, fl);
txtp->addText(fl, "void " + m_libName
+ "_protectlib_check_hash"
"(int protectlib_hash__V) {\n");
m_cHashValuep = new AstTextBlock{fl, "const int expected_hash__V = "};
txtp->addNodesp(m_cHashValuep);
txtp->addText(fl, /**/ "if (protectlib_hash__V != expected_hash__V) {\n");
txtp->addText(fl, /****/ "fprintf(stderr, \"%%Error: cannot use " + m_libName
+ " library, "
"Verliog (%u) and library (%u) hash values do not "
"agree\\n\", protectlib_hash__V, expected_hash__V);\n");
txtp->addText(fl, /****/ "std::exit(EXIT_FAILURE);\n");
txtp->addText(fl, /**/ "}\n");
txtp->addText(fl, "}\n\n");
txtp->add("void " + m_libName
+ "_protectlib_check_hash"
"(int protectlib_hash__V) {\n");
m_cHashValuep = new AstTextBlock{fl, "const int expected_hash__V = ", "", ""};
txtp->add(m_cHashValuep);
txtp->add(/**/ "if (protectlib_hash__V != expected_hash__V) {\n");
txtp->add(/****/ "fprintf(stderr, \"%%Error: cannot use " + m_libName
+ " library, "
"Verliog (%u) and library (%u) hash values do not "
"agree\\n\", protectlib_hash__V, expected_hash__V);\n");
txtp->add(/****/ "std::exit(EXIT_FAILURE);\n");
txtp->add(/**/ "}\n");
txtp->add("}\n\n");
// Initial
initialComment(txtp, fl);
txtp->addText(fl, "void* " + m_libName + "_protectlib_create(const char* scopep__V) {\n");
txtp->addText(fl, /**/ m_topName + "_container* const handlep__V = new " + m_topName
+ "_container{scopep__V};\n");
txtp->addText(fl, /**/ "return handlep__V;\n");
txtp->addText(fl, "}\n\n");
txtp->add("void* " + m_libName + "_protectlib_create(const char* scopep__V) {\n");
txtp->add(/**/ m_topName + "_container* const handlep__V = new " + m_topName
+ "_container{scopep__V};\n");
txtp->add(/**/ "return handlep__V;\n");
txtp->add("}\n\n");
// Updates
comboComment(txtp, fl);
m_cComboParamsp = new AstTextBlock{
fl, "long long " + m_libName + "_protectlib_combo_update(\n", false, true};
m_cComboParamsp->addText(fl, "void* vhandlep__V\n");
txtp->addNodesp(m_cComboParamsp);
txtp->addText(fl, ")\n");
m_cComboInsp = new AstTextBlock{fl, "{\n"};
fl, "long long " + m_libName + "_protectlib_combo_update(\n", ",\n", "\n) {\n"};
m_cComboParamsp->add("void* vhandlep__V");
txtp->add(m_cComboParamsp);
m_cComboInsp = new AstTextBlock{fl};
castPtr(fl, m_cComboInsp);
txtp->addNodesp(m_cComboInsp);
m_cComboOutsp = new AstTextBlock{fl, "handlep__V->eval();\n"};
txtp->addNodesp(m_cComboOutsp);
txtp->addText(fl, "return handlep__V->m_seqnum++;\n");
txtp->addText(fl, "}\n\n");
txtp->add(m_cComboInsp);
txtp->add("handlep__V->eval();\n");
m_cComboOutsp = new AstTextBlock{fl};
txtp->add(m_cComboOutsp);
txtp->add("return handlep__V->m_seqnum++;\n");
txtp->add("}\n\n");
if (m_hasClk) {
seqComment(txtp, fl);
m_cSeqParamsp = new AstTextBlock{
fl, "long long " + m_libName + "_protectlib_seq_update(\n", false, true};
m_cSeqParamsp->addText(fl, "void* vhandlep__V\n");
txtp->addNodesp(m_cSeqParamsp);
txtp->addText(fl, ")\n");
m_cSeqClksp = new AstTextBlock{fl, "{\n"};
fl, "long long " + m_libName + "_protectlib_seq_update(\n", ",\n", "\n) {\n"};
m_cSeqParamsp->add("void* vhandlep__V");
txtp->add(m_cSeqParamsp);
m_cSeqClksp = new AstTextBlock{fl};
castPtr(fl, m_cSeqClksp);
txtp->addNodesp(m_cSeqClksp);
m_cSeqOutsp = new AstTextBlock{fl, "handlep__V->eval();\n"};
txtp->addNodesp(m_cSeqOutsp);
txtp->addText(fl, "return handlep__V->m_seqnum++;\n");
txtp->addText(fl, "}\n\n");
txtp->add(m_cSeqClksp);
txtp->add("handlep__V->eval();\n");
m_cSeqOutsp = new AstTextBlock{fl};
txtp->add(m_cSeqOutsp);
txtp->add("return handlep__V->m_seqnum++;\n");
txtp->add("}\n\n");
}
comboIgnoreComment(txtp, fl);
m_cIgnoreParamsp = new AstTextBlock{
fl, "void " + m_libName + "_protectlib_combo_ignore(\n", false, true};
m_cIgnoreParamsp->addText(fl, "void* vhandlep__V\n");
txtp->addNodesp(m_cIgnoreParamsp);
txtp->addText(fl, ")\n");
txtp->addText(fl, "{ }\n\n");
fl, "void " + m_libName + "_protectlib_combo_ignore(\n", ",\n", "\n)\n{ }\n\n"};
m_cIgnoreParamsp->add("void* vhandlep__V");
txtp->add(m_cIgnoreParamsp);
// Final
finalComment(txtp, fl);
txtp->addText(fl, "void " + m_libName + "_protectlib_final(void* vhandlep__V) {\n");
txtp->add("void " + m_libName + "_protectlib_final(void* vhandlep__V) {\n");
castPtr(fl, txtp);
txtp->addText(fl, /**/ "handlep__V->final();\n");
txtp->addText(fl, /**/ "delete handlep__V;\n");
txtp->addText(fl, "}\n\n");
txtp->add(/**/ "handlep__V->final();\n");
txtp->add(/**/ "delete handlep__V;\n");
txtp->add("}\n\n");
txtp->addText(fl, "}\n");
txtp->add("}\n");
m_cfilep->tblockp(txtp);
}
@ -446,46 +438,44 @@ class ProtectVisitor final : public VNVisitor {
}
void handleClock(AstVar* varp) {
FileLine* const fl = varp->fileline();
handleInput(varp);
m_seqPortsp->addNodesp(varp->cloneTree(false));
m_seqPortsp->add(varp->cloneTree(false));
if (m_hasClk) {
m_seqParamsp->addText(fl, varp->prettyName() + "\n");
m_clkSensp->addText(fl, "posedge " + varp->prettyName() + " or negedge "
+ varp->prettyName());
const std::string pname = varp->prettyName();
m_seqParamsp->add(pname);
m_clkSensp->add("posedge " + pname + " or negedge " + pname);
}
m_cSeqParamsp->addText(fl, varp->dpiArgType(true, false) + "\n");
m_cSeqClksp->addText(fl, cInputConnection(varp));
m_cSeqParamsp->add(varp->dpiArgType(true, false));
m_cSeqClksp->add(cInputConnection(varp));
}
void handleDataInput(AstVar* varp) {
FileLine* const fl = varp->fileline();
handleInput(varp);
m_comboPortsp->addNodesp(varp->cloneTree(false));
m_comboParamsp->addText(fl, varp->prettyName() + "\n");
m_comboIgnorePortsp->addNodesp(varp->cloneTree(false));
if (m_hasClk) m_comboIgnoreParamsp->addText(fl, varp->prettyName() + "\n");
m_cComboParamsp->addText(fl, varp->dpiArgType(true, false) + "\n");
m_cComboInsp->addText(fl, cInputConnection(varp));
m_cIgnoreParamsp->addText(fl, varp->dpiArgType(true, false) + "\n");
m_comboPortsp->add(varp->cloneTree(false));
m_comboParamsp->add(varp->prettyName());
m_comboIgnorePortsp->add(varp->cloneTree(false));
if (m_hasClk) m_comboIgnoreParamsp->add(varp->prettyName());
m_cComboParamsp->add(varp->dpiArgType(true, false));
m_cComboInsp->add(cInputConnection(varp));
m_cIgnoreParamsp->add(varp->dpiArgType(true, false));
}
void handleInput(AstVar* varp) { m_modPortsp->addNodesp(varp->cloneTree(false)); }
void handleInput(AstVar* varp) { m_modPortsp->add(varp->cloneTree(false)); }
static void addLocalVariable(AstTextBlock* textp, const AstVar* varp, const char* suffix) {
AstVar* const newVarp
= new AstVar{varp->fileline(), VVarType::VAR, varp->name() + suffix, varp->dtypep()};
textp->addNodesp(newVarp);
textp->add(newVarp);
}
void handleOutput(AstVar* varp) {
FileLine* const fl = varp->fileline();
m_modPortsp->addNodesp(varp->cloneTree(false));
m_comboPortsp->addNodesp(varp->cloneTree(false));
m_comboParamsp->addText(fl, varp->prettyName() + "_combo__V\n");
void handleOutput(AstVar* const varp) {
const std::string pname = varp->prettyName();
m_modPortsp->add(varp->cloneTree(false));
m_comboPortsp->add(varp->cloneTree(false));
m_comboParamsp->add(pname + "_combo__V");
if (m_hasClk) {
m_seqPortsp->addNodesp(varp->cloneTree(false));
m_seqParamsp->addText(fl, varp->prettyName() + "_tmp__V\n");
m_seqPortsp->add(varp->cloneTree(false));
m_seqParamsp->add(pname + "_tmp__V");
}
addLocalVariable(m_comboDeclsp, varp, "_combo__V");
@ -494,20 +484,16 @@ class ProtectVisitor final : public VNVisitor {
addLocalVariable(m_seqDeclsp, varp, "_seq__V");
addLocalVariable(m_tmpDeclsp, varp, "_tmp__V");
m_nbAssignsp->addText(fl, varp->prettyName() + "_seq__V <= " + varp->prettyName()
+ "_tmp__V;\n");
m_seqAssignsp->addText(fl,
varp->prettyName() + " = " + varp->prettyName() + "_seq__V;\n");
m_nbAssignsp->add(pname + "_seq__V <= " + pname + "_tmp__V;\n");
m_seqAssignsp->add(pname + " = " + pname + "_seq__V;\n");
}
m_comboAssignsp->addText(fl,
varp->prettyName() + " = " + varp->prettyName() + "_combo__V;\n");
m_cComboParamsp->addText(fl, varp->dpiArgType(true, false) + "\n");
m_cComboOutsp->addText(fl, V3Task::assignInternalToDpi(varp, true, "", "", "handlep__V->")
+ "\n");
m_comboAssignsp->add(pname + " = " + pname + "_combo__V;\n");
m_cComboParamsp->add(varp->dpiArgType(true, false));
m_cComboOutsp->add(V3Task::assignInternalToDpi(varp, true, "", "", "handlep__V->") + "\n");
if (m_hasClk) {
m_cSeqParamsp->addText(fl, varp->dpiArgType(true, false) + "\n");
m_cSeqOutsp->addText(
fl, V3Task::assignInternalToDpi(varp, true, "", "", "handlep__V->") + "\n");
m_cSeqParamsp->add(varp->dpiArgType(true, false));
m_cSeqOutsp->add(V3Task::assignInternalToDpi(varp, true, "", "", "handlep__V->")
+ "\n");
}
}

View File

@ -947,28 +947,25 @@ class ConstraintExprVisitor final : public VNVisitor {
// Convert to plain foreach
FileLine* const fl = nodep->fileline();
AstNode* const arrayp = nodep->arrayp()->unlinkFrBack();
if (m_wantSingle) {
AstNode* const itemp = editSingle(fl, nodep->stmtsp());
AstNode* const cstmtp = new AstText{fl, "ret += \" \" + "};
cstmtp->addNext(itemp);
cstmtp->addNext(new AstText{fl, ";"});
AstNode* const exprsp = new AstText{fl, "([&]{ std::string ret;"};
exprsp->addNext(new AstBegin{
fl, "",
new AstForeach{fl, nodep->arrayp()->unlinkFrBack(), new AstCStmt{fl, cstmtp}},
true});
exprsp->addNext(
new AstText{fl, "return ret.empty() ? \"#b1\" : \"(bvand\" + ret + \")\";})()"});
AstNodeExpr* const newp = new AstCExpr{fl, exprsp};
newp->dtypeSetString();
nodep->replaceWith(new AstSFormatF{fl, "%@", false, newp});
AstNodeExpr* const itemp = editSingle(fl, nodep->stmtsp());
AstCStmt* const cstmtp = new AstCStmt{fl};
cstmtp->add("ret += \" \";\n");
cstmtp->add("ret += ");
cstmtp->add(itemp);
cstmtp->add(";");
AstCExpr* const cexprp = new AstCExpr{fl};
cexprp->dtypeSetString();
cexprp->add("([&]{\nstd::string ret;\n");
cexprp->add(new AstBegin{fl, "", new AstForeach{fl, arrayp, cstmtp}, true});
cexprp->add("return ret.empty() ? \"#b1\" : \"(bvand\" + ret + \")\";\n})()");
nodep->replaceWith(new AstSFormatF{fl, "%@", false, cexprp});
} else {
iterateAndNextNull(nodep->stmtsp());
nodep->replaceWith(
new AstBegin{fl, "",
new AstForeach{fl, nodep->arrayp()->unlinkFrBack(),
nodep->stmtsp()->unlinkFrBackWithNext()},
true});
nodep->replaceWith(new AstBegin{
fl, "", new AstForeach{fl, arrayp, nodep->stmtsp()->unlinkFrBackWithNext()},
true});
}
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
@ -1028,17 +1025,18 @@ class ConstraintExprVisitor final : public VNVisitor {
selp->user1(randArr);
AstNode* const itemp = new AstEq{fl, selp, nodep->pinsp()->unlinkFrBack()};
itemp->user1(true);
AstNode* const cstmtp = new AstText{fl, "ret += \" \" + "};
cstmtp->addNext(iterateSubtreeReturnEdits(itemp));
cstmtp->addNext(new AstText{fl, ";"});
AstNode* const exprsp = new AstText{fl, "([&]{ std::string ret;"};
exprsp->addNext(
new AstBegin{fl, "", new AstForeach{fl, arrayp, new AstCStmt{fl, cstmtp}}, true});
exprsp->addNext(
new AstText{fl, "return ret.empty() ? \"#b0\" : \"(bvor\" + ret + \")\";})()"});
AstNodeExpr* const newp = new AstCExpr{fl, exprsp};
newp->dtypeSetString();
nodep->replaceWith(new AstSFormatF{fl, "%@", false, newp});
AstCStmt* const cstmtp = new AstCStmt{fl};
cstmtp->add("ret += \" \";\n");
cstmtp->add("ret += ");
cstmtp->add(iterateSubtreeReturnEdits(itemp));
cstmtp->add(";");
AstCExpr* const cexprp = new AstCExpr{fl};
cexprp->dtypeSetString();
cexprp->add("([&]{\nstd::string ret;\n");
cexprp->add(new AstBegin{fl, "", new AstForeach{fl, arrayp, cstmtp}, true});
cexprp->add("return ret.empty() ? \"#b0\" : \"(bvor\" + ret + \")\";\n})()");
nodep->replaceWith(new AstSFormatF{fl, "%@", false, cexprp});
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return;
}
@ -1772,14 +1770,14 @@ class RandomizeVisitor final : public VNVisitor {
AstNodeExpr* newRandValue(FileLine* const fl, AstVar* const randcVarp,
AstNodeDType* const dtypep) {
if (randcVarp) {
AstVarRef* const argsp = new AstVarRef{fl, randcVarp, VAccess::READWRITE};
argsp->AstNode::addNext(new AstText{fl, ".randomize(__Vm_rng)"});
AstCExpr* const newp = new AstCExpr{fl, argsp};
newp->dtypep(dtypep);
return newp;
} else {
return new AstRandRNG{fl, dtypep};
AstCExpr* const cexprp = new AstCExpr{fl};
cexprp->add(new AstVarRef{fl, randcVarp, VAccess::READWRITE});
cexprp->add(".randomize(__Vm_rng)");
cexprp->dtypep(dtypep);
return cexprp;
}
return new AstRandRNG{fl, dtypep};
}
void addPrePostCall(AstClass* const classp, AstFunc* const funcp, const string& name) {
if (AstTask* userFuncp = VN_CAST(m_memberMap.findMember(classp, name), Task)) {
@ -2153,14 +2151,12 @@ class RandomizeVisitor final : public VNVisitor {
randomizep->addStmtsp(setupTaskRefp->makeStmt());
AstNodeModule* const genModp = VN_AS(genp->user2p(), NodeModule);
AstVarRef* const genRefp = new AstVarRef{fl, genModp, genp, VAccess::READWRITE};
AstNode* const argsp = genRefp;
argsp->addNext(new AstText{fl, ".next(__Vm_rng)"});
AstNodeExpr* const solverCallp = new AstCExpr{fl, argsp};
AstCExpr* const solverCallp = new AstCExpr{fl};
solverCallp->dtypeSetBit();
solverCallp->add(new AstVarRef{fl, genModp, genp, VAccess::READWRITE});
solverCallp->add(".next(__Vm_rng)");
beginValp = solverCallp;
if (randModeVarp) {
AstNodeModule* const randModeClassp = VN_AS(randModeVarp->user2p(), Class);
AstNodeFTask* const newp
@ -2434,11 +2430,10 @@ class RandomizeVisitor final : public VNVisitor {
}
// Call the solver and set return value
AstVarRef* const randNextp
= new AstVarRef{nodep->fileline(), localGenp, VAccess::READWRITE};
randNextp->AstNode::addNext(new AstText{nodep->fileline(), ".next(__Vm_rng)"});
AstNodeExpr* const solverCallp = new AstCExpr{nodep->fileline(), randNextp};
AstCExpr* const solverCallp = new AstCExpr{nodep->fileline()};
solverCallp->dtypeSetBit();
solverCallp->add(new AstVarRef{nodep->fileline(), localGenp, VAccess::READWRITE});
solverCallp->add(".next(__Vm_rng)");
randomizeFuncp->addStmtsp(new AstAssign{
nodep->fileline(),
new AstVarRef{nodep->fileline(), VN_AS(randomizeFuncp->fvarp(), Var), VAccess::WRITE},

View File

@ -158,18 +158,16 @@ AstNodeStmt* checkIterationLimit(AstNetlist* netlistp, const string& name, AstVa
AstNodeExpr* const condp = new AstGt{flp, counterRefp, constp};
AstIf* const ifp = new AstIf{flp, condp};
ifp->branchPred(VBranchPred::BP_UNLIKELY);
AstTextBlock* const blockp = new AstTextBlock{flp};
ifp->addThensp(blockp);
AstCStmt* const stmtp = new AstCStmt{flp};
ifp->addThensp(stmtp);
FileLine* const locp = netlistp->topModulep()->fileline();
const string& file = VIdProtect::protect(locp->filename());
const string& line = cvtToStr(locp->lineno());
const auto add = [&](const string& text) { blockp->addText(flp, text, true); };
add("#ifdef VL_DEBUG\n");
blockp->addNodesp(callVoidFunc(trigDumpp));
add("#endif\n");
add("VL_FATAL_MT(\"" + V3OutFormatter::quoteNameControls(file) + "\", " + line + ", \"\", ");
add("\"" + name + " region did not converge.\");\n");
const std::string& file = VIdProtect::protect(locp->filename());
const std::string& line = std::to_string(locp->lineno());
stmtp->add("#ifdef VL_DEBUG\n");
stmtp->add(callVoidFunc(trigDumpp));
stmtp->add("#endif\n");
stmtp->add("VL_FATAL_MT(\"" + V3OutFormatter::quoteNameControls(file) + "\", " + line
+ ", \"\", \"" + name + " region did not converge.\");");
return ifp;
}
@ -696,15 +694,14 @@ const TriggerKit createTriggers(AstNetlist* netlistp, AstCFunc* const initFuncp,
// Add a debug dumping statement for this trigger
const auto addDebug = [&](uint32_t index, const string& text = "") {
std::stringstream ss;
ss << "VL_DBG_MSGF(\" '" << name << "' region trigger index " << cvtToStr(index)
<< " is active";
ss << "VL_DBG_MSGF(\" ";
ss << "'" << name << "' region trigger index " << std::to_string(index) << " is active";
if (!text.empty()) ss << ": " << text;
ss << "\\n\");\n";
const string message{ss.str()};
ss << "\\n\");";
AstIf* const ifp = new AstIf{flp, getTrig(index)};
dumpp->addStmtsp(ifp);
ifp->addThensp(new AstText{flp, message, true});
ifp->addThensp(new AstCStmt{flp, ss.str()});
};
// Add a print for each of the extra triggers
@ -832,14 +829,13 @@ const TriggerKit createTriggers(AstNetlist* netlistp, AstCFunc* const initFuncp,
// Add a call to the dumping function if debug is enabled
{
AstTextBlock* const blockp = new AstTextBlock{flp};
funcp->addStmtsp(blockp);
const auto add = [&](const string& text) { blockp->addText(flp, text, true); };
add("#ifdef VL_DEBUG\n");
add("if (VL_UNLIKELY(vlSymsp->_vm_contextp__->debug())) {\n");
blockp->addNodesp(callVoidFunc(dumpp));
add("}\n");
add("#endif\n");
AstCStmt* const stmtp = new AstCStmt{flp};
funcp->addStmtsp(stmtp);
stmtp->add("#ifdef VL_DEBUG\n");
stmtp->add("if (VL_UNLIKELY(vlSymsp->_vm_contextp__->debug())) {\n");
stmtp->add(callVoidFunc(dumpp));
stmtp->add("}\n");
stmtp->add("#endif");
}
if (v3Global.opt.profExec()) funcp->addStmtsp(profExecSectionPop(flp));

View File

@ -166,7 +166,7 @@ TimingKit prepareTiming(AstNetlist* const netlistp) {
// Add arguments to a resume() call based on arguments in the suspending call
void addResumePins(AstCMethodHard* const resumep, AstNodeExpr* pinsp) {
AstCExpr* const exprp = VN_CAST(pinsp, CExpr);
AstText* const textp = VN_CAST(exprp->exprsp(), Text);
AstText* const textp = VN_CAST(exprp->nodesp(), Text);
if (textp) {
// The first argument, vlProcess, isn't used by any of resume() methods, skip it
if ((pinsp = VN_CAST(pinsp->nextp(), NodeExpr))) {

View File

@ -1366,7 +1366,7 @@ private:
// default
// These types are definitely not reducible
// AstCoverInc, AstFinish,
// AstRand, AstTime, AstUCFunc, AstCCall, AstCStmt, AstUCStmt
// AstRand, AstTime, AstCCall, AstCStmt*, AstCExpr*
void visit(AstNode* nodep) override {
if (jumpingOver()) return;
badNodeType(nodep);

View File

@ -382,9 +382,18 @@ class SubstVisitor final : public VNVisitor {
}
}
// Do not optimzie across user $c input
void visit(AstCExprUser* nodep) override {
m_ops = SUBST_MAX_OPS_NA;
iterateChildren(nodep);
}
void visit(AstCStmtUser* nodep) override {
m_ops = SUBST_MAX_OPS_NA;
iterateChildren(nodep);
}
void visit(AstNode* nodep) override {
++m_ops;
if (!nodep->isSubstOptimizable()) m_ops = SUBST_MAX_OPS_NA;
iterateChildren(nodep);
}

View File

@ -672,15 +672,15 @@ class TaskVisitor final : public VNVisitor {
if (needSyms) ccallp->argTypes("vlSymsp");
if (refp->taskp()->dpiContext()) {
// __Vscopep
AstScopeName* const snp = refp->scopeNamep()->unlinkFrBack();
UASSERT_OBJ(snp, refp, "Missing scoping context");
FileLine* const flp = refp->fileline();
// __Vscopep
ccallp->addArgsp(snp);
// __Vfilenamep
ccallp->addArgsp(
new AstCExpr{refp->fileline(), "\"" + refp->fileline()->filenameEsc() + "\"", 64});
ccallp->addArgsp(new AstCExpr{flp, "\"" + flp->filenameEsc() + "\"", 64});
// __Vlineno
ccallp->addArgsp(new AstConst(refp->fileline(), refp->fileline()->lineno()));
ccallp->addArgsp(new AstConst(flp, flp->lineno()));
}
// Create connections
@ -802,11 +802,10 @@ class TaskVisitor final : public VNVisitor {
// Extract a scalar from DPI temporary var that is scalar or 1D array
if (useSvVec) {
const std::string offset = std::to_string(i * widthWords);
AstCStmt* const cstmtp = new AstCStmt{flp, nullptr};
cstmtp->addExprsp(new AstText{flp, cvt});
cstmtp->addExprsp(lhsp);
cstmtp->addExprsp(new AstText{flp, ", " + rhsName + " + " + offset + ");"});
AstCStmt* const cstmtp = new AstCStmt{flp};
cstmtp->add(cvt);
cstmtp->add(lhsp);
cstmtp->add(", " + rhsName + " + " + std::to_string(i * widthWords) + ");");
stmtsp = AstNode::addNext(stmtsp, cstmtp);
} else {
const std::string elem = strides.empty() ? "" : "[" + std::to_string(i) + "]";
@ -818,120 +817,97 @@ class TaskVisitor final : public VNVisitor {
return stmtsp;
}
AstCFunc* makeDpiExportDispatcher(AstNodeFTask* nodep, AstVar* rtnvarp) {
// Create dispatch wrapper
AstCFunc* makeDpiExportDispatcher(AstNodeFTask* const nodep, AstVar* const rtnvarp) {
// Verilog name has __ conversion and other tricks, to match DPI C code, back that out
const string name = AstNode::prettyName(nodep->cname());
checkLegalCIdentifier(nodep, name);
FileLine* const flp = nodep->fileline();
const std::string cname = AstNode::prettyName(nodep->cname());
checkLegalCIdentifier(nodep, cname);
const char* const tmpSuffixp = V3Task::dpiTemporaryVarSuffix();
AstCFunc* const funcp = new AstCFunc{nodep->fileline(), name, m_scopep,
(rtnvarp ? rtnvarp->dpiArgType(true, true) : "")};
const std::string rtnType = rtnvarp ? rtnvarp->dpiArgType(true, true) : "";
// The function we are building
AstCFunc* const funcp = new AstCFunc{flp, cname, m_scopep, rtnType};
funcp->dpiExportDispatcher(true);
funcp->dpiContext(nodep->dpiContext());
funcp->dontCombine(true);
funcp->entryPoint(true);
funcp->isStatic(true);
funcp->protect(false);
funcp->cname(name);
funcp->cname(cname);
// Add DPI Export to top, since it's a global function
m_topScopep->scopep()->addBlocksp(funcp);
{ // Create dispatch wrapper
// Note this function may dispatch to myfunc on a different class.
// Thus we need to be careful not to assume a particular function layout.
//
// Func numbers must be the same for each function, even when there are
// completely different models with the same function name.
// Thus we can't just use a constant computed at Verilation time.
// We could use 64-bits of a MD5/SHA hash rather than a string here,
// but the compare is only done on first call then memoized, so
// it's not worth optimizing.
// Note this function may dispatch on a different class.
// Thus we need to be careful not to assume a particular function layout.
//
// Func numbers must be the same for each function, even when there are
// completely different models with the same function name.
// Thus we can't just use a constant computed at Verilation time.
// We could use 64-bits of a MD5/SHA hash rather than a string here,
// but the compare is only done on first call then memoized, so
// it's not worth optimizing.
// Static doesn't need save-restore as if below will re-fill proper value
funcp->addStmtsp(new AstCStmt{nodep->fileline(), "static int __Vfuncnum = -1;"});
// First time init (faster than what the compiler does if we did a singleton
funcp->addStmtsp(new AstCStmt{
nodep->fileline(),
"if (VL_UNLIKELY(__Vfuncnum == -1)) __Vfuncnum = Verilated::exportFuncNum(\""
+ nodep->cname() + "\");"});
// If the find fails, it will throw an error
funcp->addStmtsp(
new AstCStmt{nodep->fileline(),
"const VerilatedScope* const __Vscopep = Verilated::dpiScope();"});
// If dpiScope is fails and is null; the exportFind function throws and error
// If __Vcb is null the exportFind function throws and error
const string cbtype
= VIdProtect::protect(v3Global.opt.prefix() + "__Vcb_" + nodep->cname() + "_t");
funcp->addStmtsp(
new AstCStmt{nodep->fileline(),
cbtype + " __Vcb = (" + cbtype + ")(" // Can't use static_cast
+ "VerilatedScope::exportFind(__Vscopep, __Vfuncnum));"});
}
// Peramble - fetch the exproted function from the scope table
AstCStmt* const prep = new AstCStmt{flp};
funcp->addStmtsp(prep);
// Static doesn't need save-restore as if below will re-fill proper value
prep->add("static int __Vfuncnum = -1;\n");
// First time init (faster than what the compiler does if we did a singleton
prep->add("if (VL_UNLIKELY(__Vfuncnum == -1)) {\n");
prep->add("__Vfuncnum = Verilated::exportFuncNum(\"" + nodep->cname() + "\");\n");
prep->add("}\n");
// If the find fails, it will throw an error
prep->add("const VerilatedScope* const __Vscopep = Verilated::dpiScope();\n");
// If 'dpiScope()' fails and '__Vscopep' is null; the exportFind function throws an error
// If __Vcb is null the exportFind function throws and error
const std::string cbtype
= VIdProtect::protect(v3Global.opt.prefix() + "__Vcb_" + nodep->cname() + "_t");
prep->add(cbtype + " __Vcb = reinterpret_cast<" + cbtype
+ ">(VerilatedScope::exportFind(__Vscopep, __Vfuncnum));");
// Convert input/inout DPI arguments to Internal types
string args;
args += ("(" + EmitCUtil::symClassName()
+ "*)(__Vscopep->symsp())"); // Upcast w/o overhead
AstNode* argnodesp = nullptr;
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (AstVar* const portp = VN_CAST(stmtp, Var)) {
if (portp->isIO() && !portp->isFuncReturn() && portp != rtnvarp) {
// No createDpiTemp; we make a real internal variable instead
// SAME CODE BELOW
args += ", ";
if (args != "") {
argnodesp = argnodesp->addNext(new AstText{portp->fileline(), args, true});
args = "";
}
AstVarScope* const 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);
AstVarRef* const refp
= new AstVarRef{portp->fileline(), outvscp,
portp->isWritable() ? VAccess::WRITE : VAccess::READ};
argnodesp = argnodesp->addNext(refp);
if (portp->isNonOutput()) {
std::string frName
= portp->isInout() && portp->basicp()->isDpiPrimitive()
&& portp->dtypep()->skipRefp()->dimensions(false).second == 0
? "*"
: "";
frName += portp->name();
funcp->addStmtsp(createAssignDpiToInternal(outvscp, frName));
}
}
}
}
if (rtnvarp) {
AstVar* const portp = rtnvarp;
// SAME CODE ABOVE
args += ", ";
if (args != "") {
argnodesp = argnodesp->addNext(new AstText{portp->fileline(), args, true});
args = "";
}
AstVarScope* const outvscp = createFuncVar(funcp, portp->name() + tmpSuffixp, portp);
// Convert input/inout DPI arguments to Internal types, and construct the call
AstCStmt* const callp = new AstCStmt{flp};
const auto addFuncArg = [&](AstVar* portp) -> AstVarScope* {
// No createDpiTemp; we make a real internal variable instead
AstVarScope* const vscp = createFuncVar(funcp, portp->name() + tmpSuffixp, portp);
// No information exposure; is already visible in import/export func template
outvscp->varp()->protect(false);
AstVarRef* const refp = new AstVarRef{
portp->fileline(), outvscp, portp->isWritable() ? VAccess::WRITE : VAccess::READ};
argnodesp = argnodesp->addNext(refp);
vscp->varp()->protect(false);
portp->protect(false);
// Add argument to call
const VAccess access = portp->isWritable() ? VAccess::WRITE : VAccess::READ;
callp->add(", ");
callp->add(new AstVarRef{portp->fileline(), vscp, access});
return vscp;
};
// Call callback
callp->add("(*__Vcb)(");
// First argument is the Syms
callp->add("(" + EmitCUtil::symClassName() + "*)(__Vscopep->symsp())");
// Add function arguments
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
AstVar* const portp = VN_CAST(stmtp, Var);
if (!portp) continue;
if (!portp->isIO()) continue;
if (portp->isFuncReturn()) continue;
if (portp == rtnvarp) continue; // Handled below
// Add argument to call
AstVarScope* const outvscp = addFuncArg(portp);
if (!portp->isNonOutput()) continue;
// Convert input/inout arguments to dpi type
const std::string deref
= portp->isInout() //
&& portp->basicp()->isDpiPrimitive() //
&& portp->dtypep()->skipRefp()->dimensions(false).second == 0
? "*"
: "";
funcp->addStmtsp(createAssignDpiToInternal(outvscp, deref + portp->name()));
}
{ // Call the user function
// Add the variables referenced as VarRef's so that lifetime analysis
// doesn't rip up the variables on us
args += ");\n";
AstCStmt* const newp = new AstCStmt{nodep->fileline(), "(*__Vcb)("};
newp->addExprsp(argnodesp);
VL_DANGLING(argnodesp);
newp->addExprsp(new AstText{nodep->fileline(), args, true});
funcp->addStmtsp(newp);
}
// Return value argument goes last
if (rtnvarp) addFuncArg(rtnvarp);
// Close statement
callp->add(");");
// Call the user function
funcp->addStmtsp(callp);
// Convert output/inout arguments back to internal type
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (AstVar* const portp = VN_CAST(stmtp, Var)) {
@ -940,13 +916,12 @@ class TaskVisitor final : public VNVisitor {
}
}
}
// Convert return value
if (rtnvarp) {
funcp->addStmtsp(createDpiTemp(rtnvarp, ""));
funcp->addStmtsp(createAssignInternalToDpi(rtnvarp, false, tmpSuffixp, ""));
string stmt = "return " + rtnvarp->name(); // TODO use AstCReturn?
stmt += rtnvarp->basicp()->isDpiPrimitive() ? ";"s : "[0];"s;
funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
const std::string index = rtnvarp->basicp()->isDpiPrimitive() ? "" : "[0]";
funcp->addStmtsp(new AstCStmt{flp, "return " + rtnvarp->name() + index + ";"});
}
if (!makePortList(nodep, funcp)) return nullptr;
return funcp;
@ -1114,17 +1089,23 @@ class TaskVisitor final : public VNVisitor {
}
{ // Call the imported function
if (rtnvscp) { // isFunction will no longer work as we unlinked the return var
cfuncp->addStmtsp(createDpiTemp(rtnvscp->varp(), tmpSuffixp));
string stmt = rtnvscp->varp()->name();
stmt += tmpSuffixp;
stmt += rtnvscp->varp()->basicp()->isDpiPrimitive() ? " = " : "[0] = ";
cfuncp->addStmtsp(new AstText{nodep->fileline(), stmt, /* tracking: */ true});
}
AstCCall* const callp = new AstCCall{nodep->fileline(), dpiFuncp};
callp->dtypeSetVoid();
callp->argTypes(args);
cfuncp->addStmtsp(callp->makeStmt());
if (rtnvscp) {
// If it has a return value, capture it
cfuncp->addStmtsp(createDpiTemp(rtnvscp->varp(), tmpSuffixp));
const std::string sel = rtnvscp->varp()->basicp()->isDpiPrimitive() ? "" : "[0]";
AstCStmt* const cstmtp = new AstCStmt{nodep->fileline()};
cstmtp->add(rtnvscp->varp()->name() + tmpSuffixp + sel); // LHS
cstmtp->add(" = ");
cstmtp->add(callp); // RHS
cstmtp->add(";");
cfuncp->addStmtsp(cstmtp);
} else {
// Othervise just call it
cfuncp->addStmtsp(callp->makeStmt());
}
}
// Convert output/inout arguments back to internal type
@ -1515,8 +1496,8 @@ class TaskVisitor final : public VNVisitor {
lambdap->stmtsp()->addHereThisAsNext(newvarp);
lambdap->hasResult(false);
// Add return statement
AstCExpr* const exprp = new AstCExpr{nodep->fileline(), varp->name(), 0};
// Add return statement - TODO: Why not VarRef(outvscp)?
AstCExpr* const exprp = new AstCExpr{nodep->fileline(), varp->name()};
exprp->dtypeSetString();
lambdap->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
}

View File

@ -645,7 +645,7 @@ class TimingControlVisitor final : public VNVisitor {
// possibly a multiline string
std::string comment = ss.str();
std::replace(comment.begin(), comment.end(), '\n', ' ');
AstCExpr* const commentp = new AstCExpr{sentreep->fileline(), comment, 0};
AstCExpr* const commentp = new AstCExpr{sentreep->fileline(), comment};
commentp->dtypeSetString();
sentreep->user2p(commentp);
return commentp;
@ -656,10 +656,10 @@ class TimingControlVisitor final : public VNVisitor {
void addDebugInfo(AstCMethodHard* const methodp) const {
if (v3Global.opt.protectIds()) return;
FileLine* const flp = methodp->fileline();
AstCExpr* const ap = new AstCExpr{flp, '"' + flp->filenameEsc() + '"', 0};
AstCExpr* const ap = new AstCExpr{flp, '"' + flp->filenameEsc() + '"'};
ap->dtypeSetString();
methodp->addPinsp(ap);
AstCExpr* const bp = new AstCExpr{flp, cvtToStr(flp->lineno()), 0};
AstCExpr* const bp = new AstCExpr{flp, cvtToStr(flp->lineno())};
bp->dtypeSetString();
methodp->addPinsp(bp);
}
@ -673,8 +673,7 @@ class TimingControlVisitor final : public VNVisitor {
void addProcessInfo(AstCMethodHard* const methodp) const {
FileLine* const flp = methodp->fileline();
AstCExpr* const ap = new AstCExpr{
flp, m_procp && (hasFlags(m_procp, T_HAS_PROC)) ? "vlProcess" : "nullptr", 0};
ap->dtypeSetVoid();
flp, m_procp && (hasFlags(m_procp, T_HAS_PROC)) ? "vlProcess" : "nullptr"};
methodp->addPinsp(ap);
}
// Creates the fork handle type and returns it
@ -1164,10 +1163,9 @@ class TimingControlVisitor final : public VNVisitor {
}
void visit(AstWaitFork* nodep) override {
if (hasFlags(m_procp, T_HAS_PROC)) {
AstCExpr* const exprp
= new AstCExpr{nodep->fileline(), "vlProcess->completedFork()", 1};
exprp->pure(false);
AstWait* const waitp = new AstWait{nodep->fileline(), exprp, nullptr};
FileLine* const flp = nodep->fileline();
AstCExpr* const exprp = new AstCExpr{flp, "vlProcess->completedFork()", 1};
AstWait* const waitp = new AstWait{flp, exprp, nullptr};
nodep->replaceWith(waitp);
} else {
// never reached by any process; remove to avoid compilation error
@ -1186,8 +1184,7 @@ class TimingControlVisitor final : public VNVisitor {
if (constp->isZero()) {
// We have to await forever instead of simply returning in case we're deep in a
// callstack
AstCExpr* const foreverp = new AstCExpr{flp, "VlForever{}", 0};
foreverp->dtypeSetVoid(); // TODO: this is sloppy but harmless
AstCExpr* const foreverp = new AstCExpr{flp, "VlForever{}"};
AstCAwait* const awaitp = new AstCAwait{flp, foreverp};
awaitp->dtypeSetVoid();
nodep->replaceWith(awaitp->makeStmt());

View File

@ -528,19 +528,21 @@ class TraceVisitor final : public VNVisitor {
addInitStr("if (VL_UNLIKELY(!vlSymsp->__Vm_activity)) return;\n");
}
// Register function
std::string str;
AstCStmt* const cstmtp = new AstCStmt{flp};
m_regFuncp->addStmtsp(cstmtp);
if (traceType == VTraceType::CONSTANT) {
str = "tracep->addConstCb(";
cstmtp->add("tracep->addConstCb(");
cstmtp->add(new AstAddrOfCFunc{flp, funcp});
cstmtp->add(", " + std::to_string(funcNum) + ", vlSelf);");
} else if (traceType == VTraceType::FULL) {
str = "tracep->addFullCb(";
cstmtp->add("tracep->addFullCb(");
cstmtp->add(new AstAddrOfCFunc{flp, funcp});
cstmtp->add(", " + std::to_string(funcNum) + ", vlSelf);");
} else {
str = "tracep->addChgCb(";
cstmtp->add("tracep->addChgCb(");
cstmtp->add(new AstAddrOfCFunc{flp, funcp});
cstmtp->add(", " + std::to_string(funcNum) + ", vlSelf);");
}
m_regFuncp->addStmtsp(new AstText{flp, str, true});
m_regFuncp->addStmtsp(new AstAddrOfCFunc{flp, funcp});
m_regFuncp->addStmtsp(new AstText{flp, ", ", true});
m_regFuncp->addStmtsp(new AstConst{flp, funcNum});
m_regFuncp->addStmtsp(new AstText{flp, ", vlSelf);\n", true});
} else {
// Sub functions
funcp->argTypes(bufArg);
@ -739,10 +741,11 @@ class TraceVisitor final : public VNVisitor {
// Register it
{
AstNode* const argsp = new AstText{fl, "tracep->addCleanupCb(", true};
argsp->addNext(new AstAddrOfCFunc{fl, cleanupFuncp});
argsp->addNext(new AstText{fl, ", vlSelf);"});
m_regFuncp->addStmtsp(new AstCStmt{fl, argsp});
AstCStmt* const cstmtp = new AstCStmt{fl};
m_regFuncp->addStmtsp(cstmtp);
cstmtp->add("tracep->addCleanupCb(");
cstmtp->add(new AstAddrOfCFunc{fl, cleanupFuncp});
cstmtp->add(", vlSelf);");
}
// Clear global activity flag

View File

@ -1599,7 +1599,11 @@ class WidthVisitor final : public VNVisitor {
nodep->dtypeSetBit();
}
}
void visit(AstUCFunc* nodep) override {
void visit(AstCExpr* nodep) override {
// Inserted by V3Width only so we know has been resolved
userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
}
void visit(AstCExprUser* nodep) override {
// Give it the size the user wants.
if (m_vup && m_vup->prelim()) {
nodep->dtypeSetLogicUnsized(32, 1, VSigning::UNSIGNED); // We don't care
@ -3502,10 +3506,6 @@ class WidthVisitor final : public VNVisitor {
return false;
}
void visit(AstCExpr* nodep) override {
// Inserted by V3Width only so we know has been resolved
userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
}
void visit(AstCMethodHard* nodep) override {
// Never created before V3Width, so no need to redo it
UASSERT_OBJ(nodep->dtypep(), nodep, "CMETHODCALLs should have already been sized");
@ -6060,7 +6060,7 @@ class WidthVisitor final : public VNVisitor {
if (nodep->suffixp()) iterateCheckString(nodep, "suffix", nodep->suffixp(), BOTH);
if (nodep->widthp()) iterateCheckSigned32(nodep, "width", nodep->widthp(), BOTH);
}
void visit(AstUCStmt* nodep) override {
void visit(AstCStmtUser* nodep) override {
// Just let all arguments seek their natural sizes
assertAtStatement(nodep);
userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
@ -6724,8 +6724,8 @@ class WidthVisitor final : public VNVisitor {
v3Global.useRandomizeMethods(true);
AstCExpr* const newp
= new AstCExpr{nodep->fileline(), "__Vm_rng.set_randstate(", 1};
newp->addExprsp(exprp->unlinkFrBack());
newp->addExprsp(new AstText{nodep->fileline(), ")", true});
newp->add(exprp->unlinkFrBack());
newp->add(")");
newp->dtypeSetString();
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);

View File

@ -4103,8 +4103,15 @@ system_t_call<nodeStmtp>: // IEEE: system_tf_call (as task)
| yD_DUMPON parenE { $$ = new AstDumpCtl{$<fl>1, VDumpCtlType::ON}; }
| yD_DUMPON '(' expr ')' { $$ = new AstDumpCtl{$<fl>1, VDumpCtlType::ON}; DEL($3); }
//
| yD_C '(' cStrList ')' { $$ = (v3Global.opt.ignc() ? nullptr : new AstUCStmt{$1, $3}); }
| yD_SDF_ANNOTATE '(' exprEListE ')' { $$ = nullptr; $1->v3warn(SPECIFYIGN, "Ignoring unsupported: $sdf_annotate"); DEL($3); }
| yD_C '(' cStrList ')' {
AstCStmtUser* cstmtp = nullptr;
if (!v3Global.opt.ignc()) {
cstmtp = new AstCStmtUser{$1, true};
cstmtp->add($3);
}
$$ = cstmtp;
}
| yD_SDF_ANNOTATE '(' exprEListE ')' { $$ = nullptr; $1->v3warn(SPECIFYIGN, "Ignoring unsupported: $sdf_annotate"); DEL($3); }
| yD_STACKTRACE parenE { $$ = new AstStackTraceT{$1}; }
| yD_SYSTEM '(' expr ')' { $$ = new AstSystemT{$1, $3}; }
//
@ -4263,7 +4270,14 @@ system_t_call<nodeStmtp>: // IEEE: system_tf_call (as task)
system_f_call<nodeExprp>: // IEEE: system_tf_call (as func)
yaD_PLI systemDpiArgsE { $$ = new AstFuncRef{$<fl>1, *$1, $2}; VN_CAST($$, FuncRef)->pli(true); }
//
| yD_C '(' cStrList ')' { $$ = (v3Global.opt.ignc() ? nullptr : new AstUCFunc{$1, $3}); }
| yD_C '(' cStrList ')' {
AstCExprUser* cexprp = nullptr;
if (!v3Global.opt.ignc()) {
cexprp = new AstCExprUser{$1};
cexprp->add($3);
}
$$ = cexprp;
}
| yD_CAST '(' expr ',' expr ')' { $$ = new AstCastDynamic{$1, $5, $3}; }
| yD_STACKTRACE parenE { $$ = new AstStackTraceF{$1}; }
| yD_SYSTEM '(' expr ')' { $$ = new AstSystemF{$1, $3}; }
@ -5192,7 +5206,7 @@ pexprScope<nodeExprp>: // exprScope, For use by property_expr
// For $c("foo","bar") we want "bar" as a string, not a Verilog integer.
exprStrText<nodep>:
exprNoStr { $$ = $1; }
| strAsText { $$ = $1; }
| yaSTRING { $$ = new AstText{$<fl>1, GRAMMARP->textQuoted($<fl>1, *$1)}; }
;
exprTypeCompare<nodeExprp>:
@ -6080,10 +6094,6 @@ strAsIntIgnore<nodeExprp>: // strAsInt, but never matches for when expr
yaSTRING__IGNORE { $$ = nullptr; yyerror("Impossible token"); }
;
strAsText<nodep>:
yaSTRING { $$ = GRAMMARP->createTextQuoted($<fl>1, *$1); }
;
endLabelE<strp>:
/* empty */ { $$ = nullptr; $<fl>$ = nullptr; }
| ':' idAny { $$ = $2; $<fl>$ = $<fl>2; }

View File

@ -1678,18 +1678,18 @@
"valuep": [
{"type":"CVTPACKSTRING","name":"","addr":"(OZ)","loc":"d,227:18,227:24","dtypep":"(FG)",
"lhsp": [
{"type":"UCFUNC","name":"","addr":"(PZ)","loc":"d,227:26,227:28","dtypep":"UNLINKED",
"exprsp": [
{"type":"TEXT","name":"","addr":"(QZ)","loc":"d,227:29,227:32","shortText":"0"}
{"type":"CEXPRUSER","name":"","addr":"(PZ)","loc":"d,227:26,227:28","dtypep":"UNLINKED",
"nodesp": [
{"type":"TEXT","name":"","addr":"(QZ)","loc":"d,227:29,227:32","text":"0"}
]}
]}
],"attrsp": []},
{"type":"UCSTMT","name":"","addr":"(RZ)","loc":"d,229:7,229:9",
"exprsp": [
{"type":"CSTMTUSER","name":"","addr":"(RZ)","loc":"d,229:7,229:9",
"nodesp": [
{"type":"PARSEREF","name":"s","addr":"(SZ)","loc":"d,229:10,229:11","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []},
{"type":"TEXT","name":"","addr":"(TZ)","loc":"d,229:13,229:18","shortText":" = "},
{"type":"TEXT","name":"","addr":"(TZ)","loc":"d,229:13,229:18","text":" = "},
{"type":"PARSEREF","name":"m_process","addr":"(UZ)","loc":"d,229:20,229:29","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []},
{"type":"TEXT","name":"","addr":"(VZ)","loc":"d,229:31,229:47","shortText":"->randstate();"}
{"type":"TEXT","name":"","addr":"(VZ)","loc":"d,229:31,229:47","text":"->randstate();"}
]},
{"type":"RETURN","name":"","addr":"(WZ)","loc":"d,230:7,230:13",
"lhsp": [
@ -1702,12 +1702,12 @@
"childDTypep": [
{"type":"BASICDTYPE","name":"string","addr":"(AAB)","loc":"d,233:33,233:39","dtypep":"(AAB)","keyword":"string","generic":false,"rangep": []}
],"delayp": [],"valuep": [],"attrsp": []},
{"type":"UCSTMT","name":"","addr":"(BAB)","loc":"d,234:7,234:9",
"exprsp": [
{"type":"CSTMTUSER","name":"","addr":"(BAB)","loc":"d,234:7,234:9",
"nodesp": [
{"type":"PARSEREF","name":"m_process","addr":"(CAB)","loc":"d,234:10,234:19","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []},
{"type":"TEXT","name":"","addr":"(DAB)","loc":"d,234:21,234:35","shortText":"->randstate("},
{"type":"TEXT","name":"","addr":"(DAB)","loc":"d,234:21,234:35","text":"->randstate("},
{"type":"PARSEREF","name":"s","addr":"(EAB)","loc":"d,234:37,234:38","dtypep":"UNLINKED","lhsp": [],"ftaskrefp": []},
{"type":"TEXT","name":"","addr":"(FAB)","loc":"d,234:40,234:44","shortText":");"}
{"type":"TEXT","name":"","addr":"(FAB)","loc":"d,234:40,234:44","text":");"}
]}
],"scopeNamep": []}
],"extendsp": []},

File diff suppressed because it is too large Load Diff

View File

@ -540,7 +540,7 @@
<varref loc="d,63,22,63,25" name="clk" dtype_id="8"/>
<varref loc="d,63,22,63,25" name="__Vtrigprevexpr___TOP__clk__0" dtype_id="8"/>
</assign>
<textblock loc="d,11,8,11,9">
<cstmt loc="d,11,8,11,9">
<text loc="d,11,8,11,9"/>
<text loc="d,11,8,11,9"/>
<stmtexpr loc="a,0,0,0,0">
@ -548,7 +548,7 @@
</stmtexpr>
<text loc="d,11,8,11,9"/>
<text loc="d,11,8,11,9"/>
</textblock>
</cstmt>
</cfunc>
<cfunc loc="a,0,0,0,0" name="_dump_triggers__act">
<if loc="d,11,8,11,9">
@ -575,7 +575,9 @@
</cmethodhard>
</and>
<begin>
<text loc="d,11,8,11,9"/>
<cstmt loc="d,11,8,11,9">
<text loc="d,11,8,11,9"/>
</cstmt>
</begin>
</if>
</cfunc>
@ -604,7 +606,9 @@
</cmethodhard>
</and>
<begin>
<text loc="d,11,8,11,9"/>
<cstmt loc="d,11,8,11,9">
<text loc="d,11,8,11,9"/>
</cstmt>
</begin>
</if>
</cfunc>
@ -1529,15 +1533,14 @@
<varref loc="a,0,0,0,0" name="__VnbaIterCount" dtype_id="5"/>
</lt>
<begin>
<textblock loc="a,0,0,0,0">
<cstmt loc="a,0,0,0,0">
<text loc="a,0,0,0,0"/>
<stmtexpr loc="a,0,0,0,0">
<ccall loc="a,0,0,0,0" dtype_id="7" func="_dump_triggers__nba"/>
</stmtexpr>
<text loc="a,0,0,0,0"/>
<text loc="a,0,0,0,0"/>
<text loc="a,0,0,0,0"/>
</textblock>
</cstmt>
</begin>
</if>
<assign loc="d,11,8,11,9" dtype_id="5">
@ -1572,15 +1575,14 @@
<varref loc="a,0,0,0,0" name="__VactIterCount" dtype_id="5"/>
</lt>
<begin>
<textblock loc="a,0,0,0,0">
<cstmt loc="a,0,0,0,0">
<text loc="a,0,0,0,0"/>
<stmtexpr loc="a,0,0,0,0">
<ccall loc="a,0,0,0,0" dtype_id="7" func="_dump_triggers__act"/>
</stmtexpr>
<text loc="a,0,0,0,0"/>
<text loc="a,0,0,0,0"/>
<text loc="a,0,0,0,0"/>
</textblock>
</cstmt>
</begin>
</if>
<assign loc="d,11,8,11,9" dtype_id="5">