206 lines
9.8 KiB
C++
206 lines
9.8 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
//*************************************************************************
|
|
// DESCRIPTION: Interface typedef capture helper.
|
|
// Stores (refp, typedefp, cellp, owners, pendingClone) so LinkDot can
|
|
// rebind refs when symbol lookup fails, and V3Param clones can retarget
|
|
// typedefs without legacy paths.
|
|
//
|
|
// Code available from: https://verilator.org
|
|
//
|
|
//*************************************************************************
|
|
//
|
|
// 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-FileCopyrightText: 2003-2026 Wilson Snyder
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
//
|
|
//*************************************************************************
|
|
|
|
#ifndef VERILATOR_V3LINKDOTIFACECAPTURE_H_
|
|
#define VERILATOR_V3LINKDOTIFACECAPTURE_H_
|
|
|
|
#include "config_build.h"
|
|
|
|
#include "V3Ast.h"
|
|
|
|
#include <cstddef>
|
|
#include <functional>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
class VSymEnt;
|
|
|
|
class V3LinkDotIfaceCapture final {
|
|
public:
|
|
enum class CaptureType { IFACE, CLASS };
|
|
|
|
// Path-based map key: no pointers, only stable strings.
|
|
// {ownerModName, refName, cellPath, cloneCellPath} uniquely identifies
|
|
// every captured REFDTYPE. You cannot have two typedefs with the same
|
|
// name in the same module, so this tuple is unique.
|
|
struct CaptureKey final {
|
|
string ownerModName; // Module containing the REFDTYPE (e.g. "cca_xbar")
|
|
string refName; // REFDTYPE name (e.g. "r_chan_t")
|
|
string cellPath; // Template path (e.g. "cca_io.tlb_io")
|
|
string cloneCellPath; // Instance path (e.g. "xbar1"), empty for template
|
|
bool operator==(const CaptureKey& o) const {
|
|
return ownerModName == o.ownerModName && refName == o.refName && cellPath == o.cellPath
|
|
&& cloneCellPath == o.cloneCellPath;
|
|
}
|
|
};
|
|
struct CaptureKeyHash final {
|
|
size_t operator()(const CaptureKey& k) const {
|
|
size_t h = std::hash<string>{}(k.ownerModName);
|
|
h ^= std::hash<string>{}(k.refName) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
|
h ^= std::hash<string>{}(k.cellPath) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
|
h ^= std::hash<string>{}(k.cloneCellPath) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
|
return h;
|
|
}
|
|
};
|
|
|
|
// Template key: matches ALL entries regardless of cloneCellPath.
|
|
// Used for propagateClone and debug searches.
|
|
struct TemplateKey final {
|
|
string ownerModName;
|
|
string refName;
|
|
string cellPath;
|
|
};
|
|
|
|
struct CapturedEntry final {
|
|
CaptureType captureType = CaptureType::IFACE;
|
|
AstRefDType* refp = nullptr;
|
|
string cellPath; // Template path (e.g. "cca_io.tlb_io") - immutable key component
|
|
string cloneCellPath; // Instance-specific path (e.g. "cca_io1.tlb_io") - set by
|
|
// propagateClone when V3Param clones; empty for original entries
|
|
AstClass* origClassp = nullptr; // For CLASS captures
|
|
// Module where the RefDType lives
|
|
AstNodeModule* ownerModp = nullptr;
|
|
// Typedef definition being referenced
|
|
AstTypedef* typedefp = nullptr;
|
|
// For PARAMTYPEDTYPE
|
|
AstParamTypeDType* paramTypep = nullptr;
|
|
// Name of the module/interface that owns the typedef (stable string)
|
|
string typedefOwnerModName;
|
|
// Interface port variable for matching during cloning
|
|
AstVar* ifacePortVarp = nullptr;
|
|
// Additional REFDTYPEs sharing the same key (e.g. from macro expansions
|
|
// that produce multiple $bits() references to the same interface typedef).
|
|
// The primary refp is stored above; extras are appended here so that
|
|
// retargeting fixes ALL of them, not just the last-writer-wins primary.
|
|
std::vector<AstRefDType*> extraRefps;
|
|
// Clear template-specific targets that are stale in a clone context.
|
|
// Called by propagateClone before inserting a clone entry.
|
|
void clearStaleRefs() {
|
|
paramTypep = nullptr;
|
|
typedefp = nullptr;
|
|
extraRefps.clear();
|
|
}
|
|
// Visit every AstNode* pointer field (analogous to AstNode::foreachLink).
|
|
// The callback receives an AstNode* by reference; if it nulls the
|
|
// pointer the typed member is nulled accordingly.
|
|
template <typename T_func>
|
|
void foreachLink(T_func&& fn) {
|
|
auto callOnNode = [&](auto*& ptr) {
|
|
AstNode* np = ptr;
|
|
fn(np);
|
|
if (!np) ptr = nullptr;
|
|
};
|
|
callOnNode(refp);
|
|
callOnNode(ownerModp);
|
|
callOnNode(typedefp);
|
|
callOnNode(paramTypep);
|
|
callOnNode(ifacePortVarp);
|
|
callOnNode(origClassp);
|
|
for (auto& xrefp : extraRefps) callOnNode(xrefp);
|
|
}
|
|
};
|
|
|
|
using CapturedMap = std::unordered_map<CaptureKey, CapturedEntry, CaptureKeyHash>;
|
|
|
|
private:
|
|
friend class TypeTableDeadRefVisitor;
|
|
|
|
static CapturedMap s_map;
|
|
static bool s_enabled;
|
|
|
|
// --- Internal-only methods (not called outside V3LinkDotIfaceCapture.cpp) ---
|
|
static void enable(bool flag); // LCOV_EXCL_LINE
|
|
static void reset();
|
|
static void clearModuleCache();
|
|
static AstIfaceRefDType* ifaceRefFromVarDType(AstNodeDType* dtypep);
|
|
static string extractIfacePortName(const string& dotText);
|
|
static AstNodeDType* findDTypeByPrettyName(AstNodeModule* modp, const string& prettyName);
|
|
static AstNodeModule* findCloneViaHierarchy(AstNodeModule* containingModp,
|
|
AstNodeModule* deadTargetModp, int depth = 0);
|
|
static AstNodeModule* findLiveCloneOf(AstNodeModule* deadTargetModp,
|
|
AstNodeModule** containerp = nullptr);
|
|
static int fixDeadRefs(AstRefDType* refp, AstNodeModule* containingModp, const char* location);
|
|
static void captureInnerParamTypeRefs(AstParamTypeDType* paramTypep, AstRefDType* refp,
|
|
const string& cellPath, const string& ownerModName,
|
|
const string& ptOwnerName);
|
|
static int fixDeadRefsInTypeTable();
|
|
static int fixDeadRefsInModules();
|
|
static int fixWrongCloneRefs();
|
|
static void verifyNoDeadRefs();
|
|
template <typename T_FilterFn, typename T_Fn>
|
|
static void forEachImpl(T_FilterFn&& filter, T_Fn&& fn);
|
|
|
|
public:
|
|
static bool enabled() { return s_enabled; }
|
|
static AstNodeModule* findOwnerModule(AstNode* nodep);
|
|
// Find a Typedef by name in a module's top-level statements
|
|
static AstTypedef* findTypedefInModule(AstNodeModule* modp, const string& name);
|
|
// Find a NodeDType by name and VNType in a module's top-level statements
|
|
static AstNodeDType* findDTypeInModule(AstNodeModule* modp, const string& name, VNType type);
|
|
// Find a ParamTypeDType by name in a module's top-level statements
|
|
static AstParamTypeDType* findParamTypeInModule(AstNodeModule* modp, const string& name);
|
|
static void add(AstRefDType* refp, const string& cellPath, AstNodeModule* ownerModp,
|
|
AstTypedef* typedefp = nullptr, const string& typedefOwnerModName = "",
|
|
AstVar* ifacePortVarp = nullptr);
|
|
static void addClass(AstRefDType* refp, AstClass* origClassp, AstNodeModule* ownerModp,
|
|
AstTypedef* typedefp = nullptr, const string& typedefOwnerModName = "");
|
|
static void addParamType(AstRefDType* refp, const string& cellPath, AstNodeModule* ownerModp,
|
|
AstParamTypeDType* paramTypep, const string& paramTypeOwnerModName,
|
|
AstVar* ifacePortVarp);
|
|
// Exact lookup by full key
|
|
static const CapturedEntry* find(const CaptureKey& key);
|
|
// Pointer-based lookup: linear scan with early exit (no std::function overhead)
|
|
static const CapturedEntry* find(const AstRefDType* refp);
|
|
static void forEach(const std::function<void(const CapturedEntry&)>& fn);
|
|
static void forEachOwned(const AstNodeModule* ownerModp,
|
|
const std::function<void(const CapturedEntry&)>& fn);
|
|
static std::size_t size() { return s_map.size(); }
|
|
|
|
// Walk a dot-separated cell path (e.g. "cca_io.tlb_io") starting from
|
|
// startModp, returning the module at the end of the path. Returns
|
|
// nullptr if any component cannot be resolved.
|
|
static AstNodeModule* followCellPath(AstNodeModule* startModp, const string& cellPath);
|
|
|
|
// Create a new clone entry in the ledger, inheriting from the template.
|
|
// Ledger-only: no target lookup or AST mutation. Target resolution
|
|
// happens later in finalizeIfaceCapture where cell pointers are wired up.
|
|
static void propagateClone(const TemplateKey& tkey, AstRefDType* newRefp,
|
|
const string& cloneCellPath);
|
|
|
|
static void 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<std::string()>& indentFn);
|
|
|
|
// Null out ledger refp entries that point to freed nodes (not in the live AST).
|
|
// Called once after V3Param completes, before any code touches the ledger.
|
|
static void purgeStaleRefs();
|
|
|
|
// Debug: dump all captured entries
|
|
static void dumpEntries(const string& label);
|
|
|
|
// Called after V3Param but before V3Dead to fix any remaining cross-interface refs
|
|
// that still point to template nodes (which will be deleted by V3Dead).
|
|
static void finalizeIfaceCapture();
|
|
};
|
|
|
|
#endif // VERILATOR_V3LINKDOTIFACECAPTURE_H_
|