mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 01:54:01 +02:00
+3
-1
@@ -2374,7 +2374,9 @@ bool AstNodeDType::isLiteralType() const VL_MT_STABLE {
|
||||
if (const auto* const dtypep = VN_CAST(skipRefp(), BasicDType)) {
|
||||
return dtypep->keyword().isLiteralType();
|
||||
} else if (const auto* const dtypep = VN_CAST(skipRefp(), UnpackArrayDType)) {
|
||||
return dtypep->basicp()->isLiteralType();
|
||||
// basicp() is null for e.g. an array of unpacked structs, which is not literal
|
||||
const AstBasicDType* const basicp = dtypep->basicp();
|
||||
return basicp && basicp->isLiteralType();
|
||||
} else if (const auto* const dtypep = VN_CAST(skipRefp(), StructDType)) {
|
||||
// Currently all structs are packed, later this can be expanded to
|
||||
// 'forall members _.isLiteralType()'
|
||||
|
||||
+11
-5
@@ -421,13 +421,19 @@ class EmitCSyms final : EmitCBaseVisitorConst {
|
||||
stmt += protect("__Vscopep_" + svd.m_scopeName);
|
||||
stmt += needsEntSize ? "->varInsertSized(\"" : "->varInsert(\"";
|
||||
stmt += V3OutFormatter::quoteNameControls(prettyName) + '"';
|
||||
stmt += ", &(";
|
||||
stmt += VIdProtect::protectIf(scopep->nameDotless(), scopep->protect());
|
||||
stmt += ".";
|
||||
stmt += cName;
|
||||
stmt += "), false, ";
|
||||
const std::string varName
|
||||
= VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + "." + cName;
|
||||
// A parameter is emitted 'static const', so its members are const too and
|
||||
// need the same cast the whole-parameter insert uses.
|
||||
if (svd.m_varp->isParam()) {
|
||||
stmt += ", const_cast<void*>(static_cast<const void*>(&(";
|
||||
stmt += varName;
|
||||
stmt += "))), true, ";
|
||||
} else {
|
||||
stmt += ", &(";
|
||||
stmt += varName;
|
||||
stmt += "), false, ";
|
||||
}
|
||||
const std::string entSize
|
||||
= needsEntSize
|
||||
? "sizeof(" + varName + ") / " + std::to_string(getUnpackedElements(dtypep))
|
||||
|
||||
+71
-12
@@ -1030,9 +1030,11 @@ public:
|
||||
static bool checkIfClassOrPackage(const VSymEnt* const symp) {
|
||||
if (VN_IS(symp->nodep(), Class) || VN_IS(symp->nodep(), Package)) return true;
|
||||
|
||||
// Helper: check if a RefDType might resolve to a class later
|
||||
const auto checkUnresolvedRef = [](const AstRefDType* refp) -> bool {
|
||||
return refp && !refp->typeofp() && !refp->classOrPackageOpp();
|
||||
// Helper: check if a RefDType might resolve to a class later.
|
||||
const auto isPotentialClassRef = [](const AstRefDType* refp) -> bool {
|
||||
return refp && !refp->typeofp()
|
||||
&& (!refp->classOrPackageOpp()
|
||||
|| VN_IS(refp->classOrPackageOpp(), ClassOrPackageRef));
|
||||
};
|
||||
|
||||
// Helper: allow types that can represent a class/package handle or an unresolved ref.
|
||||
@@ -1046,7 +1048,7 @@ public:
|
||||
const AstNodeDType* dtypep = typedefp->subDTypep();
|
||||
if (!dtypep) dtypep = typedefp->childDTypep();
|
||||
if (VN_IS(dtypep, ClassRefDType)) return true;
|
||||
if (checkUnresolvedRef(VN_CAST(dtypep, RefDType))) return true;
|
||||
if (isPotentialClassRef(VN_CAST(dtypep, RefDType))) return true;
|
||||
} else if (const AstParamTypeDType* const paramTypep
|
||||
= VN_CAST(symp->nodep(), ParamTypeDType)) {
|
||||
// Before V3Param the declared default is in childDTypep (possibly
|
||||
@@ -1058,8 +1060,9 @@ public:
|
||||
childp = reqp->lhsp();
|
||||
}
|
||||
const AstNode* const checkp = childp ? childp : paramTypep->skipRefp();
|
||||
if (isValidTypeNode(checkp)) return true;
|
||||
if (checkUnresolvedRef(VN_CAST(checkp, RefDType))) return true;
|
||||
if (isValidTypeNode(checkp) || isPotentialClassRef(VN_CAST(checkp, RefDType))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -4850,7 +4853,8 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
VL_RESTORER_COPY(m_ds);
|
||||
VL_RESTORER(m_pinSymp);
|
||||
|
||||
if (!nodep->classOrPackageSkipp() && nodep->name() != "local::") {
|
||||
if (!nodep->classOrPackageSkipp() && !nodep->classOrPackageNodep()
|
||||
&& nodep->name() != "local::") {
|
||||
const bool deferIfUnresolved = m_statep->forPrimary() && m_insideClassExtParam;
|
||||
m_statep->resolveClassOrPackage(m_ds.m_dotSymp, nodep, m_ds.m_dotPos != DP_PACKAGE,
|
||||
false, ":: reference", deferIfUnresolved);
|
||||
@@ -5998,6 +6002,54 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
}
|
||||
m_ds.m_dotSymp = VL_RESTORER_PREV(m_curSymp);
|
||||
}
|
||||
|
||||
// Collect the ClassOrPackageRefs in a chained scope operand in source order.
|
||||
// The grammar builds a scope operand only from packageClassScopeItem, which
|
||||
// always yields a ClassOrPackageRef, nested in Dots, so nothing else appears.
|
||||
static void collectScopeRefs(AstNode* nodep, std::vector<AstClassOrPackageRef*>& refps) {
|
||||
if (AstClassOrPackageRef* const refp = VN_CAST(nodep, ClassOrPackageRef)) {
|
||||
refps.push_back(refp);
|
||||
return;
|
||||
}
|
||||
AstDot* const dotp = VN_CAST(nodep, Dot);
|
||||
UASSERT_OBJ(dotp, nodep, "Non-scope node in package/class scope operand");
|
||||
collectScopeRefs(dotp->lhsp(), refps);
|
||||
collectScopeRefs(dotp->rhsp(), refps);
|
||||
}
|
||||
|
||||
// Resolve a chained class-scope operand (`pkg::outer::inner` in
|
||||
// `pkg::outer::inner::t`) and reduce it to its innermost ClassOrPackageRef.
|
||||
// Returns false, having reported, if a segment could not be resolved.
|
||||
bool reduceScopeDot(AstRefDType* nodep, AstDot* scopeDotp) {
|
||||
std::vector<AstClassOrPackageRef*> refps;
|
||||
collectScopeRefs(scopeDotp, refps);
|
||||
|
||||
VSymEnt* scopeSymp = m_ds.m_dotSymp;
|
||||
for (size_t i = 0; i < refps.size(); ++i) {
|
||||
AstClassOrPackageRef* const refp = refps[i];
|
||||
if (!refp->classOrPackageSkipp() && !refp->classOrPackageNodep()
|
||||
&& !m_statep->resolveClassOrPackage(scopeSymp, refp, i == 0, false,
|
||||
"class/package reference")) {
|
||||
return false; // Error already reported
|
||||
}
|
||||
if (i + 1 == refps.size()) break;
|
||||
// A middle segment that resolved to a node with no reachable module
|
||||
// (e.g. a typedef alias of a not-yet-specialized parameterized class)
|
||||
// gives nothing to look the next segment up in.
|
||||
AstNodeModule* const modp = refp->classOrPackageSkipp();
|
||||
if (!modp) {
|
||||
refp->v3warn(E_UNSUPPORTED, "Unsupported: Multiple '::' package/class reference");
|
||||
return false;
|
||||
}
|
||||
scopeSymp = m_statep->getNodeSym(modp);
|
||||
}
|
||||
AstClassOrPackageRef* const innerp = refps.back();
|
||||
innerp->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(scopeDotp->unlinkFrBack()), scopeDotp);
|
||||
nodep->classOrPackageOpp(innerp);
|
||||
return true;
|
||||
}
|
||||
|
||||
void visit(AstRefDType* nodep) override {
|
||||
|
||||
if (auto* const typeOfp = nodep->typeofp()) {
|
||||
@@ -6064,7 +6116,16 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
}
|
||||
LINKDOT_VISIT_START();
|
||||
UINFO(5, indent() << "visit " << nodep);
|
||||
if (AstNode* const cpackagep = nodep->classOrPackageOpp()) {
|
||||
if (nodep->classOrPackageOpp()) {
|
||||
// Resolve a chained scope (`pkg::cls::t`, `pkg::cls#(P)::t`) from outside in.
|
||||
// On failure reduceScopeDot has reported, so stop before using the operand.
|
||||
if (AstDot* const scopeDotp = VN_CAST(nodep->classOrPackageOpp(), Dot)) {
|
||||
if (!reduceScopeDot(nodep, scopeDotp)) return;
|
||||
}
|
||||
// Re-read: reduceScopeDot may have replaced the operand
|
||||
AstNode* const cpackagep = nodep->classOrPackageOpp();
|
||||
UASSERT_OBJ(!VN_IS(cpackagep, Dot), cpackagep,
|
||||
"Package/class scope operand should be reduced by now");
|
||||
if (AstClassOrPackageRef* const cpackagerefp = VN_CAST(cpackagep, ClassOrPackageRef)) {
|
||||
iterate(cpackagerefp);
|
||||
const AstClass* const clsp = VN_CAST(cpackagerefp->classOrPackageNodep(), Class);
|
||||
@@ -6083,7 +6144,8 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
}
|
||||
|
||||
const bool doDefaultTypedef = !(m_resolvingTypedef && m_statep->forPrimary());
|
||||
if (!cpackagerefp->classOrPackageSkipp(doDefaultTypedef)) {
|
||||
if (!cpackagerefp->classOrPackageSkipp(doDefaultTypedef)
|
||||
&& !cpackagerefp->classOrPackageNodep()) {
|
||||
VSymEnt* const foundp = m_statep->resolveClassOrPackage(
|
||||
m_ds.m_dotSymp, cpackagerefp, true, false, "class/package reference");
|
||||
if (!foundp) return;
|
||||
@@ -6105,9 +6167,6 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
<< "'\n"
|
||||
<< cpackagerefp->warnMore() + "... Suggest '.' instead of '::'");
|
||||
}
|
||||
} else {
|
||||
cpackagep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Multiple '::' package/class reference");
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(cpackagep->unlinkFrBack()), cpackagep);
|
||||
}
|
||||
|
||||
+258
-60
@@ -341,6 +341,10 @@ class ParamProcessor final {
|
||||
return st;
|
||||
}
|
||||
|
||||
static bool isAggregateParamValue(const AstNode* nodep) {
|
||||
return VN_IS(nodep, InitArray) || VN_IS(nodep, ConsPackUOrStruct);
|
||||
}
|
||||
|
||||
static string paramValueString(const AstNode* nodep) {
|
||||
if (const AstRefDType* const refp = VN_CAST(nodep, RefDType)) {
|
||||
nodep = refp->skipRefToNonRefp();
|
||||
@@ -387,6 +391,14 @@ class ParamProcessor final {
|
||||
key += ",";
|
||||
}
|
||||
key += "}";
|
||||
} else if (const AstConsPackUOrStruct* const structp = VN_CAST(nodep, ConsPackUOrStruct)) {
|
||||
key += "{";
|
||||
for (const AstConsPackMember* memberp = structp->membersp(); memberp;
|
||||
memberp = VN_AS(memberp->nextp(), ConsPackMember)) {
|
||||
key += paramValueString(memberp->rhsp());
|
||||
key += ",";
|
||||
}
|
||||
key += "}";
|
||||
} else if (const AstClassRefDType* const classRefp = VN_CAST(nodep, ClassRefDType)) {
|
||||
// For parameterized class types, use the original class name (without specialization
|
||||
// suffix) plus the actual type parameter values. This ensures equivalent class types
|
||||
@@ -1196,15 +1208,105 @@ class ParamProcessor final {
|
||||
}
|
||||
}
|
||||
|
||||
// Fold a param/lparam member's value to a Const in place. Resolves any
|
||||
// class::member Dots in the value first (including those buried behind
|
||||
// VarRefs to sibling lparams that are not yet const, e.g. `one = base`
|
||||
// where `base = inner_a::v`), resolving siblings deepest-first so the
|
||||
// whole chain constifies. `inProgress` guards against reference cycles.
|
||||
void constifyMemberValue(AstVar* varp, std::set<AstVar*>& inProgress) {
|
||||
if (!varp->valuep() || VN_IS(varp->valuep(), Const)) return;
|
||||
if (!inProgress.insert(varp).second) {
|
||||
// Re-entered while still resolving varp: its value transitively
|
||||
// references itself. Report and replace the value with a Const, as
|
||||
// otherwise the unresolved Dot reaches V3Width as an untyped node.
|
||||
varp->v3error("Variable's initial value is circular: " << varp->prettyNameQ());
|
||||
varp->valuep()->unlinkFrBack()->deleteTree();
|
||||
varp->valuep(new AstConst{varp->fileline(), AstConst::Signed32{}, 0});
|
||||
return;
|
||||
}
|
||||
// First resolve sibling lparams this value references, so their Dots
|
||||
// are folded before we inline them here.
|
||||
std::set<AstVar*> siblings;
|
||||
varp->valuep()->foreach([&](const AstVarRef* refp) {
|
||||
AstVar* const refVarp = refp->varp();
|
||||
if (refVarp && refVarp->isParam() && refVarp->valuep()
|
||||
&& !VN_IS(refVarp->valuep(), Const)) {
|
||||
siblings.insert(refVarp);
|
||||
}
|
||||
});
|
||||
for (AstVar* const sibp : siblings) constifyMemberValue(sibp, inProgress);
|
||||
// Resolve class-scoped references and any member selections they expose.
|
||||
resolveDeferredDotsReachableFrom(varp->valuep(), m_modp);
|
||||
V3Const::constifyParamsEdit(varp);
|
||||
inProgress.erase(varp);
|
||||
}
|
||||
|
||||
// Return the member name on a deferred Dot RHS, including a selected member.
|
||||
static AstParseRef* memberParseRef(AstNode* nodep) {
|
||||
if (AstParseRef* const refp = VN_CAST(nodep, ParseRef)) return refp;
|
||||
AstNodePreSel* const selp = VN_CAST(nodep, NodePreSel);
|
||||
return selp ? VN_CAST(selp->fromp(), ParseRef) : nullptr;
|
||||
}
|
||||
|
||||
// Replace a deferred Dot with valuep, preserving any select on the member name.
|
||||
static void replaceMemberDot(AstDot* dotp, AstParseRef* memberRefp, AstNodeExpr* valuep) {
|
||||
if (memberRefp == dotp->rhsp()) {
|
||||
dotp->replaceWith(valuep);
|
||||
} else {
|
||||
AstNodePreSel* const selp = VN_AS(dotp->rhsp(), NodePreSel);
|
||||
if (AstAttrOf* const attrp = selp->attrp()) {
|
||||
if (AstNode* const oldFromp = attrp->fromp()) {
|
||||
oldFromp->replaceWith(valuep->cloneTree(false));
|
||||
VL_DO_DANGLING(oldFromp->deleteTree(), oldFromp);
|
||||
}
|
||||
}
|
||||
memberRefp->replaceWith(valuep);
|
||||
VL_DO_DANGLING(memberRefp->deleteTree(), memberRefp);
|
||||
dotp->replaceWith(dotp->rhsp()->unlinkFrBack());
|
||||
}
|
||||
VL_DO_DANGLING(dotp->deleteTree(), dotp);
|
||||
}
|
||||
|
||||
// True for an expression produced while lowering a deferred struct-member chain.
|
||||
static bool isDeferredMemberBase(const AstNodeExpr* nodep) {
|
||||
if (VN_IS(nodep, MemberSel)) return true;
|
||||
if (const AstNodePreSel* const selp = VN_CAST(nodep, NodePreSel)) {
|
||||
const AstNodeDType* const fromDTypep = selp->fromp()->dtypep();
|
||||
const AstNodeArrayDType* const arrayDTypep
|
||||
= fromDTypep ? VN_CAST(fromDTypep->skipRefOrNullp(), NodeArrayDType) : nullptr;
|
||||
if (arrayDTypep) {
|
||||
const AstNodeDType* const elemDTypep = arrayDTypep->subDTypep();
|
||||
return elemDTypep && VN_IS(elemDTypep->skipRefOrNullp(), NodeUOrStructDType);
|
||||
}
|
||||
return isDeferredMemberBase(selp->fromp());
|
||||
}
|
||||
const AstNodeDType* const dtypep = nodep->dtypep();
|
||||
return dtypep && VN_IS(dtypep->skipRefOrNullp(), NodeUOrStructDType);
|
||||
}
|
||||
|
||||
// Lower a Dot exposed after its inner class::member reference was substituted.
|
||||
static void resolveMemberDot(AstDot* dotp) {
|
||||
AstNodeExpr* const lhsp = VN_CAST(dotp->lhsp(), NodeExpr);
|
||||
AstParseRef* const memberRefp = memberParseRef(dotp->rhsp());
|
||||
if (!lhsp || !memberRefp || !isDeferredMemberBase(lhsp)) return;
|
||||
AstMemberSel* const newp = new AstMemberSel{memberRefp->fileline(), lhsp->unlinkFrBack(),
|
||||
VFlagChildDType{}, memberRefp->name()};
|
||||
replaceMemberDot(dotp, memberRefp, newp);
|
||||
}
|
||||
|
||||
// Helper to resolve DOT to RefDType for class type references.
|
||||
// If the class is parameterized and not yet specialized, specialize it first.
|
||||
// This handles cases like: iface #(param_class#(value)::typedef_name)
|
||||
void resolveDotToTypedef(AstNode* exprp) {
|
||||
AstDot* const dotp = VN_CAST(exprp, Dot);
|
||||
if (!dotp) return;
|
||||
UINFO(9, "Resolve deferred Dot: " << dotp);
|
||||
AstClassOrPackageRef* const classRefp = VN_CAST(dotp->lhsp(), ClassOrPackageRef);
|
||||
if (!classRefp) return;
|
||||
AstParseRef* const parseRefp = VN_CAST(dotp->rhsp(), ParseRef);
|
||||
if (!classRefp) {
|
||||
resolveMemberDot(dotp);
|
||||
return;
|
||||
}
|
||||
AstParseRef* const parseRefp = memberParseRef(dotp->rhsp());
|
||||
if (!parseRefp) return;
|
||||
|
||||
const AstClass* lhsClassp = VN_CAST(classRefp->classOrPackageSkipp(), Class);
|
||||
@@ -1248,37 +1350,37 @@ class ParamProcessor final {
|
||||
if (!lhsClassp) return;
|
||||
|
||||
AstNode* const memberp = m_memberMap.findMember(lhsClassp, parseRefp->name());
|
||||
UINFO(9, "Resolve deferred class member: " << parseRefp->name() << " -> " << memberp);
|
||||
if (AstTypedef* const tdefp = VN_CAST(memberp, Typedef)) {
|
||||
if (parseRefp != dotp->rhsp()) return;
|
||||
AstRefDType* const refp = new AstRefDType{dotp->fileline(), tdefp->name()};
|
||||
refp->typedefp(tdefp);
|
||||
dotp->replaceWith(refp);
|
||||
VL_DO_DANGLING(dotp->deleteTree(), dotp);
|
||||
} else if (AstVar* const varp = VN_CAST(memberp, Var)) {
|
||||
// Param/lparam member: substitute its constant value so the caller's constify can
|
||||
// succeed.
|
||||
if (varp->isParam() && varp->valuep()) {
|
||||
if (!VN_IS(varp->valuep(), Const)) V3Const::constifyParamsEdit(varp);
|
||||
if (AstConst* const constp = VN_CAST(varp->valuep(), Const)) {
|
||||
dotp->replaceWith(constp->cloneTree(false));
|
||||
VL_DO_DANGLING(dotp->deleteTree(), dotp);
|
||||
}
|
||||
}
|
||||
substituteParamMember(dotp, parseRefp, varp);
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve an unresolved RefDType whose classOrPackageOp targets a parameterized
|
||||
// class or a typedef alias of one. Specializes the class and links the typedef.
|
||||
void resolveParamClassRefDType(AstNodeDType* dtypep) {
|
||||
// Recurse into struct/union members for buried RefDTypes
|
||||
if (AstNodeUOrStructDType* const sup = VN_CAST(dtypep, NodeUOrStructDType)) {
|
||||
for (AstMemberDType* memp = sup->membersp(); memp;
|
||||
memp = VN_AS(memp->nextp(), MemberDType)) {
|
||||
resolveParamClassRefDType(memp->subDTypep());
|
||||
}
|
||||
return;
|
||||
// Substitute a class param/lparam member's constant value for dotp. The
|
||||
// reverse-order deferred-Dot walk lowers any outer fields to MemberSels.
|
||||
void substituteParamMember(AstDot* const dotp, AstParseRef* const memberRefp,
|
||||
AstVar* const varp) {
|
||||
if (!varp->isParam() || !varp->valuep()) return;
|
||||
if (!VN_IS(varp->valuep(), Const)) {
|
||||
std::set<AstVar*> inProgress;
|
||||
constifyMemberValue(varp, inProgress);
|
||||
}
|
||||
AstRefDType* const refp = dtypep ? VN_CAST(dtypep, RefDType) : nullptr;
|
||||
if (!refp) return;
|
||||
AstConst* const constp = VN_CAST(varp->valuep(), Const);
|
||||
if (!constp) return;
|
||||
AstConst* const newp = constp->cloneTree(false);
|
||||
newp->dtypep(varp->subDTypep());
|
||||
replaceMemberDot(dotp, memberRefp, newp);
|
||||
}
|
||||
|
||||
// Resolve one class-scoped RefDType. The generic deferred walk finds these
|
||||
// through every dtype wrapper and follows out-of-tree typedef references.
|
||||
void resolveParamClassRefDType(AstRefDType* refp) {
|
||||
if (refp->typedefp() || refp->refDTypep()) return;
|
||||
|
||||
AstClassOrPackageRef* const classRefp
|
||||
@@ -1422,30 +1524,32 @@ class ParamProcessor final {
|
||||
}
|
||||
}
|
||||
|
||||
// Include the pin's value in the specialization name, so cells passing equal
|
||||
// values share one module clone.
|
||||
void nameByPinValue(AstPin* pinp, AstNodeModule* srcModp, AstVar* modvarp, string& longnamer,
|
||||
bool& any_overridesr) {
|
||||
longnamer += "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(pinp->exprp());
|
||||
any_overridesr = true;
|
||||
}
|
||||
|
||||
void cellPinCleanup(AstNode* nodep, AstPin* pinp, AstPin* paramsp, AstNodeModule* srcModp,
|
||||
string& longnamer, bool& any_overridesr) {
|
||||
if (!pinp->exprp()) return; // No-connect
|
||||
if (AstVar* const modvarp = pinp->modVarp()) {
|
||||
resolveParamClassRefDType(modvarp->subDTypep());
|
||||
resolveDeferredDotsReachableFrom(modvarp->subDTypep(), m_modp);
|
||||
if (!modvarp->isGParam()) {
|
||||
pinp->v3fatalSrc("Attempted parameter setting of non-parameter: Param "
|
||||
<< pinp->prettyNameQ() << " of " << nodep->prettyNameQ());
|
||||
} else if (VN_IS(pinp->exprp(), InitArray) && arraySubDTypep(modvarp->subDTypep())) {
|
||||
// Array assigned to array
|
||||
AstNode* const exprp = pinp->exprp();
|
||||
longnamer += "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(exprp);
|
||||
any_overridesr = true;
|
||||
} else if (VN_IS(pinp->exprp(), InitArray)) {
|
||||
// Array assigned to scalar parameter. Treat the InitArray as a constant
|
||||
// integer array and include it in the module name. Constantify nested
|
||||
// expressions before mangling the value number.
|
||||
V3Const::constifyParamsEdit(pinp->exprp());
|
||||
longnamer
|
||||
+= "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(pinp->exprp());
|
||||
any_overridesr = true;
|
||||
nameByPinValue(pinp, srcModp, modvarp, longnamer, any_overridesr);
|
||||
} else {
|
||||
UINFO(9, "cellPinCleanup: before constify " << pinp << " " << modvarp);
|
||||
V3Const::constifyParamsEdit(pinp->exprp());
|
||||
if (isAggregateParamValue(pinp->exprp())) {
|
||||
nameByPinValue(pinp, srcModp, modvarp, longnamer, any_overridesr);
|
||||
return;
|
||||
}
|
||||
// Cast/CastSize default values are not yet folded by V3Width.
|
||||
// Constify here so the comparison below sees a Const node.
|
||||
// Other node kinds are handled in the branches above.
|
||||
@@ -1606,15 +1710,10 @@ class ParamProcessor final {
|
||||
if (normedNamep) VL_DO_DANGLING(normedNamep->deleteTree(), normedNamep);
|
||||
}
|
||||
} else if (AstParamTypeDType* const modvarp = pinp->modPTypep()) {
|
||||
// Handle DOT with ParseRef RHS (e.g., p_class#(8)::p_type)
|
||||
// by this point ClassOrPackageRef should be updated to point to the specialized class.
|
||||
resolveDotToTypedef(pinp->exprp());
|
||||
resolveParamClassRefDType(modvarp->subDTypep());
|
||||
// Also resolve through the pin expression's typedef chain
|
||||
if (const AstRefDType* const pinRefp = VN_CAST(pinp->exprp(), RefDType)) {
|
||||
if (const AstTypedef* const tdefp = pinRefp->typedefp())
|
||||
resolveParamClassRefDType(tdefp->subDTypep());
|
||||
}
|
||||
// Resolve the pin and declared type through arbitrary dtype wrappers
|
||||
// and typedef chains before widthing either one.
|
||||
resolveDeferredDotsReachableFrom(pinp->exprp(), m_modp);
|
||||
resolveDeferredDotsReachableFrom(modvarp->subDTypep(), m_modp);
|
||||
|
||||
AstNodeDType* rawTypep = VN_CAST(pinp->exprp(), NodeDType);
|
||||
// Guard against widthing a struct/union still owned by a
|
||||
@@ -2155,6 +2254,67 @@ class ParamProcessor final {
|
||||
return newClassp;
|
||||
}
|
||||
|
||||
// Resolve deferred class-scoped references in post-order. Resolution may
|
||||
// deparameterize a class and delete its parameter pins, so no pointer to a
|
||||
// child may remain pending when its parent is resolved.
|
||||
class DeferredResolverVisitor final : public VNVisitor {
|
||||
ParamProcessor& m_processor;
|
||||
std::set<const AstNode*> m_reachedDecls;
|
||||
|
||||
bool firstReach(const AstNode* const declp) { return m_reachedDecls.insert(declp).second; }
|
||||
|
||||
void iterateParamType(AstParamTypeDType* const paramTypep) {
|
||||
if (firstReach(paramTypep)) iterate(paramTypep);
|
||||
}
|
||||
void iterateTypedef(AstTypedef* const tdefp) {
|
||||
if (firstReach(tdefp)) iterate(tdefp->subDTypep());
|
||||
}
|
||||
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstClassOrPackageRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
AstNode* const targetp = nodep->classOrPackageNodep();
|
||||
if (AstTypedef* const tdefp = VN_CAST(targetp, Typedef)) {
|
||||
iterateTypedef(tdefp);
|
||||
} else if (AstParamTypeDType* const paramTypep = VN_CAST(targetp, ParamTypeDType)) {
|
||||
iterateParamType(paramTypep);
|
||||
}
|
||||
}
|
||||
void visit(AstDot* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
m_processor.resolveDotToTypedef(nodep);
|
||||
}
|
||||
void visit(AstRefDType* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
AstTypedef* const tdefp = nodep->typedefp();
|
||||
if (tdefp) {
|
||||
iterateTypedef(tdefp);
|
||||
} else if (AstParamTypeDType* const paramTypep
|
||||
= VN_CAST(nodep->refDTypep(), ParamTypeDType)) {
|
||||
iterateParamType(paramTypep);
|
||||
}
|
||||
m_processor.resolveParamClassRefDType(nodep);
|
||||
}
|
||||
void visit(AstVarRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
AstVar* const varp = nodep->varp();
|
||||
if (firstReach(varp)) {
|
||||
iterate(varp->subDTypep());
|
||||
const auto& deferredVarps = v3Global.rootp()->deferredParamVarps();
|
||||
if (varp->varType() == VVarType::LPARAM && deferredVarps.count(varp)) {
|
||||
UASSERT_OBJ(varp->valuep(), varp, "VarRef should have non-null valuep");
|
||||
iterate(varp->valuep());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
DeferredResolverVisitor(ParamProcessor& processor, AstNode* rootp)
|
||||
: m_processor{processor} {
|
||||
iterate(rootp);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
// After an interface cell inside parentModp has been deparameterized
|
||||
// (rewired from template to clone), retarget REFDTYPEs that still
|
||||
@@ -2181,6 +2341,16 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
// Resolve deferred class-scoped values and types reachable from rootp. In
|
||||
// addition to AST children, follow typedefs, parameter types, deferred
|
||||
// localparam values, and referenced-variable dtypes. This makes the walk
|
||||
// independent of the particular dtype wrappers around a RefDType.
|
||||
void resolveDeferredDotsReachableFrom(AstNode* rootp, const AstNodeModule* modp) {
|
||||
VL_RESTORER(m_modp);
|
||||
m_modp = modp;
|
||||
DeferredResolverVisitor{*this, rootp};
|
||||
}
|
||||
|
||||
AstNodeModule* nodeDeparam(AstNode* nodep, AstNodeModule* srcModp, AstNodeModule* modp,
|
||||
const string& someInstanceName) {
|
||||
// Return new or reused de-parameterized module
|
||||
@@ -2221,14 +2391,9 @@ public:
|
||||
}
|
||||
// Create new module name with _'s between the constants
|
||||
UINFOTREE(10, nodep, "", "cell");
|
||||
// Resolve `class::member` Dots in pin values so constify sees Consts.
|
||||
// Single-pass: filter-collect class-scoped Dots, then reverse-iterate so inner
|
||||
// Dots resolve before outer (vector stays empty for cells with no class Dots).
|
||||
std::vector<AstDot*> dotps;
|
||||
nodep->foreach([&](AstDot* dotp) {
|
||||
if (VN_IS(dotp->lhsp(), ClassOrPackageRef)) dotps.push_back(dotp);
|
||||
});
|
||||
for (auto it = dotps.rbegin(); it != dotps.rend(); ++it) resolveDotToTypedef(*it);
|
||||
// Resolve `class::member` Dots in pin values, and in any deferred
|
||||
// lparam reachable from the pin tree, so constify sees Consts.
|
||||
resolveDeferredDotsReachableFrom(nodep, modp);
|
||||
// Evaluate all module constants
|
||||
V3Const::constifyParamsEdit(nodep);
|
||||
// Set name for warnings for when we param propagate the module
|
||||
@@ -2583,6 +2748,7 @@ class ParamVisitor final : public VNVisitor {
|
||||
VarsByName m_modIfaceRefs; // Interface-ref Vars in current module, keyed by name
|
||||
bool m_modIfaceRefsDone = false; // m_modIfaceRefs has been gathered for m_modp
|
||||
string m_generateHierName; // Generate portion of hierarchy name
|
||||
bool m_inGenerateCond = false; // Traversing a generate condition; see iterateGenerateCond
|
||||
|
||||
// METHODS
|
||||
|
||||
@@ -2895,7 +3061,9 @@ class ParamVisitor final : public VNVisitor {
|
||||
|
||||
// Visit parameters in the instantiation.
|
||||
iterateChildren(nodep);
|
||||
m_cellps.emplace(!isIface, nodep);
|
||||
// A generate condition is folded and deleted before the drain loop runs, so
|
||||
// queueing from one only leaves a dangling pointer. See iterateGenerateCond.
|
||||
if (!m_inGenerateCond) m_cellps.emplace(!isIface, nodep);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
@@ -2949,7 +3117,14 @@ class ParamVisitor final : public VNVisitor {
|
||||
}
|
||||
|
||||
void visit(AstRefDType* nodep) override {
|
||||
if (isCircularType(nodep)) {
|
||||
const bool isCircular = isCircularType(nodep);
|
||||
// A module-body `typedef C#(Cfg)::t alias` the deferred pin/param walk
|
||||
// never reached would survive to V3Width unlinked. If its alias chain is
|
||||
// broken, run the same deferred resolution here.
|
||||
if (!isCircular && nodep->typedefp() && !nodep->skipRefOrNullp()) {
|
||||
m_processor.resolveDeferredDotsReachableFrom(nodep, m_modp);
|
||||
}
|
||||
if (isCircular) {
|
||||
nodep->v3error("Typedef's type is circular: " << nodep->prettyName());
|
||||
} else if (nodep->typedefp() && nodep->subDTypep()
|
||||
&& (VN_IS(nodep->subDTypep()->skipRefOrNullp(), IfaceRefDType)
|
||||
@@ -3100,6 +3275,7 @@ class ParamVisitor final : public VNVisitor {
|
||||
v3Global.rootp()->pushDeferredParamVarp(nodep);
|
||||
return;
|
||||
}
|
||||
m_processor.resolveDeferredDotsReachableFrom(nodep, m_modp);
|
||||
V3Const::constifyParamsEdit(nodep);
|
||||
}
|
||||
}
|
||||
@@ -3311,9 +3487,24 @@ class ParamVisitor final : public VNVisitor {
|
||||
}
|
||||
|
||||
// Generate Statements
|
||||
|
||||
// Traverse a generate condition (if/case expression, for init/cond/inc).
|
||||
//
|
||||
// m_cellps assumes a queued ref outlives the drain loop, which holds elsewhere
|
||||
// because deletion is deferred. A generate condition doesn't defer and folds
|
||||
// immediately, so we can't queue. Anything queued here would be left dangling.
|
||||
// Refs in the surviving arm are queued when it is recursed.
|
||||
void iterateGenerateCond(AstNode* nodep) {
|
||||
VL_RESTORER(m_inGenerateCond);
|
||||
m_inGenerateCond = true;
|
||||
iterateAndNextNull(nodep);
|
||||
// The condition may reference deferred lparams whose Dots are still pending.
|
||||
m_processor.resolveDeferredDotsReachableFrom(nodep, m_modp);
|
||||
}
|
||||
|
||||
void visit(AstGenIf* nodep) override {
|
||||
UINFO(9, " GENIF " << nodep);
|
||||
iterateAndNextNull(nodep->condp());
|
||||
iterateGenerateCond(nodep->condp());
|
||||
// We suppress errors when widthing params since short-circuiting in
|
||||
// the conditional evaluation may mean these error can never occur. We
|
||||
// then make sure that short-circuiting is used by constifyParamsEdit.
|
||||
@@ -3347,9 +3538,9 @@ class ParamVisitor final : public VNVisitor {
|
||||
UINFO(9, " BEGIN " << nodep);
|
||||
UINFO(9, " GENFOR " << forp);
|
||||
// Visit child nodes before unrolling
|
||||
iterateAndNextNull(forp->initsp());
|
||||
iterateAndNextNull(forp->condp());
|
||||
iterateAndNextNull(forp->incsp());
|
||||
iterateGenerateCond(forp->initsp());
|
||||
iterateGenerateCond(forp->condp());
|
||||
iterateGenerateCond(forp->incsp());
|
||||
V3Width::widthParamsEdit(forp); // Param typed widthing will NOT recurse the body
|
||||
// Outer wrapper around generate used to hold genvar, and to ensure genvar
|
||||
// doesn't conflict in V3LinkDot resolution with other genvars
|
||||
@@ -3381,8 +3572,15 @@ class ParamVisitor final : public VNVisitor {
|
||||
UINFO(9, " GENCASE " << nodep);
|
||||
bool hit = false;
|
||||
AstNode* keepp = nullptr;
|
||||
iterateAndNextNull(nodep->exprp());
|
||||
iterateGenerateCond(nodep->exprp());
|
||||
V3Case::caseLint(nodep);
|
||||
// Case-item expressions must also be linked before widthing the case.
|
||||
for (AstGenCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), GenCaseItem)) {
|
||||
for (AstNode* ep = itemp->condsp(); ep; ep = ep->nextp()) {
|
||||
m_processor.resolveDeferredDotsReachableFrom(ep, m_modp);
|
||||
}
|
||||
}
|
||||
V3Width::widthParamsEdit(nodep); // Param typed widthing will NOT recurse the
|
||||
// body, don't trigger errors yet.
|
||||
V3Const::constifyParamsEdit(nodep->exprp()); // exprp may change
|
||||
@@ -3392,7 +3590,7 @@ class ParamVisitor final : public VNVisitor {
|
||||
itemp = VN_AS(itemp->nextp(), GenCaseItem)) {
|
||||
for (AstNode* ep = itemp->condsp(); ep;) {
|
||||
AstNode* const nextp = ep->nextp(); // May edit list
|
||||
iterateAndNextNull(ep);
|
||||
iterateGenerateCond(ep);
|
||||
VL_DO_DANGLING(V3Const::constifyParamsEdit(ep), ep); // ep may change
|
||||
ep = nextp;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user