verilator/src/V3LinkDotIfaceCapture.cpp

240 lines
9.5 KiB
C++

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator:
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2025 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
//
//*************************************************************************
#include "V3LinkDotIfaceCapture.h"
#include "V3Error.h"
#include "V3Global.h"
VL_DEFINE_DEBUG_FUNCTIONS;
V3LinkDotIfaceCapture::CapturedMap V3LinkDotIfaceCapture::s_map{};
bool V3LinkDotIfaceCapture::s_enabled = true;
AstNodeModule* V3LinkDotIfaceCapture::findOwnerModule(AstNode* nodep) {
for (AstNode* curp = nodep; curp; curp = curp->backp()) {
if (AstNodeModule* const modp = VN_CAST(curp, NodeModule)) return modp;
}
return nullptr;
}
bool V3LinkDotIfaceCapture::finalizeCapturedEntry(CapturedMap::iterator it, const char* reasonp) {
CapturedIfaceTypedef& entry = it->second;
AstRefDType* const pendingRefp = entry.pendingClonep;
AstTypedef* const reboundTypedefp = entry.typedefp;
if (!pendingRefp || !reboundTypedefp) return false;
if (entry.cellp) pendingRefp->user2p(entry.cellp);
pendingRefp->user3(false);
pendingRefp->typedefp(reboundTypedefp);
entry.pendingClonep = nullptr;
return true;
}
string V3LinkDotIfaceCapture::extractIfacePortName(const string& dotText) {
string name = dotText;
const size_t dotPos = name.find('.');
if (dotPos != string::npos) name = name.substr(0, dotPos);
const size_t braPos = name.find("__BRA__");
if (braPos != string::npos) name = name.substr(0, braPos);
return name;
}
void V3LinkDotIfaceCapture::add(AstRefDType* refp, AstCell* cellp, AstNodeModule* ownerModp,
AstTypedef* typedefp, AstNodeModule* typedefOwnerModp,
AstVar* ifacePortVarp) {
if (!refp) return;
if (!typedefp) typedefp = refp->typedefp();
if (!typedefOwnerModp && typedefp) typedefOwnerModp = findOwnerModule(typedefp);
s_map[refp] = CapturedIfaceTypedef{
CaptureType::IFACE, refp, cellp, nullptr, ownerModp, typedefp,
typedefOwnerModp, nullptr, ifacePortVarp};
}
void V3LinkDotIfaceCapture::addClass(AstRefDType* refp, AstClass* origClassp,
AstNodeModule* ownerModp, AstTypedef* typedefp,
AstNodeModule* typedefOwnerModp) {
if (!refp) return;
if (!typedefp) typedefp = refp->typedefp();
if (!typedefOwnerModp && typedefp) typedefOwnerModp = findOwnerModule(typedefp);
s_map[refp] = CapturedIfaceTypedef{CaptureType::CLASS, refp, nullptr,
origClassp, ownerModp, typedefp,
typedefOwnerModp, nullptr, nullptr};
}
const V3LinkDotIfaceCapture::CapturedIfaceTypedef*
V3LinkDotIfaceCapture::find(const AstRefDType* refp) {
if (!refp) return nullptr;
const auto it = s_map.find(refp);
if (VL_UNLIKELY(it == s_map.end())) return nullptr;
return &it->second;
}
bool V3LinkDotIfaceCapture::erase(const AstRefDType* refp) {
if (!refp) return false;
const auto it = s_map.find(refp);
if (it == s_map.end()) return false;
s_map.erase(it);
return true;
}
bool V3LinkDotIfaceCapture::replaceRef(const AstRefDType* oldRefp, AstRefDType* newRefp) {
if (!oldRefp || !newRefp) return false;
const auto it = s_map.find(oldRefp);
if (it == s_map.end()) return false;
auto entry = it->second;
entry.refp = newRefp;
s_map.erase(it);
s_map.emplace(newRefp, entry);
return true;
}
bool V3LinkDotIfaceCapture::replaceTypedef(const AstRefDType* refp, AstTypedef* newTypedefp) {
if (!refp || !newTypedefp) return false;
auto it = s_map.find(refp);
if (it == s_map.end()) return false;
it->second.typedefp = newTypedefp;
it->second.typedefOwnerModp = findOwnerModule(newTypedefp);
// For CLASS captures, update the RefDType node directly
if (it->second.captureType == CaptureType::CLASS && it->second.refp) {
it->second.refp->typedefp(newTypedefp);
// Also update classOrPackagep to point to the specialized class
if (AstClass* const newClassp = VN_CAST(it->second.typedefOwnerModp, Class)) {
it->second.refp->classOrPackagep(newClassp);
}
UINFO(9, "class capture updated RefDType typedefp: " << it->second.refp << " -> "
<< newTypedefp);
}
finalizeCapturedEntry(it, "typedef clone");
return true;
}
void V3LinkDotIfaceCapture::propagateClone(const AstRefDType* origRefp, AstRefDType* newRefp) {
if (!origRefp || !newRefp) return;
const auto it = s_map.find(origRefp);
UASSERT_OBJ(it != s_map.end(), origRefp,
"iface capture propagateClone missing entry for orig=" << cvtToStr(origRefp));
CapturedIfaceTypedef& entry = it->second;
if (entry.cellp) newRefp->user2p(entry.cellp);
newRefp->user3(false);
entry.pendingClonep = newRefp;
// If replaceTypedef was already called (interface cloned before module),
// entry.typedefp will differ from the original RefDType's typedef.
// In that case, finalize now with the updated typedef.
if (entry.typedefp && origRefp->typedefp() && entry.typedefp != origRefp->typedefp()) {
finalizeCapturedEntry(it, "ref clone");
}
}
template <typename FilterFn, typename Fn>
void V3LinkDotIfaceCapture::forEachImpl(FilterFn&& filter, Fn&& fn) {
std::vector<const AstRefDType*> keys;
keys.reserve(s_map.size());
for (const auto& kv : s_map) keys.push_back(kv.first);
for (const AstRefDType* key : keys) {
const auto it = s_map.find(key);
if (it == s_map.end()) continue;
CapturedIfaceTypedef& entry = it->second;
if (entry.cellp && entry.refp && entry.refp->user2p() != entry.cellp) {
entry.refp->user2p(entry.cellp);
}
if (!filter(entry)) continue;
fn(entry);
}
}
void V3LinkDotIfaceCapture::forEach(const std::function<void(const CapturedIfaceTypedef&)>& fn) {
if (!fn) return;
forEachImpl([](const CapturedIfaceTypedef&) { return true; }, fn);
}
void V3LinkDotIfaceCapture::forEachOwned(
const AstNodeModule* ownerModp, const std::function<void(const CapturedIfaceTypedef&)>& fn) {
if (!ownerModp || !fn) return;
forEachImpl(
[ownerModp](const CapturedIfaceTypedef& e) {
return e.ownerModp == ownerModp || e.typedefOwnerModp == ownerModp;
},
fn);
}
// replaces the lambda used in V3LinkDot.cpp for iface capture
void V3LinkDotIfaceCapture::captureTypedefContext(
AstRefDType* refp, const char* stageLabel, int dotPos, bool dotIsFinal,
const std::string& dotText, VSymEnt* dotSymp, VSymEnt* curSymp, AstNodeModule* modp,
AstNode* nodep, const std::function<bool(AstVar*, AstRefDType*)>& promoteVarCb,
const std::function<std::string()>& indentFn) {
if (!enabled() || !refp) return;
UINFO(9, indentFn() << "iface capture capture request stage=" << stageLabel
<< " typedef=" << refp << " name=" << refp->name() << " dotPos=" << dotPos
<< " dotText='" << dotText << "' dotSym=" << dotSymp);
const AstCell* ifaceCellp = nullptr;
if (dotSymp && VN_IS(dotSymp->nodep(), Cell)) {
const AstCell* const cellp = VN_AS(dotSymp->nodep(), Cell);
if (cellp->modp() && VN_IS(cellp->modp(), Iface)) ifaceCellp = cellp;
}
if (!ifaceCellp) {
UINFO(9, indentFn() << "iface capture capture skipped typedef=" << refp
<< " (no iface context)");
return;
}
AstVar* ifacePortVarp = nullptr;
if (!dotText.empty() && curSymp) {
const std::string portName = extractIfacePortName(dotText);
if (VSymEnt* const portSymp = curSymp->findIdFallback(portName)) {
ifacePortVarp = VN_CAST(portSymp->nodep(), Var);
UINFO(9, indentFn() << "iface capture found port var '" << portName << "' -> "
<< ifacePortVarp);
}
}
refp->user2p(const_cast<AstCell*>(ifaceCellp));
V3LinkDotIfaceCapture::add(refp, const_cast<AstCell*>(ifaceCellp), modp, refp->typedefp(),
nullptr, ifacePortVarp);
UINFO(9, indentFn() << "iface capture capture success typedef=" << refp
<< " cell=" << ifaceCellp
<< " mod=" << (ifaceCellp->modp() ? ifaceCellp->modp()->name() : "<null>")
<< " dotPos=" << dotPos);
if (!dotIsFinal) return;
AstVar* enclosingVarp = nullptr;
for (AstNode* curp = nodep; curp; curp = curp->backp()) {
if (AstVar* const varp = VN_CAST(curp, Var)) {
enclosingVarp = varp;
break;
}
if (VN_IS(curp, ParamTypeDType)) break;
if (VN_IS(curp, NodeModule)) break;
}
if (!enclosingVarp || enclosingVarp->user3SetOnce()) return;
UINFO(9, indentFn() << "iface capture typedef owner var=" << enclosingVarp
<< " name=" << enclosingVarp->prettyName());
if (promoteVarCb && promoteVarCb(enclosingVarp, refp)) return;
UINFO(9, indentFn() << "iface capture failed to convert owner var name="
<< enclosingVarp->prettyName());
}