Skip to content

Commit

Permalink
Represent lookups via interface port connections using hierarchical r…
Browse files Browse the repository at this point in the history
…eferences so we can track the path
  • Loading branch information
MikePopoloski committed Feb 15, 2025
1 parent 7f4d967 commit 595466f
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 32 deletions.
29 changes: 29 additions & 0 deletions include/slang/ast/HierarchicalReference.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace slang::ast {

class Compilation;
class Expression;
class InstanceSymbol;
class Symbol;
struct LookupResult;

Expand All @@ -27,20 +28,48 @@ class SLANG_EXPORT HierarchicalReference {
struct Element {
/// The symbol through which the path traverses.
not_null<const Symbol*> symbol;

/// The selector used to get to the @a symbol, either
/// an index if the parent was an array, or a field name.
std::variant<int32_t, std::string_view> selector;

/// Constructs an element with a name selector.
Element(const Symbol& symbol);

/// Constructs an element with an index selector.
Element(const Symbol& symbol, int32_t index);
};

/// The target symbol of the hierarchical reference.
const Symbol* target = nullptr;

/// The expression that was used to start the lookup,
/// typically a HierarchicalValueExpression.
const Expression* expr = nullptr;

/// The resolved path to the target symbol.
std::span<const Element> path;

/// The number of times the path traverses upward before
/// going back down the hierarchy to reach the target symbol.
size_t upwardCount = 0;

/// Default constructor.
HierarchicalReference() = default;

/// Constructs a HierarchicalReference from a lookup result.
static HierarchicalReference fromLookup(Compilation& compilation, const LookupResult& result);

/// Returns true if the hierarchical reference was resolved
/// via an interface port connection.
bool isViaIfacePort() const;

/// Re-resolves the target symbol starting from the corresponding port in
/// the new base instance and following the same path as the original reference.
///
/// @returns the resolved symbol, or nullptr if the path could not be followed
/// or this reference does not resolve through an interface port.
const Symbol* retargetIfacePort(const InstanceSymbol& base) const;
};

} // namespace slang::ast
7 changes: 5 additions & 2 deletions include/slang/ast/Lookup.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,12 @@ enum class SLANG_EXPORT LookupResultFlags : uint8_t {

/// The lookup was resolved through a forwarded typedef. Some language
/// rules restrict where this can be done.
FromForwardTypedef = 1 << 4
FromForwardTypedef = 1 << 4,

/// The lookup was resolved through an interface port connection.
IfacePort = 1 << 5
};
SLANG_BITMASK(LookupResultFlags, FromForwardTypedef)
SLANG_BITMASK(LookupResultFlags, IfacePort)

/// This type denotes the ordering of symbols within a particular scope, for the purposes of
/// determining whether a found symbol is visible compared to the given location.
Expand Down
49 changes: 48 additions & 1 deletion source/ast/HierarchicalReference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@

#include "slang/ast/Compilation.h"
#include "slang/ast/Symbol.h"
#include "slang/ast/symbols/InstanceSymbols.h"
#include "slang/ast/symbols/PortSymbols.h"
#include "slang/ast/symbols/ValueSymbol.h"
#include "slang/ast/types/Type.h"

namespace slang::ast {

Expand All @@ -22,7 +26,7 @@ HierarchicalReference::Element::Element(const Symbol& symbol, int32_t index) :

HierarchicalReference HierarchicalReference::fromLookup(Compilation& compilation,
const LookupResult& result) {
if (!result.flags.has(LookupResultFlags::IsHierarchical))
if (!result.flags.has(LookupResultFlags::IsHierarchical | LookupResultFlags::IfacePort))
return {};

HierarchicalReference ref;
Expand All @@ -32,4 +36,47 @@ HierarchicalReference HierarchicalReference::fromLookup(Compilation& compilation
return ref;
}

bool HierarchicalReference::isViaIfacePort() const {
return !path.empty() && path[0].symbol->kind == SymbolKind::InterfacePort;
}

const Symbol* HierarchicalReference::retargetIfacePort(const InstanceSymbol& base) const {
if (!isViaIfacePort() || !target)
return nullptr;

auto port = base.body.findPort(path[0].symbol->name);
if (!port)
return nullptr;

// TODO: think about modport restrictions
auto [symbol, modport] = port->as<InterfacePortSymbol>().getConnection();
for (size_t i = 1; i < path.size(); i++) {
if (!symbol)
return nullptr;

if (symbol->kind == SymbolKind::Instance)
symbol = &symbol->as<InstanceSymbol>().body;
else if (!symbol->isScope())
return nullptr;

auto& elem = path[i];
if (auto index = std::get_if<int32_t>(&elem.selector)) {
// TODO: handle array indices
}
else {
auto name = std::get<std::string_view>(elem.selector);
symbol = symbol->as<Scope>().find(name);
}
}

if (symbol) {
SLANG_ASSERT(symbol->kind == target->kind);
SLANG_ASSERT((symbol->isValue() == target->isValue()) &&
(!symbol->isValue() || symbol->as<ValueSymbol>().getType().isMatching(
target->as<ValueSymbol>().getType())));
}

return symbol;
}

} // namespace slang::ast
35 changes: 21 additions & 14 deletions source/ast/Lookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,12 @@ bool lookupDownward(std::span<const NamePlusLoc> nameParts, NameComponents name,
// - This is not a direct interface port, package, or $unit reference
const bool isCBOrVirtualIface = symbol->kind == SymbolKind::ClockingBlock || isVirtualIface;
if (it == nameParts.rbegin()) {
if (symbol->kind != SymbolKind::InterfacePort && symbol->kind != SymbolKind::Package &&
symbol->kind != SymbolKind::CompilationUnit && !isCBOrVirtualIface) {
if (symbol->kind == SymbolKind::InterfacePort) {
result.flags |= LookupResultFlags::IfacePort;
result.path.emplace_back(*symbol);
}
else if (symbol->kind != SymbolKind::Package &&
symbol->kind != SymbolKind::CompilationUnit && !isCBOrVirtualIface) {
result.flags |= LookupResultFlags::IsHierarchical;
result.path.emplace_back(*symbol);
}
Expand Down Expand Up @@ -498,18 +502,21 @@ bool lookupDownward(std::span<const NamePlusLoc> nameParts, NameComponents name,
if (!checkClassParams(name))
return false;

if (result.flags.has(LookupResultFlags::IsHierarchical) && symbol) {
if (VariableSymbol::isKind(symbol->kind) &&
symbol->as<VariableSymbol>().lifetime == VariableLifetime::Automatic) {
// If we found an automatic variable check that we didn't try to reference it
// hierarchically.
result.addDiag(*context.scope, diag::AutoVariableHierarchical, name.range);
return false;
}
else if (symbol->isType()) {
// Types cannot be referenced hierarchically.
result.addDiag(*context.scope, diag::TypeHierarchical, name.range);
return false;
if (result.flags.has(LookupResultFlags::IsHierarchical | LookupResultFlags::IfacePort) &&
symbol) {
if (result.flags.has(LookupResultFlags::IsHierarchical)) {
if (VariableSymbol::isKind(symbol->kind) &&
symbol->as<VariableSymbol>().lifetime == VariableLifetime::Automatic) {
// If we found an automatic variable check that we didn't try to reference it
// hierarchically.
result.addDiag(*context.scope, diag::AutoVariableHierarchical, name.range);
return false;
}
else if (symbol->isType()) {
// Types cannot be referenced hierarchically.
result.addDiag(*context.scope, diag::TypeHierarchical, name.range);
return false;
}
}
result.path.emplace_back(*symbol);
}
Expand Down
16 changes: 4 additions & 12 deletions source/ast/expressions/MiscExpressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,8 @@ HierarchicalValueExpression::HierarchicalValueExpression(const Scope& scope,
}

ConstantValue HierarchicalValueExpression::evalImpl(EvalContext& context) const {
if (!context.getCompilation().hasFlag(CompilationFlags::AllowHierarchicalConst) &&
if (!ref.isViaIfacePort() &&
!context.getCompilation().hasFlag(CompilationFlags::AllowHierarchicalConst) &&
!context.astCtx.flags.has(ASTFlags::ConfigParam)) {
context.addDiag(diag::ConstEvalHierarchicalName, sourceRange) << symbol.name;
return nullptr;
Expand All @@ -501,16 +502,6 @@ ConstantValue HierarchicalValueExpression::evalImpl(EvalContext& context) const
if (!checkConstantBase(context))
return nullptr;

switch (symbol.kind) {
case SymbolKind::Parameter:
case SymbolKind::EnumValue:
case SymbolKind::Specparam:
break;
default:
context.addDiag(diag::ConstEvalHierarchicalName, sourceRange) << symbol.name;
return nullptr;
}

switch (symbol.kind) {
case SymbolKind::Parameter: {
auto v = symbol.as<ParameterSymbol>().getValue(sourceRange);
Expand All @@ -527,7 +518,8 @@ ConstantValue HierarchicalValueExpression::evalImpl(EvalContext& context) const
case SymbolKind::Specparam:
return symbol.as<SpecparamSymbol>().getValue(sourceRange);
default:
SLANG_UNREACHABLE;
context.addDiag(diag::ConstEvalHierarchicalName, sourceRange) << symbol.name;
return nullptr;
}
}

Expand Down
6 changes: 4 additions & 2 deletions source/ast/symbols/SpecifySymbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,15 +223,17 @@ static const Expression* bindTerminal(const ExpressionSyntax& syntax,
break;
}

if (valueExpr->kind != ExpressionKind::NamedValue) {
if (valueExpr->kind != ExpressionKind::NamedValue &&
(valueExpr->kind != ExpressionKind::HierarchicalValue ||
!valueExpr->as<HierarchicalValueExpression>().ref.isViaIfacePort())) {
auto code = (valueExpr->kind == ExpressionKind::ElementSelect ||
valueExpr->kind == ExpressionKind::RangeSelect)
? diag::SpecifyPathMultiDim
: diag::InvalidSpecifyPath;
context.addDiag(code, syntax.sourceRange());
}
else {
auto& symbol = valueExpr->as<NamedValueExpression>().symbol;
auto& symbol = valueExpr->as<ValueExpressionBase>().symbol;
if (SpecifyBlockSymbol::checkPathTerminal(symbol, *expr->type, *parentParent, dir,
valueExpr->sourceRange)) {
return expr;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class VariableReferenceVisitor : public ast::ASTVisitor<VariableReferenceVisitor
bool leftOperand = false) :
netlist(netlist), evalCtx(evalCtx), leftOperand(leftOperand) {}

void handle(const ast::NamedValueExpression& expr) {
void handle(const ast::ValueExpressionBase& expr) {

// If the symbol reference is to a constant (eg a parameter or enum
// value), then skip it.
Expand Down

0 comments on commit 595466f

Please sign in to comment.