Internals: Disambiguate variable references under `with` clauses of `randomize()` methods (#5277)
This commit is contained in:
parent
54f9f4b6a9
commit
45ee949cc4
|
|
@ -1603,6 +1603,9 @@ class AstLambdaArgRef final : public AstNodeExpr {
|
||||||
// Lambda argument usage
|
// Lambda argument usage
|
||||||
// These are not AstVarRefs because we need to be able to delete/clone lambdas during
|
// These are not AstVarRefs because we need to be able to delete/clone lambdas during
|
||||||
// optimizations and AstVar's are painful to remove.
|
// optimizations and AstVar's are painful to remove.
|
||||||
|
ASTGEN_MEMBERS_AstLambdaArgRef;
|
||||||
|
|
||||||
|
private:
|
||||||
string m_name; // Name of variable
|
string m_name; // Name of variable
|
||||||
bool m_index; // Index, not value
|
bool m_index; // Index, not value
|
||||||
|
|
||||||
|
|
@ -1611,7 +1614,6 @@ public:
|
||||||
: ASTGEN_SUPER_LambdaArgRef(fl)
|
: ASTGEN_SUPER_LambdaArgRef(fl)
|
||||||
, m_name{name}
|
, m_name{name}
|
||||||
, m_index(index) {}
|
, m_index(index) {}
|
||||||
ASTGEN_MEMBERS_AstLambdaArgRef;
|
|
||||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||||
string emitVerilog() override { return name(); }
|
string emitVerilog() override { return name(); }
|
||||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||||
|
|
@ -2216,6 +2218,10 @@ public:
|
||||||
: ASTGEN_SUPER_ThisRef(fl) {
|
: ASTGEN_SUPER_ThisRef(fl) {
|
||||||
childDTypep(dtypep);
|
childDTypep(dtypep);
|
||||||
}
|
}
|
||||||
|
AstThisRef(FileLine* fl, AstClassRefDType* dtypep)
|
||||||
|
: ASTGEN_SUPER_ThisRef(fl) {
|
||||||
|
this->dtypep(dtypep);
|
||||||
|
}
|
||||||
ASTGEN_MEMBERS_AstThisRef;
|
ASTGEN_MEMBERS_AstThisRef;
|
||||||
string emitC() override { return "this"; }
|
string emitC() override { return "this"; }
|
||||||
string emitVerilog() override { return "this"; }
|
string emitVerilog() override { return "this"; }
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@
|
||||||
|
|
||||||
#include "V3LinkDot.h"
|
#include "V3LinkDot.h"
|
||||||
|
|
||||||
|
#include "V3Global.h"
|
||||||
#include "V3Graph.h"
|
#include "V3Graph.h"
|
||||||
#include "V3MemberMap.h"
|
#include "V3MemberMap.h"
|
||||||
#include "V3Parse.h"
|
#include "V3Parse.h"
|
||||||
|
|
@ -2046,6 +2047,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||||
VSymEnt* m_curSymp = nullptr; // SymEnt for current lookup point
|
VSymEnt* m_curSymp = nullptr; // SymEnt for current lookup point
|
||||||
VSymEnt* m_modSymp = nullptr; // SymEnt for current module
|
VSymEnt* m_modSymp = nullptr; // SymEnt for current module
|
||||||
VSymEnt* m_pinSymp = nullptr; // SymEnt for pin lookups
|
VSymEnt* m_pinSymp = nullptr; // SymEnt for pin lookups
|
||||||
|
VSymEnt* m_fromSymp = nullptr; // SymEnt for randomize lookups
|
||||||
const AstCell* m_cellp = nullptr; // Current cell
|
const AstCell* m_cellp = nullptr; // Current cell
|
||||||
AstNodeModule* m_modp = nullptr; // Current module
|
AstNodeModule* m_modp = nullptr; // Current module
|
||||||
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||||
|
|
@ -2465,6 +2467,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||||
UINFO(8, " " << nodep << endl);
|
UINFO(8, " " << nodep << endl);
|
||||||
const DotStates lastStates = m_ds;
|
const DotStates lastStates = m_ds;
|
||||||
const bool start = (m_ds.m_dotPos == DP_NONE); // Save, as m_dotp will be changed
|
const bool start = (m_ds.m_dotPos == DP_NONE); // Save, as m_dotp will be changed
|
||||||
|
VL_RESTORER(m_fromSymp);
|
||||||
{
|
{
|
||||||
if (start) { // Starting dot sequence
|
if (start) { // Starting dot sequence
|
||||||
if (debug() >= 9) nodep->dumpTree("- dot-in: ");
|
if (debug() >= 9) nodep->dumpTree("- dot-in: ");
|
||||||
|
|
@ -2580,6 +2583,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||||
"ParseRefs should no longer exist");
|
"ParseRefs should no longer exist");
|
||||||
const DotStates lastStates = m_ds;
|
const DotStates lastStates = m_ds;
|
||||||
const bool start = (m_ds.m_dotPos == DP_NONE); // Save, as m_dotp will be changed
|
const bool start = (m_ds.m_dotPos == DP_NONE); // Save, as m_dotp will be changed
|
||||||
|
|
||||||
if (start) {
|
if (start) {
|
||||||
m_ds.init(m_curSymp);
|
m_ds.init(m_curSymp);
|
||||||
// Note m_ds.m_dot remains nullptr; this is a reference not under a dot
|
// Note m_ds.m_dot remains nullptr; this is a reference not under a dot
|
||||||
|
|
@ -2648,12 +2652,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||||
classOrPackagep = cpackagerefp->classOrPackagep();
|
classOrPackagep = cpackagerefp->classOrPackagep();
|
||||||
UASSERT_OBJ(classOrPackagep, m_ds.m_dotp->lhsp(), "Bad package link");
|
UASSERT_OBJ(classOrPackagep, m_ds.m_dotp->lhsp(), "Bad package link");
|
||||||
if (cpackagerefp->name() == "local::") {
|
if (cpackagerefp->name() == "local::") {
|
||||||
if (m_pinSymp) {
|
m_fromSymp = nullptr;
|
||||||
m_ds.m_dotSymp = m_curSymp->fallbackp();
|
|
||||||
} else {
|
|
||||||
nodep->v3error("Illegal 'local::' outside 'randomize() with'");
|
|
||||||
m_ds.m_dotErr = true;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
m_ds.m_dotSymp = m_statep->getNodeSym(classOrPackagep);
|
m_ds.m_dotSymp = m_statep->getNodeSym(classOrPackagep);
|
||||||
}
|
}
|
||||||
|
|
@ -2675,6 +2674,19 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||||
VSymEnt* foundp;
|
VSymEnt* foundp;
|
||||||
string baddot;
|
string baddot;
|
||||||
VSymEnt* okSymp = nullptr;
|
VSymEnt* okSymp = nullptr;
|
||||||
|
if (m_fromSymp) {
|
||||||
|
foundp = m_fromSymp->findIdFlat(nodep->name());
|
||||||
|
if (foundp) {
|
||||||
|
UINFO(9, " randomize-with fromSym " << foundp->nodep() << endl);
|
||||||
|
if (m_ds.m_dotPos != DP_NONE) m_ds.m_dotPos = DP_MEMBER;
|
||||||
|
AstLambdaArgRef* const lambdaRefp
|
||||||
|
= new AstLambdaArgRef{nodep->fileline(), "item", false};
|
||||||
|
nodep->replaceWith(new AstMemberSel{nodep->fileline(), lambdaRefp,
|
||||||
|
VFlagChildDType{}, nodep->name()});
|
||||||
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (allowScope) {
|
if (allowScope) {
|
||||||
foundp = m_statep->findDotted(nodep->fileline(), m_ds.m_dotSymp, nodep->name(),
|
foundp = m_statep->findDotted(nodep->fileline(), m_ds.m_dotSymp, nodep->name(),
|
||||||
baddot, okSymp); // Maybe nullptr
|
baddot, okSymp); // Maybe nullptr
|
||||||
|
|
@ -2730,8 +2742,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (allowFTask && VN_IS(foundp->nodep(), NodeFTask)) {
|
} else if (allowFTask && VN_IS(foundp->nodep(), NodeFTask)) {
|
||||||
AstTaskRef* const taskrefp
|
AstNodeFTaskRef* taskrefp;
|
||||||
= new AstTaskRef{nodep->fileline(), nodep->name(), nullptr};
|
if (VN_IS(foundp->nodep(), Task)) {
|
||||||
|
taskrefp = new AstTaskRef{nodep->fileline(), nodep->name(), nullptr};
|
||||||
|
} else {
|
||||||
|
taskrefp = new AstFuncRef{nodep->fileline(), nodep->name(), nullptr};
|
||||||
|
}
|
||||||
nodep->replaceWith(taskrefp);
|
nodep->replaceWith(taskrefp);
|
||||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||||
if (start) m_ds = lastStates;
|
if (start) m_ds = lastStates;
|
||||||
|
|
@ -2979,6 +2995,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||||
UINFO(4, "(Backto) Link ClassOrPackageRef: " << nodep << endl);
|
UINFO(4, "(Backto) Link ClassOrPackageRef: " << nodep << endl);
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
|
||||||
|
if (nodep->name() == "local::") {
|
||||||
|
if (!m_fromSymp) {
|
||||||
|
nodep->v3error("Illegal 'local::' outside 'randomize() with'");
|
||||||
|
m_ds.m_dotErr = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
AstClass* const modClassp = VN_CAST(m_modp, Class);
|
AstClass* const modClassp = VN_CAST(m_modp, Class);
|
||||||
if (m_statep->forPrimary() && refClassp && !nodep->paramsp()
|
if (m_statep->forPrimary() && refClassp && !nodep->paramsp()
|
||||||
&& nodep->classOrPackagep()->hasGParam()
|
&& nodep->classOrPackagep()->hasGParam()
|
||||||
|
|
@ -3124,10 +3146,10 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||||
void visit(AstMethodCall* nodep) override {
|
void visit(AstMethodCall* nodep) override {
|
||||||
// Created here so should already be resolved.
|
// Created here so should already be resolved.
|
||||||
VL_RESTORER(m_ds);
|
VL_RESTORER(m_ds);
|
||||||
VL_RESTORER(m_pinSymp);
|
VL_RESTORER(m_fromSymp);
|
||||||
{
|
{
|
||||||
m_ds.init(m_curSymp);
|
m_ds.init(m_curSymp);
|
||||||
if (nodep->name() == "randomize" && VN_IS(nodep->pinsp(), With)) {
|
if (nodep->name() == "randomize" && nodep->pinsp()) {
|
||||||
const AstNodeDType* fromDtp = nodep->fromp()->dtypep();
|
const AstNodeDType* fromDtp = nodep->fromp()->dtypep();
|
||||||
if (!fromDtp) {
|
if (!fromDtp) {
|
||||||
if (const AstNodeVarRef* const varRefp = VN_CAST(nodep->fromp(), NodeVarRef)) {
|
if (const AstNodeVarRef* const varRefp = VN_CAST(nodep->fromp(), NodeVarRef)) {
|
||||||
|
|
@ -3157,8 +3179,16 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||||
nodep->v3error("'randomize() with' on a non-class-instance "
|
nodep->v3error("'randomize() with' on a non-class-instance "
|
||||||
<< fromDtp->prettyNameQ());
|
<< fromDtp->prettyNameQ());
|
||||||
else
|
else
|
||||||
m_pinSymp = m_statep->getNodeSym(classDtp->classp());
|
m_fromSymp = m_statep->getNodeSym(classDtp->classp());
|
||||||
}
|
}
|
||||||
|
AstNode* pinsp = nodep->pinsp();
|
||||||
|
if (VN_IS(pinsp, With)) {
|
||||||
|
iterate(pinsp);
|
||||||
|
pinsp = pinsp->nextp();
|
||||||
|
}
|
||||||
|
m_fromSymp = nullptr;
|
||||||
|
iterateAndNextNull(pinsp);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
|
|
@ -3191,6 +3221,8 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VL_RESTORER(m_fromSymp);
|
||||||
|
|
||||||
bool staticAccess = false;
|
bool staticAccess = false;
|
||||||
if (m_ds.m_unresolvedClass) {
|
if (m_ds.m_unresolvedClass) {
|
||||||
// Unable to link before V3Param
|
// Unable to link before V3Param
|
||||||
|
|
@ -3210,12 +3242,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||||
staticAccess = true;
|
staticAccess = true;
|
||||||
AstClassOrPackageRef* const cpackagerefp
|
AstClassOrPackageRef* const cpackagerefp
|
||||||
= VN_AS(m_ds.m_dotp->lhsp(), ClassOrPackageRef);
|
= VN_AS(m_ds.m_dotp->lhsp(), ClassOrPackageRef);
|
||||||
if (cpackagerefp->name() == "local") {
|
|
||||||
nodep->v3warn(E_UNSUPPORTED,
|
|
||||||
"Unsupported: " << AstNode::prettyNameQ(cpackagerefp->name()));
|
|
||||||
}
|
|
||||||
UASSERT_OBJ(cpackagerefp->classOrPackagep(), m_ds.m_dotp->lhsp(), "Bad package link");
|
UASSERT_OBJ(cpackagerefp->classOrPackagep(), m_ds.m_dotp->lhsp(), "Bad package link");
|
||||||
nodep->classOrPackagep(cpackagerefp->classOrPackagep());
|
if (cpackagerefp->name() == "local::") {
|
||||||
|
m_fromSymp = nullptr;
|
||||||
|
} else {
|
||||||
|
nodep->classOrPackagep(cpackagerefp->classOrPackagep());
|
||||||
|
}
|
||||||
// Class/package :: HERE function() . method_called_on_function_return_value()
|
// Class/package :: HERE function() . method_called_on_function_return_value()
|
||||||
m_ds.m_dotPos = DP_MEMBER;
|
m_ds.m_dotPos = DP_MEMBER;
|
||||||
m_ds.m_dotp = nullptr;
|
m_ds.m_dotp = nullptr;
|
||||||
|
|
@ -3284,6 +3316,24 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||||
dotSymp = m_statep->findDotted(nodep->fileline(), dotSymp, nodep->dotted(), baddot,
|
dotSymp = m_statep->findDotted(nodep->fileline(), dotSymp, nodep->dotted(), baddot,
|
||||||
okSymp); // Maybe nullptr
|
okSymp); // Maybe nullptr
|
||||||
}
|
}
|
||||||
|
if (m_fromSymp) {
|
||||||
|
VSymEnt* const foundp = m_fromSymp->findIdFlat(nodep->name());
|
||||||
|
if (foundp) {
|
||||||
|
UINFO(9, " randomize-with fromSym " << foundp->nodep() << endl);
|
||||||
|
AstNodeExpr* argsp = nullptr;
|
||||||
|
if (nodep->pinsp()) {
|
||||||
|
iterateAndNextNull(nodep->pinsp());
|
||||||
|
argsp = nodep->pinsp()->unlinkFrBackWithNext();
|
||||||
|
}
|
||||||
|
if (m_ds.m_dotPos != DP_NONE) m_ds.m_dotPos = DP_MEMBER;
|
||||||
|
AstNode* const newp = new AstMethodCall{
|
||||||
|
nodep->fileline(), new AstLambdaArgRef{nodep->fileline(), "item", false},
|
||||||
|
VFlagChildDType{}, nodep->name(), argsp};
|
||||||
|
nodep->replaceWith(newp);
|
||||||
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
VSymEnt* const foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
|
VSymEnt* const foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
|
||||||
AstNodeFTask* const taskp
|
AstNodeFTask* const taskp
|
||||||
= foundp ? VN_CAST(foundp->nodep(), NodeFTask) : nullptr; // Maybe nullptr
|
= foundp ? VN_CAST(foundp->nodep(), NodeFTask) : nullptr; // Maybe nullptr
|
||||||
|
|
@ -3524,7 +3574,6 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||||
VL_RESTORER(m_curSymp);
|
VL_RESTORER(m_curSymp);
|
||||||
{
|
{
|
||||||
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
|
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
|
||||||
if (m_pinSymp) m_curSymp->importFromClass(m_statep->symsp(), m_pinSymp);
|
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
m_ds.m_dotSymp = VL_RESTORER_PREV(m_curSymp);
|
m_ds.m_dotSymp = VL_RESTORER_PREV(m_curSymp);
|
||||||
|
|
|
||||||
|
|
@ -36,12 +36,14 @@ class LinkLValueVisitor final : public VNVisitor {
|
||||||
bool m_setContinuously = false; // Set that var has some continuous assignment
|
bool m_setContinuously = false; // Set that var has some continuous assignment
|
||||||
bool m_setStrengthSpecified = false; // Set that var has assignment with strength specified.
|
bool m_setStrengthSpecified = false; // Set that var has assignment with strength specified.
|
||||||
bool m_setForcedByCode = false; // Set that var is the target of an AstAssignForce/AstRelease
|
bool m_setForcedByCode = false; // Set that var is the target of an AstAssignForce/AstRelease
|
||||||
|
bool m_setIfRand = false; // Update VarRefs if var declared as rand
|
||||||
VAccess m_setRefLvalue; // Set VarRefs to lvalues for pin assignments
|
VAccess m_setRefLvalue; // Set VarRefs to lvalues for pin assignments
|
||||||
|
|
||||||
// VISITs
|
// VISITs
|
||||||
// Result handing
|
// Result handing
|
||||||
void visit(AstNodeVarRef* nodep) override {
|
void visit(AstNodeVarRef* nodep) override {
|
||||||
// VarRef: LValue its reference
|
// VarRef: LValue its reference
|
||||||
|
if (m_setIfRand && !(nodep->varp() && nodep->varp()->isRand())) return;
|
||||||
if (m_setRefLvalue != VAccess::NOCHANGE) nodep->access(m_setRefLvalue);
|
if (m_setRefLvalue != VAccess::NOCHANGE) nodep->access(m_setRefLvalue);
|
||||||
if (nodep->varp() && nodep->access().isWriteOrRW()) {
|
if (nodep->varp() && nodep->access().isWriteOrRW()) {
|
||||||
if (m_setContinuously) {
|
if (m_setContinuously) {
|
||||||
|
|
@ -321,6 +323,11 @@ class LinkLValueVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void visit(AstConstraint* nodep) override {
|
||||||
|
VL_RESTORER(m_setIfRand);
|
||||||
|
m_setIfRand = true;
|
||||||
|
iterateChildren(nodep);
|
||||||
|
}
|
||||||
|
|
||||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,12 @@
|
||||||
// Mark all classes that inherit from previously marked classed
|
// Mark all classes that inherit from previously marked classed
|
||||||
// Mark all classes whose instances are randomized member variables of marked classes
|
// Mark all classes whose instances are randomized member variables of marked classes
|
||||||
// Each marked class:
|
// Each marked class:
|
||||||
// define a virtual randomize() method that randomizes its random variables
|
// * define a virtual randomize() method that randomizes its random variables
|
||||||
|
// Each call to randomize():
|
||||||
|
// * define __Vrandwith### functions for randomize() calls with inline constraints and
|
||||||
|
// put then into randomized classes
|
||||||
|
// * replace calls to randomize() that use inline constraints with calls to __Vrandwith###
|
||||||
|
// functions
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
|
|
@ -37,6 +42,8 @@
|
||||||
#include "V3MemberMap.h"
|
#include "V3MemberMap.h"
|
||||||
#include "V3UniqueNames.h"
|
#include "V3UniqueNames.h"
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
|
|
@ -278,6 +285,15 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
||||||
backp = backp->backp())
|
backp = backp->backp())
|
||||||
backp->user1(true);
|
backp->user1(true);
|
||||||
}
|
}
|
||||||
|
void visit(AstMemberSel* nodep) override {
|
||||||
|
if (!m_constraintExprp) return;
|
||||||
|
if (VN_IS(nodep->fromp(), LambdaArgRef)) {
|
||||||
|
if (!nodep->varp()->isRand()) return;
|
||||||
|
for (AstNode* backp = nodep; backp != m_constraintExprp && !backp->user1();
|
||||||
|
backp = backp->backp())
|
||||||
|
backp->user1(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
void visit(AstNodeModule* nodep) override {
|
void visit(AstNodeModule* nodep) override {
|
||||||
VL_RESTORER(m_modp);
|
VL_RESTORER(m_modp);
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
|
|
@ -406,6 +422,7 @@ class ConstraintExprVisitor final : public VNVisitor {
|
||||||
// VISITORS
|
// VISITORS
|
||||||
void visit(AstNodeVarRef* nodep) override {
|
void visit(AstNodeVarRef* nodep) override {
|
||||||
AstVar* const varp = nodep->varp();
|
AstVar* const varp = nodep->varp();
|
||||||
|
AstNodeModule* const classOrPackagep = nodep->classOrPackagep();
|
||||||
const VarRandMode randMode = {.asInt = varp->user1()};
|
const VarRandMode randMode = {.asInt = varp->user1()};
|
||||||
if (!randMode.usesRandMode && editFormat(nodep)) return;
|
if (!randMode.usesRandMode && editFormat(nodep)) return;
|
||||||
|
|
||||||
|
|
@ -438,6 +455,7 @@ class ConstraintExprVisitor final : public VNVisitor {
|
||||||
AstClass* const classp = VN_AS(varp->user2p(), Class);
|
AstClass* const classp = VN_AS(varp->user2p(), Class);
|
||||||
AstVarRef* const varRefp
|
AstVarRef* const varRefp
|
||||||
= new AstVarRef{varp->fileline(), classp, varp, VAccess::WRITE};
|
= new AstVarRef{varp->fileline(), classp, varp, VAccess::WRITE};
|
||||||
|
varRefp->classOrPackagep(classOrPackagep);
|
||||||
methodp->addPinsp(varRefp);
|
methodp->addPinsp(varRefp);
|
||||||
methodp->addPinsp(new AstConst{varp->dtypep()->fileline(), AstConst::Unsized64{},
|
methodp->addPinsp(new AstConst{varp->dtypep()->fileline(), AstConst::Unsized64{},
|
||||||
(size_t)varp->width()});
|
(size_t)varp->width()});
|
||||||
|
|
@ -603,13 +621,92 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename TreeNodeType>
|
class ClassLookupHelper final {
|
||||||
class CaptureFrame final {
|
const std::set<AstNodeModule*>
|
||||||
TreeNodeType* m_treep; // Original tree
|
m_visibleModules; // Modules directly reachale from our lookup point
|
||||||
|
std::map<AstNode*, AstNodeModule*>
|
||||||
|
m_classMap; // Memoized mapping between nodes and modules that define them
|
||||||
|
|
||||||
|
// BFS search
|
||||||
|
template <typename Action>
|
||||||
|
static void foreachSuperClass(AstClass* classp, Action action) {
|
||||||
|
std::queue<AstClass*> classes;
|
||||||
|
classes.push(classp);
|
||||||
|
while (!classes.empty()) {
|
||||||
|
classp = classes.front();
|
||||||
|
classes.pop();
|
||||||
|
for (AstClassExtends* extendsp = classp->extendsp(); extendsp;
|
||||||
|
extendsp = VN_AS(extendsp->nextp(), ClassExtends)) {
|
||||||
|
AstClass* const superClassp
|
||||||
|
= VN_AS(extendsp->childDTypep(), ClassRefDType)->classp();
|
||||||
|
action(superClassp);
|
||||||
|
classes.push(superClassp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::set<AstNodeModule*> initVisibleModules(AstClass* classp) {
|
||||||
|
std::set<AstNodeModule*> visibleModules = {classp};
|
||||||
|
std::vector<AstNodeModule*> symLookupOrder = {classp};
|
||||||
|
foreachSuperClass(classp,
|
||||||
|
[&](AstClass* superclassp) { visibleModules.emplace(superclassp); });
|
||||||
|
return visibleModules;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool moduleInClassHierarchy(AstNodeModule* modp) const {
|
||||||
|
return m_visibleModules.count(modp) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNodeModule* findDeclaringModule(AstNode* nodep, bool classHierarchyOnly = true) {
|
||||||
|
auto it = m_classMap.find(nodep);
|
||||||
|
if (it != m_classMap.end()) return it->second;
|
||||||
|
for (AstNode* backp = nodep; backp; backp = backp->backp()) {
|
||||||
|
AstNodeModule* const modp = VN_CAST(backp, NodeModule);
|
||||||
|
if (modp) {
|
||||||
|
m_classMap.emplace(nodep, modp);
|
||||||
|
if (classHierarchyOnly)
|
||||||
|
UASSERT_OBJ(moduleInClassHierarchy(modp), nodep,
|
||||||
|
"Node does not belong to class");
|
||||||
|
return modp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassLookupHelper(AstClass* classp)
|
||||||
|
: m_visibleModules(initVisibleModules(classp)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class CaptureMode : uint8_t {
|
||||||
|
CAP_NO = 0x0,
|
||||||
|
CAP_VALUE = 0x01,
|
||||||
|
CAP_THIS = 0x02,
|
||||||
|
CAP_F_SET_CLASSORPACKAGEP = 0x4,
|
||||||
|
CAP_F_XREF = 0x8
|
||||||
|
};
|
||||||
|
CaptureMode operator|(CaptureMode a, CaptureMode b) {
|
||||||
|
return static_cast<CaptureMode>(static_cast<uint8_t>(a) | static_cast<uint8_t>(b));
|
||||||
|
}
|
||||||
|
CaptureMode operator&(CaptureMode a, CaptureMode b) {
|
||||||
|
return static_cast<CaptureMode>(static_cast<uint8_t>(a) & static_cast<uint8_t>(b));
|
||||||
|
}
|
||||||
|
CaptureMode mode(CaptureMode a) { return a & static_cast<CaptureMode>(0x3); }
|
||||||
|
bool hasFlags(CaptureMode a, CaptureMode flags) {
|
||||||
|
return ((static_cast<uint8_t>(a) & 0xc & static_cast<uint8_t>(flags))
|
||||||
|
== static_cast<uint8_t>(flags));
|
||||||
|
}
|
||||||
|
|
||||||
|
class CaptureVisitor final : public VNVisitor {
|
||||||
AstArg* m_argsp; // Original references turned into arguments
|
AstArg* m_argsp; // Original references turned into arguments
|
||||||
AstNodeModule* m_myModulep; // Module for which static references will stay uncaptured.
|
AstNodeModule* m_callerp; // Module of the outer context (for capturing `this`)
|
||||||
// Map original var nodes to their clones
|
AstClass* m_classp; // Module of inner context (for symbol lookup)
|
||||||
std::map<const AstVar*, AstVar*> m_varCloneMap;
|
std::map<const AstVar*, AstVar*> m_varCloneMap; // Map original var nodes to their clones
|
||||||
|
std::set<AstNode*> m_ignore; // Nodes to ignore for capturing
|
||||||
|
ClassLookupHelper m_lookup; // Util for class lookup
|
||||||
|
AstVar* m_thisp = nullptr; // Variable for outer context's object, if necessary
|
||||||
|
|
||||||
|
// METHODS
|
||||||
|
|
||||||
bool captureVariable(FileLine* const fileline, AstNodeVarRef* varrefp, AstVar*& varp) {
|
bool captureVariable(FileLine* const fileline, AstNodeVarRef* varrefp, AstVar*& varp) {
|
||||||
auto it = m_varCloneMap.find(varrefp->varp());
|
auto it = m_varCloneMap.find(varrefp->varp());
|
||||||
|
|
@ -629,57 +726,30 @@ class CaptureFrame final {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Action>
|
template <typename NodeT>
|
||||||
static void foreachSuperClass(AstClass* classp, Action action) {
|
void fixupClassOrPackage(AstNode* memberp, NodeT refp) {
|
||||||
for (AstClassExtends* extendsp = classp->extendsp(); extendsp;
|
AstNodeModule* const declClassp = m_lookup.findDeclaringModule(memberp, false);
|
||||||
extendsp = VN_AS(extendsp->nextp(), ClassExtends)) {
|
if (declClassp != m_classp) refp->classOrPackagep(declClassp);
|
||||||
AstClass* const superclassp = VN_AS(extendsp->childDTypep(), ClassRefDType)->classp();
|
|
||||||
action(superclassp);
|
|
||||||
foreachSuperClass(superclassp, action);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
template <typename NodeT>
|
||||||
explicit CaptureFrame(TreeNodeType* const nodep, AstNodeModule* const myModulep,
|
bool isReferenceToInnerMember(NodeT nodep) {
|
||||||
const bool clone = true, VNRelinker* const linkerp = nullptr)
|
return VN_IS(nodep->fromp(), LambdaArgRef);
|
||||||
: m_treep(clone ? nodep->cloneTree(true) : nodep->unlinkFrBackWithNext(linkerp))
|
|
||||||
, m_argsp(nullptr)
|
|
||||||
, m_myModulep(myModulep) {
|
|
||||||
|
|
||||||
std::set<AstNodeModule*> visibleModules = {myModulep};
|
|
||||||
if (AstClass* classp = VN_CAST(m_myModulep, Class)) {
|
|
||||||
foreachSuperClass(classp,
|
|
||||||
[&](AstClass* superclassp) { visibleModules.emplace(superclassp); });
|
|
||||||
}
|
|
||||||
m_treep->foreachAndNext([&](AstNodeVarRef* varrefp) {
|
|
||||||
UASSERT_OBJ(varrefp->varp(), varrefp, "Variable unlinked");
|
|
||||||
if (!varrefp->varp()->isFuncLocal() && !VN_IS(varrefp, VarXRef)
|
|
||||||
&& (visibleModules.count(varrefp->classOrPackagep())))
|
|
||||||
return;
|
|
||||||
AstVar* newVarp;
|
|
||||||
bool newCapture = captureVariable(varrefp->fileline(), varrefp, newVarp /*ref*/);
|
|
||||||
AstNodeVarRef* const newVarRefp = newCapture ? varrefp->cloneTree(false) : nullptr;
|
|
||||||
if (!varrefp->varp()->lifetime().isStatic() || varrefp->classOrPackagep()) {
|
|
||||||
// Keeping classOrPackagep will cause a broken link after inlining
|
|
||||||
varrefp->classOrPackagep(nullptr); // AstScope will figure this out
|
|
||||||
}
|
|
||||||
varrefp->varp(newVarp);
|
|
||||||
if (!newCapture) return;
|
|
||||||
if (VN_IS(varrefp, VarXRef)) {
|
|
||||||
AstVarRef* const notXVarRefp
|
|
||||||
= new AstVarRef{varrefp->fileline(), newVarp, VAccess::READ};
|
|
||||||
notXVarRefp->classOrPackagep(varrefp->classOrPackagep());
|
|
||||||
varrefp->replaceWith(notXVarRefp);
|
|
||||||
varrefp->deleteTree();
|
|
||||||
varrefp = notXVarRefp;
|
|
||||||
}
|
|
||||||
m_argsp = AstNode::addNext(m_argsp, new AstArg{varrefp->fileline(), "", newVarRefp});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PUBLIC METHODS
|
AstVar* importThisp(FileLine* fl) {
|
||||||
|
if (!m_thisp) {
|
||||||
TreeNodeType* getTree() const { return m_treep; }
|
AstClassRefDType* const refDTypep
|
||||||
|
= new AstClassRefDType{fl, VN_AS(m_callerp, Class), nullptr};
|
||||||
|
v3Global.rootp()->typeTablep()->addTypesp(refDTypep);
|
||||||
|
m_thisp = new AstVar{fl, VVarType::BLOCKTEMP, "__Vthis", refDTypep};
|
||||||
|
m_thisp->funcLocal(true);
|
||||||
|
m_thisp->lifetime(VLifetime::AUTOMATIC);
|
||||||
|
m_thisp->direction(VDirection::INPUT);
|
||||||
|
m_argsp = AstNode::addNext(m_argsp, new AstArg{fl, "", new AstThisRef{fl, refDTypep}});
|
||||||
|
}
|
||||||
|
return m_thisp;
|
||||||
|
}
|
||||||
|
|
||||||
AstVar* getVar(AstVar* const varp) const {
|
AstVar* getVar(AstVar* const varp) const {
|
||||||
const auto it = m_varCloneMap.find(varp);
|
const auto it = m_varCloneMap.find(varp);
|
||||||
|
|
@ -687,7 +757,164 @@ public:
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CaptureMode getVarRefCaptureMode(AstNodeVarRef* varRefp) {
|
||||||
|
AstNodeModule* const modp = m_lookup.findDeclaringModule(varRefp->varp(), false);
|
||||||
|
|
||||||
|
const bool callerIsClass = VN_IS(m_callerp, Class);
|
||||||
|
const bool refIsXref = VN_IS(varRefp, VarXRef);
|
||||||
|
const bool varIsFuncLocal = varRefp->varp()->isFuncLocal();
|
||||||
|
const bool varHasAutomaticLifetime = varRefp->varp()->lifetime().isAutomatic();
|
||||||
|
const bool varIsDeclaredInCaller = modp == m_callerp;
|
||||||
|
const bool varIsFieldOfCaller = modp ? m_lookup.moduleInClassHierarchy(modp) : false;
|
||||||
|
|
||||||
|
if (refIsXref) return CaptureMode::CAP_VALUE | CaptureMode::CAP_F_XREF;
|
||||||
|
if (varIsFuncLocal && varHasAutomaticLifetime) return CaptureMode::CAP_VALUE;
|
||||||
|
// Static var in function (will not be inlined, because it's in class)
|
||||||
|
if (callerIsClass && varIsFuncLocal) return CaptureMode::CAP_VALUE;
|
||||||
|
if (callerIsClass && varIsDeclaredInCaller) return CaptureMode::CAP_THIS;
|
||||||
|
if (callerIsClass && varIsFieldOfCaller) return CaptureMode::CAP_THIS;
|
||||||
|
UASSERT_OBJ(!callerIsClass, varRefp, "Invalid reference?");
|
||||||
|
return CaptureMode::CAP_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void captureRefByValue(AstNodeVarRef* nodep, CaptureMode capModeFlags) {
|
||||||
|
AstVar* newVarp;
|
||||||
|
bool newCapture = captureVariable(nodep->fileline(), nodep, newVarp /*ref*/);
|
||||||
|
AstNodeVarRef* const newVarRefp = newCapture ? nodep->cloneTree(false) : nullptr;
|
||||||
|
if (!hasFlags(capModeFlags, CaptureMode::CAP_F_SET_CLASSORPACKAGEP)) {
|
||||||
|
// Keeping classOrPackagep will cause a broken link after inlining
|
||||||
|
nodep->classOrPackagep(nullptr); // AstScope will figure this out
|
||||||
|
}
|
||||||
|
nodep->varp(newVarp);
|
||||||
|
if (!newCapture) return;
|
||||||
|
if (hasFlags(capModeFlags, CaptureMode::CAP_F_XREF)) {
|
||||||
|
AstVarRef* const notXVarRefp
|
||||||
|
= new AstVarRef{nodep->fileline(), newVarp, VAccess::READ};
|
||||||
|
notXVarRefp->classOrPackagep(nodep->classOrPackagep());
|
||||||
|
nodep->replaceWith(notXVarRefp);
|
||||||
|
nodep->deleteTree();
|
||||||
|
nodep = notXVarRefp;
|
||||||
|
}
|
||||||
|
m_ignore.emplace(nodep);
|
||||||
|
m_argsp = AstNode::addNext(m_argsp, new AstArg{nodep->fileline(), "", newVarRefp});
|
||||||
|
}
|
||||||
|
|
||||||
|
void captureRefByThis(AstNodeVarRef* nodep, CaptureMode capModeFlags) {
|
||||||
|
AstVar* const thisp = importThisp(nodep->fileline());
|
||||||
|
AstVarRef* const thisRefp = new AstVarRef{nodep->fileline(), thisp, nodep->access()};
|
||||||
|
m_ignore.emplace(thisRefp);
|
||||||
|
AstMemberSel* const memberSelp
|
||||||
|
= new AstMemberSel(nodep->fileline(), thisRefp, nodep->varp());
|
||||||
|
nodep->replaceWith(memberSelp);
|
||||||
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
|
m_ignore.emplace(memberSelp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// VISITORS
|
||||||
|
|
||||||
|
void visit(AstNodeVarRef* nodep) override {
|
||||||
|
if (m_ignore.count(nodep)) return;
|
||||||
|
m_ignore.emplace(nodep);
|
||||||
|
UASSERT_OBJ(nodep->varp(), nodep, "Variable unlinked");
|
||||||
|
CaptureMode capMode = getVarRefCaptureMode(nodep);
|
||||||
|
if (mode(capMode) == CaptureMode::CAP_NO) return;
|
||||||
|
if (mode(capMode) == CaptureMode::CAP_VALUE) captureRefByValue(nodep, capMode);
|
||||||
|
if (mode(capMode) == CaptureMode::CAP_THIS) captureRefByThis(nodep, capMode);
|
||||||
|
}
|
||||||
|
void visit(AstNodeFTaskRef* nodep) override {
|
||||||
|
if (m_ignore.count(nodep)) {
|
||||||
|
iterateChildren(nodep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_ignore.emplace(nodep);
|
||||||
|
UASSERT_OBJ(nodep->taskp(), nodep, "Task unlinked");
|
||||||
|
// We assume that constraint targets are not referenced this way.
|
||||||
|
if (VN_IS(nodep, MethodCall) || VN_IS(nodep, New)) {
|
||||||
|
m_ignore.emplace(nodep);
|
||||||
|
iterateChildren(nodep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AstClass* classp = VN_CAST(m_lookup.findDeclaringModule(nodep->taskp(), false), Class);
|
||||||
|
if ((classp == m_callerp) && VN_IS(m_callerp, Class)) {
|
||||||
|
AstNodeExpr* const pinsp = nodep->pinsp();
|
||||||
|
if (pinsp) pinsp->unlinkFrBack();
|
||||||
|
AstVar* const thisp = importThisp(nodep->fileline());
|
||||||
|
AstVarRef* const thisRefp = new AstVarRef{
|
||||||
|
nodep->fileline(), thisp, nodep->isPure() ? VAccess::READ : VAccess::READWRITE};
|
||||||
|
m_ignore.emplace(thisRefp);
|
||||||
|
AstMethodCall* const methodCallp
|
||||||
|
= new AstMethodCall{nodep->fileline(), thisRefp, thisp->name(), pinsp};
|
||||||
|
methodCallp->taskp(nodep->taskp());
|
||||||
|
methodCallp->dtypep(nodep->dtypep());
|
||||||
|
nodep->replaceWith(methodCallp);
|
||||||
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
|
m_ignore.emplace(methodCallp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void visit(AstMemberSel* nodep) override {
|
||||||
|
if (!isReferenceToInnerMember(nodep)) {
|
||||||
|
iterateChildren(nodep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AstVarRef* const varRefp
|
||||||
|
= new AstVarRef(nodep->fileline(), nodep->varp(), nodep->access());
|
||||||
|
fixupClassOrPackage(nodep->varp(), varRefp);
|
||||||
|
varRefp->user1(nodep->user1());
|
||||||
|
nodep->replaceWith(varRefp);
|
||||||
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
|
m_ignore.emplace(varRefp);
|
||||||
|
}
|
||||||
|
void visit(AstMethodCall* nodep) override {
|
||||||
|
if (!isReferenceToInnerMember(nodep) || m_ignore.count(nodep)) {
|
||||||
|
iterateChildren(nodep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AstNodeExpr* const pinsp
|
||||||
|
= nodep->pinsp() ? nodep->pinsp()->unlinkFrBackWithNext() : nullptr;
|
||||||
|
AstNodeFTaskRef* taskRefp = nullptr;
|
||||||
|
if (VN_IS(nodep->taskp(), Task))
|
||||||
|
taskRefp = new AstTaskRef{nodep->fileline(), nodep->name(), pinsp};
|
||||||
|
else if (VN_IS(nodep->taskp(), Func))
|
||||||
|
taskRefp = new AstFuncRef{nodep->fileline(), nodep->name(), pinsp};
|
||||||
|
UASSERT_OBJ(taskRefp, nodep, "Node needs to point to regular method");
|
||||||
|
taskRefp->taskp(nodep->taskp());
|
||||||
|
taskRefp->dtypep(nodep->dtypep());
|
||||||
|
fixupClassOrPackage(nodep->taskp(), taskRefp);
|
||||||
|
taskRefp->user1(nodep->user1());
|
||||||
|
nodep->replaceWith(taskRefp);
|
||||||
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
|
m_ignore.emplace(taskRefp);
|
||||||
|
}
|
||||||
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CaptureVisitor(AstNode* const nodep, AstNodeModule* callerp, AstClass* const classp,
|
||||||
|
const bool clone = true, VNRelinker* const linkerp = nullptr)
|
||||||
|
: m_argsp(nullptr)
|
||||||
|
, m_callerp(callerp)
|
||||||
|
, m_classp(classp)
|
||||||
|
, m_lookup(classp) {
|
||||||
|
iterateAndNextNull(nodep);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PUBLIC METHODS
|
||||||
|
|
||||||
AstArg* getArgs() const { return m_argsp; }
|
AstArg* getArgs() const { return m_argsp; }
|
||||||
|
|
||||||
|
void addFunctionArguments(AstNodeFTask* funcp) const {
|
||||||
|
for (AstArg* argp = getArgs(); argp; argp = VN_AS(argp->nextp(), Arg)) {
|
||||||
|
if (AstNodeVarRef* varrefp = VN_CAST(argp->exprp(), NodeVarRef)) {
|
||||||
|
if ((varrefp->classOrPackagep() == m_callerp) || VN_IS(varrefp, VarXRef)) {
|
||||||
|
// Keeping classOrPackagep will cause a broken link after inlining
|
||||||
|
varrefp->classOrPackagep(nullptr);
|
||||||
|
}
|
||||||
|
funcp->addStmtsp(getVar(varrefp->varp()));
|
||||||
|
} else {
|
||||||
|
UASSERT_OBJ(VN_IS(argp->exprp(), ThisRef), argp->exprp(), "Wrong arg expression");
|
||||||
|
funcp->addStmtsp(m_thisp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//######################################################################
|
//######################################################################
|
||||||
|
|
@ -1315,23 +1542,14 @@ class RandomizeVisitor final : public VNVisitor {
|
||||||
classp->findBasicDType(VBasicDTypeKwd::RANDOM_GENERATOR)};
|
classp->findBasicDType(VBasicDTypeKwd::RANDOM_GENERATOR)};
|
||||||
localGenp->funcLocal(true);
|
localGenp->funcLocal(true);
|
||||||
|
|
||||||
AstFunc* const randomizeFuncp
|
AstFunc* const randomizeFuncp = V3Randomize::newRandomizeFunc(
|
||||||
= V3Randomize::newRandomizeFunc(m_memberMap, classp, m_inlineUniqueNames.get(nodep));
|
m_memberMap, classp, m_inlineUniqueNames.get(nodep), false);
|
||||||
|
|
||||||
// Detach the expression and prepare variable copies
|
// Detach the expression and prepare variable copies
|
||||||
const CaptureFrame<AstNode> captured{withp->exprp(), classp, false};
|
const CaptureVisitor captured{withp->exprp(), m_modp, classp, false};
|
||||||
UASSERT_OBJ(VN_IS(captured.getTree(), ConstraintExpr), captured.getTree(),
|
|
||||||
"Wrong expr type");
|
|
||||||
|
|
||||||
// Add function arguments
|
// Add function arguments
|
||||||
for (AstArg* argp = captured.getArgs(); argp; argp = VN_AS(argp->nextp(), Arg)) {
|
captured.addFunctionArguments(randomizeFuncp);
|
||||||
AstNodeVarRef* varrefp = VN_AS(argp->exprp(), NodeVarRef);
|
|
||||||
if ((varrefp->classOrPackagep() == m_modp) || VN_IS(varrefp, VarXRef)) {
|
|
||||||
// Keeping classOrPackagep will cause a broken link after inlining
|
|
||||||
varrefp->classOrPackagep(nullptr);
|
|
||||||
}
|
|
||||||
randomizeFuncp->addStmtsp(captured.getVar(varrefp->varp()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add constraints clearing code
|
// Add constraints clearing code
|
||||||
if (classGenp) {
|
if (classGenp) {
|
||||||
|
|
@ -1366,9 +1584,12 @@ class RandomizeVisitor final : public VNVisitor {
|
||||||
if (!classGenp && randModeVarp) addSetRandMode(randomizeFuncp, localGenp, randModeVarp);
|
if (!classGenp && randModeVarp) addSetRandMode(randomizeFuncp, localGenp, randModeVarp);
|
||||||
|
|
||||||
// Generate constraint setup code and a hardcoded call to the solver
|
// Generate constraint setup code and a hardcoded call to the solver
|
||||||
randomizeFuncp->addStmtsp(captured.getTree());
|
AstNode* const capturedTreep = withp->exprp()->unlinkFrBackWithNext();
|
||||||
ConstraintExprVisitor{m_memberMap, captured.getTree(), randomizeFuncp, localGenp,
|
randomizeFuncp->addStmtsp(capturedTreep);
|
||||||
randModeVarp};
|
{
|
||||||
|
ConstraintExprVisitor{m_memberMap, capturedTreep, randomizeFuncp, localGenp,
|
||||||
|
randModeVarp};
|
||||||
|
}
|
||||||
|
|
||||||
// Call the solver and set return value
|
// Call the solver and set return value
|
||||||
AstVarRef* const randNextp
|
AstVarRef* const randNextp
|
||||||
|
|
@ -1423,7 +1644,7 @@ void V3Randomize::randomizeNetlist(AstNetlist* nodep) {
|
||||||
}
|
}
|
||||||
|
|
||||||
AstFunc* V3Randomize::newRandomizeFunc(VMemberMap& memberMap, AstClass* nodep,
|
AstFunc* V3Randomize::newRandomizeFunc(VMemberMap& memberMap, AstClass* nodep,
|
||||||
const std::string& name) {
|
const std::string& name, bool allowVirtual) {
|
||||||
AstFunc* funcp = VN_AS(memberMap.findMember(nodep, name), Func);
|
AstFunc* funcp = VN_AS(memberMap.findMember(nodep, name), Func);
|
||||||
if (!funcp) {
|
if (!funcp) {
|
||||||
v3Global.useRandomizeMethods(true);
|
v3Global.useRandomizeMethods(true);
|
||||||
|
|
@ -1438,7 +1659,7 @@ AstFunc* V3Randomize::newRandomizeFunc(VMemberMap& memberMap, AstClass* nodep,
|
||||||
funcp = new AstFunc{nodep->fileline(), name, nullptr, fvarp};
|
funcp = new AstFunc{nodep->fileline(), name, nullptr, fvarp};
|
||||||
funcp->dtypep(dtypep);
|
funcp->dtypep(dtypep);
|
||||||
funcp->classMethod(true);
|
funcp->classMethod(true);
|
||||||
funcp->isVirtual(nodep->isExtended());
|
funcp->isVirtual(allowVirtual && nodep->isExtended());
|
||||||
nodep->addMembersp(funcp);
|
nodep->addMembersp(funcp);
|
||||||
memberMap.insert(nodep, funcp);
|
memberMap.insert(nodep, funcp);
|
||||||
AstClass* const basep = nodep->baseMostClassp();
|
AstClass* const basep = nodep->baseMostClassp();
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,8 @@ public:
|
||||||
static void randomizeNetlist(AstNetlist* nodep) VL_MT_DISABLED;
|
static void randomizeNetlist(AstNetlist* nodep) VL_MT_DISABLED;
|
||||||
|
|
||||||
static AstFunc* newRandomizeFunc(VMemberMap& memberMap, AstClass* nodep,
|
static AstFunc* newRandomizeFunc(VMemberMap& memberMap, AstClass* nodep,
|
||||||
const std::string& name = "randomize") VL_MT_DISABLED;
|
const std::string& name = "randomize",
|
||||||
|
bool allowVirtual = true) VL_MT_DISABLED;
|
||||||
static AstFunc* newSRandomFunc(VMemberMap& memberMap, AstClass* nodep) VL_MT_DISABLED;
|
static AstFunc* newSRandomFunc(VMemberMap& memberMap, AstClass* nodep) VL_MT_DISABLED;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -221,6 +221,7 @@ class WidthVisitor final : public VNVisitor {
|
||||||
const AstWith* m_withp = nullptr; // Current 'with' statement
|
const AstWith* m_withp = nullptr; // Current 'with' statement
|
||||||
const AstFunc* m_funcp = nullptr; // Current function
|
const AstFunc* m_funcp = nullptr; // Current function
|
||||||
const AstAttrOf* m_attrp = nullptr; // Current attribute
|
const AstAttrOf* m_attrp = nullptr; // Current attribute
|
||||||
|
const AstNodeExpr* m_randomizeFromp = nullptr; // Current randomize method call fromp
|
||||||
const bool m_paramsOnly; // Computing parameter value; limit operation
|
const bool m_paramsOnly; // Computing parameter value; limit operation
|
||||||
const bool m_doGenerate; // Do errors later inside generate statement
|
const bool m_doGenerate; // Do errors later inside generate statement
|
||||||
int m_dtTables = 0; // Number of created data type tables
|
int m_dtTables = 0; // Number of created data type tables
|
||||||
|
|
@ -2993,6 +2994,8 @@ class WidthVisitor final : public VNVisitor {
|
||||||
nodep->dtypep(foundp->dtypep());
|
nodep->dtypep(foundp->dtypep());
|
||||||
nodep->varp(varp);
|
nodep->varp(varp);
|
||||||
nodep->didWidth(true);
|
nodep->didWidth(true);
|
||||||
|
if (nodep->fromp()->sameTree(m_randomizeFromp) && varp->isRand()) // null-safe
|
||||||
|
V3LinkLValue::linkLValueSet(nodep);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (AstEnumItemRef* const adfoundp = VN_CAST(foundp, EnumItemRef)) {
|
if (AstEnumItemRef* const adfoundp = VN_CAST(foundp, EnumItemRef)) {
|
||||||
|
|
@ -3791,6 +3794,8 @@ class WidthVisitor final : public VNVisitor {
|
||||||
AstClass* const first_classp = adtypep->classp();
|
AstClass* const first_classp = adtypep->classp();
|
||||||
AstWith* withp = nullptr;
|
AstWith* withp = nullptr;
|
||||||
if (nodep->name() == "randomize") {
|
if (nodep->name() == "randomize") {
|
||||||
|
VL_RESTORER(m_randomizeFromp);
|
||||||
|
m_randomizeFromp = nodep->fromp();
|
||||||
withp = methodWithArgument(nodep, false, false, adtypep->findVoidDType(),
|
withp = methodWithArgument(nodep, false, false, adtypep->findVoidDType(),
|
||||||
adtypep->findBitDType(), adtypep);
|
adtypep->findBitDType(), adtypep);
|
||||||
methodOkArguments(nodep, 0, 0);
|
methodOkArguments(nodep, 0, 0);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
%Error: t/t_package_local_bad.v:9:23: Illegal 'local::' outside 'randomize() with'
|
%Error: t/t_package_local_bad.v:9:16: Illegal 'local::' outside 'randomize() with'
|
||||||
9 | $display(local::x);
|
9 | $display(local::x);
|
||||||
| ^
|
| ^~~~~
|
||||||
%Error: t/t_package_local_bad.v:9:23: Can't find definition of scope/variable/func: 'x'
|
%Error: t/t_package_local_bad.v:9:23: Can't find definition of scope/variable/func: 'x'
|
||||||
9 | $display(local::x);
|
9 | $display(local::x);
|
||||||
| ^
|
| ^
|
||||||
|
|
|
||||||
|
|
@ -26,14 +26,6 @@ class Boo;
|
||||||
int unsigned boo;
|
int unsigned boo;
|
||||||
endclass
|
endclass
|
||||||
|
|
||||||
class Boo2;
|
|
||||||
function new();
|
|
||||||
boo = 6;
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
int unsigned boo;
|
|
||||||
endclass
|
|
||||||
|
|
||||||
class Foo extends Boo;
|
class Foo extends Boo;
|
||||||
rand int unsigned a;
|
rand int unsigned a;
|
||||||
rand int unsigned b;
|
rand int unsigned b;
|
||||||
|
|
@ -52,7 +44,7 @@ endclass
|
||||||
// Current AstWith representation makes VARs of caller indistinguishable from VARs of randomized
|
// Current AstWith representation makes VARs of caller indistinguishable from VARs of randomized
|
||||||
// object if both the caller and callee are the same module, but different instances.
|
// object if both the caller and callee are the same module, but different instances.
|
||||||
// That's why for the purpose of this test, the caller derives a different class
|
// That's why for the purpose of this test, the caller derives a different class
|
||||||
class Bar extends Boo2;
|
class Bar extends Boo;
|
||||||
// Give the local variables a different scope by defining the functino under Bar
|
// Give the local variables a different scope by defining the functino under Bar
|
||||||
static function bit test_local_constrdep(Foo foo, int c);
|
static function bit test_local_constrdep(Foo foo, int c);
|
||||||
return foo.randomize() with { a <= c; a > 1; x % a == 0; } == 1;
|
return foo.randomize() with { a <= c; a > 1; x % a == 0; } == 1;
|
||||||
|
|
@ -60,10 +52,17 @@ class Bar extends Boo2;
|
||||||
|
|
||||||
function bit test_capture_of_callers_derived_var(Foo foo);
|
function bit test_capture_of_callers_derived_var(Foo foo);
|
||||||
boo = 4;
|
boo = 4;
|
||||||
|
foo.a = 3;
|
||||||
return (foo.randomize() with { a == local::boo; } == 1) && (foo.a == 4);
|
return (foo.randomize() with { a == local::boo; } == 1) && (foo.a == 4);
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
static function bit test_capture_of_callees_derived_var(Foo foo);
|
static function bit test_capture_of_callees_derived_var(Foo foo);
|
||||||
|
foo.a = 5;
|
||||||
|
return (foo.randomize() with { a == boo; } == 1) && (foo.a == 6);
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
static function bit test_capture_of_local_qualifier(Foo foo);
|
||||||
|
foo.a = 5;
|
||||||
return (foo.randomize() with { a == boo; } == 1) && (foo.a == 6);
|
return (foo.randomize() with { a == boo; } == 1) && (foo.a == 6);
|
||||||
endfunction
|
endfunction
|
||||||
endclass
|
endclass
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2019 by Wilson Snyder. This program is free software; you
|
||||||
|
# can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
|
if (!$Self->have_solver) {
|
||||||
|
skip("No constraint solver installed");
|
||||||
|
} else {
|
||||||
|
compile(
|
||||||
|
);
|
||||||
|
|
||||||
|
execute(
|
||||||
|
check_finished => 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2024 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
class c1;
|
||||||
|
rand int c1_f;
|
||||||
|
endclass
|
||||||
|
class c2;
|
||||||
|
rand int c2_f;
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class Cls;
|
||||||
|
rand int x;
|
||||||
|
rand enum {
|
||||||
|
ONE_Y,
|
||||||
|
TWO_Y
|
||||||
|
} y;
|
||||||
|
virtual function int get_x();
|
||||||
|
return x;
|
||||||
|
endfunction
|
||||||
|
endclass
|
||||||
|
class SubA extends Cls;
|
||||||
|
c1 e = new;
|
||||||
|
rand enum {
|
||||||
|
AMBIG,
|
||||||
|
ONE_A,
|
||||||
|
TWO_A
|
||||||
|
} en;
|
||||||
|
function c1 get_c();
|
||||||
|
return e;
|
||||||
|
endfunction
|
||||||
|
function int op(int v);
|
||||||
|
return v + 1;
|
||||||
|
endfunction
|
||||||
|
endclass
|
||||||
|
class SubB extends Cls;
|
||||||
|
c2 e = new;
|
||||||
|
rand enum {
|
||||||
|
AMBIG,
|
||||||
|
ONE_B,
|
||||||
|
TWO_B
|
||||||
|
} en;
|
||||||
|
SubA f = new;
|
||||||
|
function c2 get_c();
|
||||||
|
return e;
|
||||||
|
endfunction
|
||||||
|
function int op(int v);
|
||||||
|
return v - 1;
|
||||||
|
endfunction
|
||||||
|
function int doit;
|
||||||
|
// access ambiguous names so width complains if we miss something
|
||||||
|
doit = 1;
|
||||||
|
|
||||||
|
f.x = 4;
|
||||||
|
x = 5;
|
||||||
|
doit = f.randomize() with { x == local::x; };
|
||||||
|
if (f.x != x) $stop;
|
||||||
|
|
||||||
|
doit &= f.randomize() with { e.c1_f == local::e.c2_f; };
|
||||||
|
doit &= f.randomize() with { get_x() == local::get_x(); };
|
||||||
|
doit &= f.randomize() with { get_c().c1_f == local::get_c().c2_f; };
|
||||||
|
doit &= f.randomize() with { (get_c).c1_f == (local::get_c).c2_f; };
|
||||||
|
|
||||||
|
f.y = ONE_Y;
|
||||||
|
y = TWO_Y;
|
||||||
|
doit &= f.randomize() with { y == local::y; };
|
||||||
|
if (f.y != y) $stop;
|
||||||
|
|
||||||
|
f.en = SubA::ONE_A;
|
||||||
|
doit &= f.randomize() with { en == AMBIG; };
|
||||||
|
if (doit != 1) $stop;
|
||||||
|
if (f.en != SubA::AMBIG) $stop;
|
||||||
|
|
||||||
|
f.en = SubA::ONE_A;
|
||||||
|
doit &= f.randomize() with { en == ONE_A; };
|
||||||
|
doit &= f.randomize() with { local::en == local::AMBIG; };
|
||||||
|
en = ONE_B;
|
||||||
|
doit &= f.randomize() with { local::en == ONE_B; };
|
||||||
|
|
||||||
|
doit &= f.randomize() with { x == local::op(op(0)); };
|
||||||
|
if (f.x != 0) $stop;
|
||||||
|
doit &= f.randomize() with { x == op(local::op(1)); };
|
||||||
|
if (f.x != 1) $stop;
|
||||||
|
doit &= f.randomize() with { x == local::op(op(local::op(op(0)))); };
|
||||||
|
if (f.x != 0) $stop;
|
||||||
|
doit &= f.randomize() with { x == op(local::op(op(local::op(1)))); };
|
||||||
|
if (f.x != 1) $stop;
|
||||||
|
endfunction
|
||||||
|
endclass
|
||||||
|
|
||||||
|
module t (/*AUTOARG*/);
|
||||||
|
SubB obj = new;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
if (obj.doit != 1) $stop;
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue