[OM] Overhaul of path operations (#6253)

This PR does an overhaul of how path related operations work in the OM dialect.

When OM classes are created from FIRRTL, a base path is now fed through the
class hieararchy.  All paths to hardware objects are now created relative to
the base path.  The `path` op has been renamed to `path_create`, and now takes
a basepath SSA parameter, which means that it is impossible to create a path
which is not relative to some base.

A second set of operations to create "frozen paths" have been added.  In
FreezePaths, we lower all path operations to hard-coded strings, which allows
us to remove the hardware from the OM IR and still be valid.  These operations
have been changed to return `FrozenPathType`, which is to make sure that people
don't try to inter-mix the two kinds of path types.  The intention is that the
evaluator will only understand frozen paths.

The PathAttr in OM no longer represents the entire target path, but just the
list of instances which lead up to it.

This also adds an OM utility for parsing FIRRTL style target strings, which is
used in the `frozenpath_create` MLIR assembly format.  The existing APIs
contained in FIRRTL were not suitable since these new strings do not include
the circuit name, and we need some restrictions for parsing base paths, which
can not target components. The new parser is a bit more resilient to badly
formed path strings. In the future it may be possible to reunite these two
bodies of code.

The existing python support for paths had to be removed.
This commit is contained in:
Andrew Young 2023-10-11 12:33:10 -07:00 committed by GitHub
parent 0518211c34
commit 8e4992be35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 748 additions and 234 deletions

View File

@ -13,6 +13,30 @@
#ifndef CIRCT_DIALECT_OM_OMATTRIBUTES_H
#define CIRCT_DIALECT_OM_OMATTRIBUTES_H
#include "circt/Dialect/OM/OMDialect.h"
#include "mlir/IR/BuiltinAttributes.h"
namespace circt::om {
/// A module name, and the name of an instance inside that module.
struct PathElement {
PathElement(mlir::StringAttr module, mlir::StringAttr instance)
: module(module), instance(instance) {}
bool operator==(const PathElement &rhs) const {
return module == rhs.module && instance == rhs.instance;
}
// NOLINTNEXTLINE(readability-identifier-naming)
friend llvm::hash_code hash_value(const PathElement &arg) {
return ::llvm::hash_combine(arg.module, arg.instance);
}
mlir::StringAttr module;
mlir::StringAttr instance;
};
} // namespace circt::om
#include "circt/Dialect/HW/HWAttributes.h"
#include "mlir/IR/Attributes.h"

View File

@ -103,21 +103,20 @@ def MapAttr : AttrDef<OMDialect, "Map", [TypedAttrInterface]> {
}];
}
def OMPathAttribute : AttrDef<OMDialect, "Path", [TypedAttrInterface]> {
let summary = "An attribute that represents a path";
def OMPathAttr : AttrDef<OMDialect, "Path"> {
let summary = "An attribute that represents an instance path";
let mnemonic = "path";
let parameters = (ins "::mlir::StringAttr":$path);
let parameters = (ins ArrayRefParameter<"::circt::om::PathElement">:$path);
let builders = [
AttrBuilderWithInferredContext<(ins "::mlir::StringAttr":$path)>
];
let genVerifyDecl = 1;
let assemblyFormat = " `<` $path `>` ";
let hasCustomAssemblyFormat = 1;
let extraClassDeclaration = [{
mlir::Type getType();
auto begin() const { return getPath().begin(); }
auto end() const { return getPath().end(); }
}];
}

View File

@ -13,6 +13,7 @@
#ifndef CIRCT_DIALECT_OM_OMOPS_H
#define CIRCT_DIALECT_OM_OMOPS_H
#include "circt/Dialect/OM/OMAttributes.h"
#include "circt/Dialect/OM/OMDialect.h"
#include "circt/Dialect/OM/OMOpInterfaces.h"
#include "circt/Dialect/OM/OMTypes.h"

View File

@ -297,27 +297,138 @@ def MapCreateOp : OMOp<"map_create", [Pure, SameTypeOperands]> {
let hasCustomAssemblyFormat = true;
}
def PathOp : OMOp<"path", [Pure,
def BasePathCreateOp : OMOp<"basepath_create", [Pure,
DeclareOpInterfaceMethods<SymbolUserOpInterface>
]> {
let summary = "Produce a base path value";
let description = [{
Produces a value which represents a fragment of a hierarchical path to a
target. Given a base path, extend it with the name of a module instance, to
produce a new base path. The instance is identified via an NLA. Once the
final verilog name of the instance is known, this op can be converted into
a FrozenBasePathOp.
Example:
```mlir
hw.module @Foo() -> () {
hw.inst "bar" sym @bar @Bar() -> ()
}
hw.hierpath @Path [@Foo::@bar]
om.class @OM(%basepath: !om.basepath) {
%0 = om.basepath_create %base @Path
}
```
}];
let arguments = (ins BasePathType:$basePath, FlatSymbolRefAttr:$target);
let results = (outs BasePathType:$result);
let assemblyFormat = "$basePath $target attr-dict";
}
def PathCreateOp : OMOp<"path_create", [Pure,
DeclareOpInterfaceMethods<SymbolUserOpInterface>
]> {
let summary = "Produce a path value";
let description = [{
Produces a value which represents a hierarchical path to a hardware
component.
target.
from a base path to a target.
Example:
```mlir
hw.module @Foo {
hw.module @Foo() -> () {
%wire = hw.wire sym @w: !i1
}
hw.hierpath @Path [@Foo::@w]
%0 = om.path member @Path
om.class @OM(%basepath: !om.basepath)
%0 = om.path_create reference %basepath @Path
}
```
}];
let arguments = (ins TargetKind:$targetKind,
FlatSymbolRefAttr:$target);
let arguments = (ins
TargetKind:$targetKind,
BasePathType:$basePath,
FlatSymbolRefAttr:$target
);
let results = (outs PathType:$result);
let assemblyFormat = "$targetKind $target attr-dict";
let assemblyFormat = "$targetKind $basePath $target attr-dict";
}
def EmptyPathOp : OMOp<"path_empty", [Pure]> {
let summary = "Produce a path value to nothing";
let description = [{
Produces a value which represents a hierarchical path to nothing.
Example:
```mlir
om.class @OM()
%0 = om.path_empty
}
```
}];
let results = (outs FrozenPathType:$result);
let assemblyFormat = "attr-dict";
}
def FrozenBasePathCreateOp : OMOp<"frozenbasepath_create", [Pure]> {
let summary = "Produce a frozen base path value";
let description = [{
Produces a value which represents a fragment of a hierarchical path to a
target.
Example:
```mlir
om.class @OM(%basepath: !om.basepath)
%0 = om.frozenbasepath_create %basepath "Foo/bar:Bar/baz"
}
```
}];
let arguments = (ins FrozenBasePathType:$basePath, OMPathAttr:$path);
let results = (outs FrozenBasePathType:$result);
let assemblyFormat = "$basePath custom<BasePathString>($path) attr-dict";
}
def FrozenPathCreateOp : OMOp<"frozenpath_create", [Pure]> {
let summary = "Produce a frozen path value";
let description = [{
Produces a value which represents a hierarchical path to a hardware
component from a base path to a target.
Example:
```mlir
om.class @OM(%basepath: !om.basepath)
%0 = om.frozenpath_create reference %base "Foo/bar:Bar>w.a"
}
```
}];
let arguments = (ins
TargetKind:$targetKind,
FrozenBasePathType:$basePath,
OMPathAttr:$path,
StrAttr:$module,
StrAttr:$ref,
StrAttr:$field
);
let results = (outs FrozenPathType:$result);
let assemblyFormat = [{
$targetKind $basePath custom<PathString>($path, $module, $ref, $field)
attr-dict
}];
}
def FrozenEmptyPathOp : OMOp<"frozenpath_empty", [Pure]> {
let summary = "Produce a frozen path value to nothing";
let description = [{
Produces a value which represents a hierarchical path to nothing.
Example:
```mlir
om.class @OM()
%0 = om.frozenpath_empty
}
```
}];
let results = (outs FrozenPathType:$result);
let assemblyFormat = "attr-dict";
}
def AnyCastOp : OMOp<"any_cast", [Pure]> {
@ -337,22 +448,4 @@ def AnyCastOp : OMOp<"any_cast", [Pure]> {
let assemblyFormat =
"$input attr-dict `:` functional-type($input, $result)";
}
def PathAppendOp : OMOp<"path_append", [Pure]> {
let summary = "Append two path value";
let description = [{
Appends the path to the root path. The root path must not have a component.
The appended path must begin where the root path ends.
Example:
```mlir
%root = om.constant #om.path<"Foo/bar:Bar">
%path = om.constant #om.path<"Bar/baz:Baz>output">
%0 = om.path_append %root, %path
```
}];
let arguments = (ins PathType:$root, PathType:$path);
let results = (outs PathType:$result);
let assemblyFormat = "$root `,` $path attr-dict";
}
#endif // CIRCT_DIALECT_OM_OMOPS_TD

View File

@ -0,0 +1,35 @@
//===- OMUtils.h - OM Utility Functions -----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef CIRCT_DIALECT_OM_OMUTILS_H
#define CIRCT_DIALECT_OM_OMUTILS_H
#include "circt/Support/LLVM.h"
#include "llvm/ADT/STLExtras.h"
namespace circt::om {
struct PathElement;
class PathAttr;
/// Parse a target string of the form "Foo/bar:Bar/baz" in to a base path.
ParseResult parseBasePath(MLIRContext *context, StringRef spelling,
PathAttr &path);
/// Parse a target string in to a path.
/// "Foo/bar:Bar/baz:Baz>wire.a[1]"
/// |--------------| Path
/// |--| Module
/// |--| Ref
/// |---| Field
ParseResult parsePath(MLIRContext *context, StringRef spelling, PathAttr &path,
StringAttr &module, StringAttr &ref, StringAttr &field);
} // namespace circt::om
#endif // CIRCT_DIALECT_OM_OMUTILS_H

View File

@ -44,9 +44,6 @@ with Context() as ctx, Location.unknown():
%0 = om.object @Child(%c_14) : (!om.integer) -> !om.class.type<@Child>
om.class.field @child, %0 : !om.class.type<@Child>
%path = om.constant #om.path<"path"> : !om.path
om.class.field @path, %path : !om.path
om.class.field @reference, %sym : !om.ref
%list = om.constant #om.list<!om.string, ["X" : !om.string, "Y" : !om.string]> : !om.list<!om.string>
@ -129,7 +126,7 @@ print(obj.get_field_loc("field"))
# CHECK: 14
print(obj.child.foo)
# CHECK: loc("-":64:7)
# CHECK: loc("-":61:7)
print(obj.child.get_field_loc("foo"))
# CHECK: ('Root', 'x')
print(obj.reference)
@ -137,15 +134,9 @@ print(obj.reference)
# CHECK: 14
print(snd)
# CHECK: loc("-":43:7)
# CHECK: loc("-":40:7)
print(obj.get_field_loc("tuple"))
# CHECK: path
print(obj.path)
# location of om.class.field @path, %path : !om.path
# CHECK: loc("-":35:7)
print(obj.get_field_loc("path"))
try:
print(obj.tuple[3])
except IndexError as e:
@ -161,7 +152,7 @@ for (name, field) in obj:
# CHECK-SAME: loc: loc("-":28:7)
# location from om.class.field @reference, %sym : !om.ref
# CHECK: name: reference, field: ('Root', 'x')
# CHECK-SAME: loc: loc("-":37:7)
# CHECK-SAME: loc: loc("-":34:7)
loc = obj.get_field_loc(name)
print(f"name: {name}, field: {field}, loc: {loc}")

View File

@ -416,14 +416,6 @@ void circt::python::populateDialectOMSubmodule(py::module &m) {
.def("__len__", &omMapAttrGetNumElements);
PyMapAttrIterator::bind(m);
// Add the PathAttr definition
mlir_attribute_subclass(m, "PathAttr", omAttrIsAPathAttr)
.def_property_readonly("value", [](MlirAttribute arr) {
auto path = mlirIdentifierStr(omPathAttrGetPath(arr));
std::string pathStr(path.data, path.length);
return pathStr;
});
// Add the ClassType class definition.
mlir_type_subclass(m, "ClassType", omTypeIsAClassType, omClassTypeGetTypeID)
.def_property_readonly("name", [](MlirType type) {

View File

@ -5,7 +5,7 @@
from __future__ import annotations
from ._om_ops_gen import *
from .._mlir_libs._circt._om import Evaluator as BaseEvaluator, Object as BaseObject, List as BaseList, Tuple as BaseTuple, Map as BaseMap, ClassType, ReferenceAttr, ListAttr, MapAttr, PathAttr, OMIntegerAttr
from .._mlir_libs._circt._om import Evaluator as BaseEvaluator, Object as BaseObject, List as BaseList, Tuple as BaseTuple, Map as BaseMap, ClassType, ReferenceAttr, ListAttr, MapAttr, OMIntegerAttr
from ..ir import Attribute, Diagnostic, DiagnosticSeverity, Module, StringAttr, IntegerAttr, IntegerType
from ..support import attribute_to_var, var_to_attribute

View File

@ -363,15 +363,3 @@ MlirAttribute omMapAttrGetElementValue(MlirAttribute attr, intptr_t pos) {
auto mapAttr = llvm::cast<MapAttr>(unwrap(attr));
return wrap(mapAttr.getElements().getValue()[pos].getValue());
}
//===----------------------------------------------------------------------===//
// PathAttr API.
//===----------------------------------------------------------------------===//
bool omAttrIsAPathAttr(MlirAttribute attr) {
return unwrap(attr).isa<PathAttr>();
}
MlirIdentifier omPathAttrGetPath(MlirAttribute attr) {
return wrap(unwrap(attr).cast<PathAttr>().getPath());
}

View File

@ -303,6 +303,8 @@ bool LowerClassesPass::shouldCreateClass(FModuleLike moduleLike) {
ClassLoweringState LowerClassesPass::createClass(FModuleLike moduleLike) {
// Collect the parameter names from input properties.
SmallVector<StringRef> formalParamNames;
// Every class gets a base path as its first parameter.
formalParamNames.emplace_back("basepath");
for (auto [index, port] : llvm::enumerate(moduleLike.getPorts()))
if (port.isInput() && isa<PropertyType>(port.type))
formalParamNames.push_back(port.name);
@ -371,6 +373,9 @@ void LowerClassesPass::lowerClass(om::ClassOp classOp, FModuleLike moduleLike) {
// Construct the OM Class body with block arguments for each input property,
// updating the mapping to map from the input property to the block argument.
Block *classBody = &classOp->getRegion(0).emplaceBlock();
// Every class created from a module gets a base path as its first parameter.
classBody->addArgument(BasePathType::get(&getContext()),
UnknownLoc::get(&getContext()));
for (auto inputProperty : inputProperties) {
BlockArgument parameterValue =
classBody->addArgument(inputProperty.type, inputProperty.loc);
@ -440,6 +445,10 @@ void LowerClassesPass::lowerClassExtern(ClassExternOp classExternOp,
Block *classBody = &classExternOp.getRegion().emplaceBlock();
OpBuilder builder = OpBuilder::atBlockBegin(classBody);
// Every class gets a base path as its first parameter.
classBody->addArgument(BasePathType::get(&getContext()),
UnknownLoc::get(&getContext()));
for (unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i) {
auto type = moduleLike.getPortType(i);
if (!isa<PropertyType>(type))
@ -471,6 +480,8 @@ void LowerClassesPass::lowerClassExtern(ClassExternOp classExternOp,
static LogicalResult
updateObjectInstance(firrtl::ObjectOp firrtlObject, OpBuilder &builder,
SmallVectorImpl<Operation *> &opsToErase) {
// The 0'th argument is the base path.
auto basePath = firrtlObject->getBlock()->getArgument(0);
// build a table mapping the indices of input ports to their position in the
// om class's parameter list.
auto firrtlClassType = firrtlObject.getType();
@ -478,7 +489,8 @@ updateObjectInstance(firrtl::ObjectOp firrtlObject, OpBuilder &builder,
llvm::SmallVector<unsigned> argIndexTable;
argIndexTable.resize(numElements);
unsigned nextArgIndex = 0;
unsigned nextArgIndex = 1;
for (unsigned i = 0; i < numElements; ++i) {
auto direction = firrtlClassType.getElement(i).direction;
if (direction == Direction::In)
@ -490,6 +502,7 @@ updateObjectInstance(firrtl::ObjectOp firrtlObject, OpBuilder &builder,
llvm::SmallVector<Value> args;
args.resize(nextArgIndex);
args[0] = basePath;
for (auto *user : llvm::make_early_inc_range(firrtlObject->getUsers())) {
if (auto subfield = dyn_cast<ObjectSubfieldOp>(user)) {
@ -562,6 +575,8 @@ updateModuleInstanceClass(InstanceOp firrtlInstance, OpBuilder &builder,
// parameters. The order of the SmallVector needs to match the order the
// formal parameters are declared on the corresponding Class.
SmallVector<Value> actualParameters;
// The 0'th argument is the base path.
actualParameters.push_back(firrtlInstance->getBlock()->getArgument(0));
for (auto result : firrtlInstance.getResults()) {
// If the port is an output, continue.
if (firrtlInstance.getPortDirection(result.getResultNumber()) ==
@ -821,6 +836,9 @@ struct PathOpConversion : public OpConversionPattern<firrtl::PathOp> {
auto pathType = om::PathType::get(context);
auto pathInfo = pathInfoTable.lookup(op.getTarget());
// The 0'th argument is the base path.
auto basePath = op->getBlock()->getArgument(0);
// If the target was optimized away, then replace the path operation with
// a deleted path.
if (!pathInfo) {
@ -828,8 +846,7 @@ struct PathOpConversion : public OpConversionPattern<firrtl::PathOp> {
return emitError(op.getLoc(), "DontTouch target was deleted");
if (op.getTargetKind() == firrtl::TargetKind::Instance)
return emitError(op.getLoc(), "Instance target was deleted");
auto pathAttr = om::PathAttr::get(StringAttr::get(context, "OMDeleted"));
rewriter.replaceOpWithNewOp<om::ConstantOp>(op, pathAttr);
rewriter.replaceOpWithNewOp<om::EmptyPathOp>(op);
return success();
}
@ -861,9 +878,9 @@ struct PathOpConversion : public OpConversionPattern<firrtl::PathOp> {
break;
}
rewriter.replaceOpWithNewOp<om::PathOp>(
rewriter.replaceOpWithNewOp<om::PathCreateOp>(
op, pathType, om::TargetKindAttr::get(op.getContext(), targetKind),
symbol);
basePath, symbol);
return success();
}
@ -1133,6 +1150,9 @@ static void populateTypeConverter(TypeConverter &converter) {
// Convert FIRRTL PathType to OM PathType.
converter.addConversion([](om::PathType type) { return type; });
converter.addConversion([](om::BasePathType type) { return type; });
converter.addConversion([](om::FrozenPathType type) { return type; });
converter.addConversion([](om::FrozenBasePathType type) { return type; });
converter.addConversion([](firrtl::PathType type) {
return om::PathType::get(type.getContext());
});

View File

@ -14,6 +14,7 @@ add_circt_dialect_library(
OMOpInterfaces.cpp
OMOps.cpp
OMTypes.cpp
OMUtils.cpp
DEPENDS
MLIROMIncGen

View File

@ -565,8 +565,8 @@ ArrayAttr circt::om::evaluator::MapValue::getKeys() {
attrs.push_back(key);
std::sort(attrs.begin(), attrs.end(), [](Attribute l, Attribute r) {
if (auto lInt = dyn_cast<IntegerAttr>(l))
if (auto rInt = dyn_cast<IntegerAttr>(r))
if (auto lInt = dyn_cast<mlir::IntegerAttr>(l))
if (auto rInt = dyn_cast<mlir::IntegerAttr>(r))
return lInt.getValue().ult(rInt.getValue());
assert(isa<StringAttr>(l) && isa<StringAttr>(r) &&

View File

@ -91,11 +91,39 @@ circt::om::MapAttr::verify(function_ref<InFlightDiagnostic()> emitError,
return success();
}
circt::om::PathAttr circt::om::PathAttr::get(mlir::StringAttr path) {
return om::PathAttr::get(path.getContext(), path);
void PathAttr::print(AsmPrinter &odsPrinter) const {
odsPrinter << '[';
llvm::interleaveComma(getPath(), odsPrinter, [&](PathElement element) {
odsPrinter.printKeywordOrString(element.module);
odsPrinter << '>';
odsPrinter.printKeywordOrString(element.instance);
});
odsPrinter << ']';
}
Type circt::om::PathAttr::getType() { return PathType::get(getContext()); }
Attribute PathAttr::parse(AsmParser &odsParser, Type odsType) {
auto *context = odsParser.getContext();
SmallVector<PathElement> path;
if (odsParser.parseCommaSeparatedList(
OpAsmParser::Delimiter::Square, [&]() -> ParseResult {
std::string module;
std::string instance;
if (odsParser.parseKeywordOrString(&module) ||
odsParser.parseGreater() ||
odsParser.parseKeywordOrString(&instance))
return failure();
path.emplace_back(StringAttr::get(context, module),
StringAttr::get(context, instance));
return success();
}))
return nullptr;
return PathAttr::get(context, path);
}
LogicalResult PathAttr::verify(function_ref<mlir::InFlightDiagnostic()>,
ArrayRef<PathElement> path) {
return success();
}
Type circt::om::IntegerAttr::getType() {
return OMIntegerType::get(getContext());

View File

@ -11,14 +11,69 @@
//===----------------------------------------------------------------------===//
#include "circt/Dialect/OM/OMOps.h"
#include "circt/Dialect/HW/HWOps.h"
#include "circt/Dialect/OM/OMUtils.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/ImplicitLocOpBuilder.h"
using namespace mlir;
using namespace circt::om;
//===----------------------------------------------------------------------===//
// Path Printers and Parsers
//===----------------------------------------------------------------------===//
static ParseResult parseBasePathString(OpAsmParser &parser, PathAttr &path) {
auto *context = parser.getContext();
auto loc = parser.getCurrentLocation();
std::string rawPath;
if (parser.parseString(&rawPath))
return failure();
if (parseBasePath(context, rawPath, path))
return parser.emitError(loc, "invalid base path");
return success();
}
static void printBasePathString(OpAsmPrinter &p, Operation *op, PathAttr path) {
p << '\"';
llvm::interleave(
path, p,
[&](const PathElement &elt) {
p << elt.module.getValue() << '/' << elt.instance.getValue();
},
":");
p << '\"';
}
static ParseResult parsePathString(OpAsmParser &parser, PathAttr &path,
StringAttr &module, StringAttr &ref,
StringAttr &field) {
auto *context = parser.getContext();
auto loc = parser.getCurrentLocation();
std::string rawPath;
if (parser.parseString(&rawPath))
return failure();
if (parsePath(context, rawPath, path, module, ref, field))
return parser.emitError(loc, "invalid path");
return success();
}
static void printPathString(OpAsmPrinter &p, Operation *op, PathAttr path,
StringAttr module, StringAttr ref,
StringAttr field) {
p << '\"';
for (const auto &elt : path)
p << elt.module.getValue() << '/' << elt.instance.getValue() << ':';
if (!module.getValue().empty())
p << module.getValue();
if (!ref.getValue().empty())
p << '>' << ref.getValue();
if (!field.getValue().empty())
p << field.getValue();
p << '\"';
}
//===----------------------------------------------------------------------===//
// Shared definitions
//===----------------------------------------------------------------------===//
@ -406,7 +461,7 @@ LogicalResult TupleGetOp::inferReturnTypes(
MLIRContext *context, std::optional<Location> location, ValueRange operands,
DictionaryAttr attributes, OpaqueProperties, RegionRange regions,
llvm::SmallVectorImpl<Type> &inferredReturnTypes) {
auto idx = attributes.getAs<IntegerAttr>("index");
auto idx = attributes.getAs<mlir::IntegerAttr>("index");
if (operands.empty() || !idx)
return failure();
@ -456,8 +511,25 @@ ParseResult circt::om::MapCreateOp::parse(OpAsmParser &parser,
return success();
}
LogicalResult PathOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
// Get the containing ModuleOp.
//===----------------------------------------------------------------------===//
// BasePathCreateOp
//===----------------------------------------------------------------------===//
LogicalResult
BasePathCreateOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
auto hierPath = symbolTable.lookupNearestSymbolFrom<hw::HierPathOp>(
*this, getTargetAttr());
if (!hierPath)
return emitOpError("invalid symbol reference");
return success();
}
//===----------------------------------------------------------------------===//
// PathCreateOp
//===----------------------------------------------------------------------===//
LogicalResult
PathCreateOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
auto hierPath = symbolTable.lookupNearestSymbolFrom<hw::HierPathOp>(
*this, getTargetAttr());
if (!hierPath)

186
lib/Dialect/OM/OMUtils.cpp Normal file
View File

@ -0,0 +1,186 @@
//===- OMUtils.cpp - OM Utility Functions ---------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "circt/Dialect/OM/OMUtils.h"
#include "circt/Dialect/OM/OMAttributes.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
using namespace circt;
using namespace om;
namespace {
struct PathParser {
enum class TokenKind {
Space,
Tilde,
Bar,
Colon,
Greater,
Slash,
LBrace,
RBrace,
Period,
Id,
Eof
};
struct Token {
TokenKind kind;
StringRef spelling;
};
Token formToken(TokenKind kind, size_t n) {
auto s = spelling.take_front(n);
spelling = spelling.drop_front(n);
return {kind, s};
}
bool isIDChar(char c) {
return c != ' ' && c != '~' && c != '|' && c != ':' && c != '>' &&
c != '/' && c != '[' && c != ']' && c != '.';
}
Token parseToken() {
size_t pos = 0;
auto size = spelling.size();
if (0 == size)
return formToken(TokenKind::Eof, pos);
auto current = spelling[pos];
switch (current) {
case '~':
return formToken(TokenKind::Tilde, pos + 1);
case '|':
return formToken(TokenKind::Bar, pos + 1);
case ':':
return formToken(TokenKind::Colon, pos + 1);
case '>':
return formToken(TokenKind::Greater, pos + 1);
case '/':
return formToken(TokenKind::Slash, pos + 1);
case '[':
return formToken(TokenKind::LBrace, pos + 1);
case ']':
return formToken(TokenKind::RBrace, pos + 1);
case '.':
return formToken(TokenKind::Period, pos + 1);
default:
break;
}
// Parsing a token.
while (pos != size && isIDChar(spelling[pos]))
++pos;
return formToken(TokenKind::Id, pos);
}
ParseResult parseToken(TokenKind kind, StringRef &result) {
auto save = spelling;
auto token = parseToken();
if (token.kind != kind)
return spelling = save, failure();
result = token.spelling;
return success();
}
ParseResult parseToken(TokenKind kind) {
StringRef ignore;
return parseToken(kind, ignore);
}
/// basepath ::= eof | id '/' id (':' id '/' id)* eof
ParseResult parseBasePath(PathAttr &pathAttr) {
if (succeeded(parseToken(TokenKind::Eof)))
return success();
SmallVector<PathElement> path;
while (true) {
StringRef module;
StringRef instance;
if (parseToken(TokenKind::Id, module) || parseToken(TokenKind::Slash) ||
parseToken(TokenKind::Id, instance))
return failure();
path.emplace_back(StringAttr::get(context, module),
StringAttr::get(context, instance));
if (parseToken(TokenKind::Colon))
break;
}
pathAttr = PathAttr::get(context, path);
if (parseToken(TokenKind::Eof))
return failure();
return success();
}
/// path ::= id ('/' id ':' path | ('>' id ('[' id ']' | '.' id)* )?) eof
ParseResult parsePath(PathAttr &pathAttr, StringAttr &moduleAttr,
StringAttr &refAttr, StringAttr &fieldAttr) {
SmallVector<PathElement> path;
StringRef module;
while (true) {
if (parseToken(TokenKind::Id, module))
return failure();
if (parseToken(TokenKind::Slash))
break;
StringRef instance;
if (parseToken(TokenKind::Id, instance) || parseToken(TokenKind::Colon))
return failure();
path.emplace_back(StringAttr::get(context, module),
StringAttr::get(context, instance));
}
pathAttr = PathAttr::get(context, path);
moduleAttr = StringAttr::get(context, module);
if (succeeded(parseToken(TokenKind::Greater))) {
StringRef ref;
if (parseToken(TokenKind::Id, ref))
return failure();
refAttr = StringAttr::get(context, ref);
SmallString<64> field;
while (true) {
if (succeeded(parseToken(TokenKind::LBrace))) {
StringRef id;
if (parseToken(TokenKind::Id, id) || parseToken(TokenKind::RBrace))
return failure();
field += '[';
field += id;
field += ']';
} else if (succeeded(parseToken(TokenKind::Period))) {
StringRef id;
if (parseToken(TokenKind::Id, id))
return failure();
field += '.';
field += id;
} else {
break;
}
}
fieldAttr = StringAttr::get(context, field);
} else {
refAttr = StringAttr::get(context, "");
fieldAttr = StringAttr::get(context, "");
}
if (parseToken(TokenKind::Eof))
return failure();
return success();
}
MLIRContext *context;
StringRef spelling;
};
} // namespace
ParseResult circt::om::parseBasePath(MLIRContext *context, StringRef spelling,
PathAttr &path) {
return PathParser{context, spelling}.parseBasePath(path);
}
ParseResult circt::om::parsePath(MLIRContext *context, StringRef spelling,
PathAttr &path, StringAttr &module,
StringAttr &ref, StringAttr &field) {
return PathParser{context, spelling}.parsePath(path, module, ref, field);
}

View File

@ -25,30 +25,38 @@ namespace {
struct PathVisitor {
PathVisitor(hw::InstanceGraph &instanceGraph, hw::InnerRefNamespace &irn)
: instanceGraph(instanceGraph), irn(irn) {}
LogicalResult processPath(PathOp path);
LogicalResult run(Operation *op);
StringAttr field;
LogicalResult processPath(Location loc, hw::HierPathOp hierPathOp,
PathAttr &targetPath, StringAttr &bottomModule,
StringAttr &component, StringAttr &field);
LogicalResult process(BasePathCreateOp pathOp);
LogicalResult process(PathCreateOp pathOp);
LogicalResult process(EmptyPathOp pathOp);
LogicalResult run(ModuleOp module);
hw::InstanceGraph &instanceGraph;
hw::InnerRefNamespace &irn;
};
} // namespace
static LogicalResult getAccessPath(Location loc, Type type, size_t fieldId,
SmallVectorImpl<char> &result) {
StringAttr &result) {
SmallString<64> field;
while (fieldId) {
if (auto aliasType = dyn_cast<hw::TypeAliasType>(type))
type = aliasType.getCanonicalType();
if (auto structType = dyn_cast<hw::StructType>(type)) {
auto index = structType.getIndexForFieldID(fieldId);
auto &element = structType.getElements()[index];
result.push_back('.');
llvm::append_range(result, element.name.getValue());
field.push_back('.');
llvm::append_range(field, element.name.getValue());
type = element.type;
fieldId -= structType.getFieldID(index);
} else if (auto arrayType = dyn_cast<hw::ArrayType>(type)) {
auto index = arrayType.getIndexForFieldID(fieldId);
result.push_back('[');
Twine(index).toVector(result);
result.push_back(']');
field.push_back('[');
Twine(index).toVector(field);
field.push_back(']');
type = arrayType.getElementType();
fieldId -= arrayType.getFieldID(index);
} else {
@ -56,47 +64,19 @@ static LogicalResult getAccessPath(Location loc, Type type, size_t fieldId,
<< fieldId << " in type " << type;
}
}
result = StringAttr::get(loc->getContext(), field);
return success();
}
LogicalResult PathVisitor::processPath(PathOp path) {
auto *context = path->getContext();
auto &symbolTable = irn.symTable;
StringRef targetKind;
switch (path.getTargetKind()) {
case TargetKind::DontTouch:
targetKind = "OMDontTouchedReferenceTarget";
break;
case TargetKind::Instance:
targetKind = "OMInstanceTarget";
break;
case TargetKind::Reference:
targetKind = "OMReferenceTarget";
break;
case TargetKind::MemberReference:
targetKind = "OMMemberReferenceTarget";
break;
case TargetKind::MemberInstance:
targetKind = "OMMemberInstanceTarget";
break;
}
LogicalResult PathVisitor::processPath(Location loc, hw::HierPathOp hierPathOp,
PathAttr &targetPath,
StringAttr &bottomModule,
StringAttr &component,
StringAttr &field) {
auto *context = hierPathOp->getContext();
// Look up the associated HierPathOp.
auto hierPathOp =
symbolTable.lookup<hw::HierPathOp>(path.getTargetAttr().getAttr());
auto namepath = hierPathOp.getNamepathAttr().getValue();
// The name of the module which starts off the path.
StringAttr topModule;
// The path from the top module to the target. Represents a pair of instance
// and its target module's name.
SmallVector<std::pair<StringAttr, StringAttr>> modules;
// If we're targeting a component or port of the target module, this will hold
// its name.
StringAttr component;
// If we're indexing in to the component, this will be the access path.
SmallString<64> field;
SmallVector<PathElement> modules;
// Process the final target first.
auto &end = namepath.back();
@ -106,29 +86,36 @@ LogicalResult PathVisitor::processPath(PathOp path) {
// We are targeting the port of a module.
auto module = cast<hw::HWModuleLike>(target.getOp());
auto index = target.getPort();
bottomModule = module.getModuleNameAttr();
component = StringAttr::get(context, module.getPortName(index));
auto loc = module.getPortLoc(index);
auto type = module.getPortTypes()[index];
if (failed(getAccessPath(loc, type, target.getField(), field)))
return failure();
topModule = module.getModuleNameAttr();
} else {
auto *op = target.getOp();
assert(op && "innerRef should be targeting something");
// Get the current module.
topModule = innerRef.getModule();
auto currentModule = innerRef.getModule();
// Get the verilog name of the target.
auto verilogName = op->getAttrOfType<StringAttr>("hw.verilogName");
if (!verilogName) {
auto diag = path->emitError("component does not have verilog name");
auto diag = emitError(loc, "component does not have verilog name");
diag.attachNote(op->getLoc()) << "component here";
return diag;
}
// If this is our inner ref pair: [Foo::bar]
// if "bar" is an instance, modules = [Foo::bar], bottomModule = Bar.
// if "bar" is a wire, modules = [], bottomModule = Foo, component = bar.
if (auto inst = dyn_cast<hw::HWInstanceLike>(op)) {
// We are targeting an instance.
modules.emplace_back(verilogName, inst.getReferencedModuleNameAttr());
modules.emplace_back(currentModule, verilogName);
bottomModule = inst.getReferencedModuleNameAttr();
component = StringAttr::get(context, "");
field = StringAttr::get(context, "");
} else {
// We are targeting a regular component.
bottomModule = currentModule;
component = verilogName;
auto innerSym = cast<hw::InnerSymbolOpInterface>(op);
auto value = innerSym.getTargetResult();
@ -140,18 +127,34 @@ LogicalResult PathVisitor::processPath(PathOp path) {
} else {
// We are targeting a module.
auto symbolRef = cast<FlatSymbolRefAttr>(end);
topModule = symbolRef.getAttr();
bottomModule = symbolRef.getAttr();
component = StringAttr::get(context, "");
field = StringAttr::get(context, "");
}
// Process the rest of the hierarchical path.
for (auto attr : llvm::reverse(namepath.drop_back())) {
auto innerRef = cast<hw::InnerRefAttr>(attr);
modules.emplace_back(innerRef.getName(), topModule);
topModule = innerRef.getModule();
auto target = irn.lookup(innerRef);
assert(target.isOpOnly() && "can't target a port the middle of a namepath");
auto *op = target.getOp();
// Get the verilog name of the target.
auto verilogName = op->getAttrOfType<StringAttr>("hw.verilogName");
if (!verilogName) {
auto diag = emitError(loc, "component does not have verilog name");
diag.attachNote(op->getLoc()) << "component here";
return diag;
}
modules.emplace_back(innerRef.getModule(), verilogName);
}
auto topRef = namepath.front();
auto topModule = isa<hw::InnerRefAttr>(topRef)
? cast<hw::InnerRefAttr>(topRef).getModule()
: cast<FlatSymbolRefAttr>(topRef).getAttr();
// Handle the modules not present in the path.
assert(topModule && "must have something?");
auto *node = instanceGraph.lookup(topModule);
while (!node->noUses()) {
auto module = node->getModule<hw::HWModuleLike>();
@ -161,68 +164,122 @@ LogicalResult PathVisitor::processPath(PathOp path) {
break;
if (!node->hasOneUse()) {
auto diag = path->emitError() << "unable to uniquely resolve target "
"due to multiple instantiation";
auto diag = emitError(loc) << "unable to uniquely resolve target "
"due to multiple instantiation";
for (auto *use : node->uses()) {
if (auto *op = use->getInstance<Operation *>())
diag.attachNote(op->getLoc()) << "instance here";
else
else {
auto module = node->getModule();
diag.attachNote(module->getLoc()) << "module marked public";
}
}
return diag;
}
// Get the single instance of this module.
auto *record = *node->usesBegin();
// Get the verilog name of the instance.
auto *inst = record->getInstance<Operation *>();
// If the instance is external, just break here.
if (!inst)
break;
// Get the verilog name of the instance.
auto verilogName = inst->getAttrOfType<StringAttr>("hw.verilogName");
if (!verilogName)
return inst->emitError("component does not have verilog name");
modules.emplace_back(verilogName, module.getModuleNameAttr());
node = record->getParent();
modules.emplace_back(node->getModule().getModuleNameAttr(), verilogName);
}
// We are finally at the top of the instance graph.
topModule = node->getModule().getModuleNameAttr();
// Create the target path.
targetPath = PathAttr::get(context, llvm::to_vector(llvm::reverse(modules)));
return success();
}
// Create the target string.
SmallString<128> targetString;
targetString.append(targetKind);
targetString.append(":");
targetString.append(topModule);
if (!modules.empty())
targetString.append("/");
for (auto [instance, module] : llvm::reverse(modules)) {
targetString.append(instance);
targetString.append(":");
targetString.append(module);
}
if (component) {
targetString.append(">");
targetString.append(component);
}
targetString.append(field);
auto targetPath = PathAttr::get(StringAttr::get(context, targetString));
LogicalResult PathVisitor::process(PathCreateOp path) {
auto hierPathOp =
irn.symTable.lookup<hw::HierPathOp>(path.getTargetAttr().getAttr());
PathAttr targetPath;
StringAttr bottomModule;
StringAttr ref;
StringAttr field;
if (failed(processPath(path.getLoc(), hierPathOp, targetPath, bottomModule,
ref, field)))
return failure();
// Replace the old path operation.
OpBuilder builder(path);
auto constantOp = builder.create<ConstantOp>(path.getLoc(), targetPath);
path.replaceAllUsesWith(constantOp.getResult());
auto frozenPath = builder.create<FrozenPathCreateOp>(
path.getLoc(), path.getTargetKindAttr(), path->getOperand(0), targetPath,
bottomModule, ref, field);
path.replaceAllUsesWith(frozenPath.getResult());
path->erase();
return success();
}
LogicalResult PathVisitor::process(BasePathCreateOp path) {
auto hierPathOp =
irn.symTable.lookup<hw::HierPathOp>(path.getTargetAttr().getAttr());
PathAttr targetPath;
StringAttr bottomModule;
StringAttr ref;
StringAttr field;
if (failed(processPath(path.getLoc(), hierPathOp, targetPath, bottomModule,
ref, field)))
return failure();
if (!ref.empty())
return path->emitError("basepath must target an instance");
if (!field.empty())
return path->emitError("basepath must not target a field");
// Replace the old path operation.
OpBuilder builder(path);
auto frozenPath = builder.create<FrozenBasePathCreateOp>(
path.getLoc(), path->getOperand(0), targetPath);
path.replaceAllUsesWith(frozenPath.getResult());
path->erase();
return success();
}
LogicalResult PathVisitor::process(EmptyPathOp path) {
OpBuilder builder(path);
auto frozenPath = builder.create<FrozenEmptyPathOp>(path.getLoc());
path.replaceAllUsesWith(frozenPath.getResult());
path->erase();
return success();
}
LogicalResult PathVisitor::run(Operation *op) {
auto result = op->walk([&](PathOp path) -> WalkResult {
if (failed(processPath(path)))
return WalkResult::interrupt();
return WalkResult::advance();
});
if (result.wasInterrupted())
return failure();
LogicalResult PathVisitor::run(ModuleOp module) {
auto frozenBasePathType = FrozenBasePathType::get(module.getContext());
auto frozenPathType = FrozenPathType::get(module.getContext());
auto updatePathType = [&](Value value) {
if (isa<BasePathType>(value.getType()))
value.setType(frozenBasePathType);
if (isa<PathType>(value.getType()))
value.setType(frozenPathType);
};
for (auto classLike : module.getOps<ClassLike>()) {
// Transform PathType block argument to FrozenPathType.
for (auto arg : classLike.getBodyBlock()->getArguments())
updatePathType(arg);
auto result = classLike->walk([&](Operation *op) -> WalkResult {
if (auto basePath = dyn_cast<BasePathCreateOp>(op)) {
if (failed(process(basePath)))
return WalkResult::interrupt();
} else if (auto path = dyn_cast<PathCreateOp>(op)) {
if (failed(process(path)))
return WalkResult::interrupt();
} else if (auto path = dyn_cast<EmptyPathOp>(op)) {
if (failed(process(path)))
return WalkResult::interrupt();
}
return WalkResult::advance();
});
if (result.wasInterrupted())
return failure();
}
return success();
}

View File

@ -2,7 +2,7 @@
firrtl.circuit "Component" {
// CHECK-LABEL: om.class @Class_0
// CHECK-SAME: %[[REF1:.+]]: !om.class.type<@Class_1>
// CHECK-SAME: %[[REF1:[^ ]+]]: !om.class.type<@Class_1>
firrtl.class private @Class_0(in %someReference_in: !firrtl.class<@Class_1(out someInt: !firrtl.integer)>, out %someReference: !firrtl.class<@Class_1(out someInt: !firrtl.integer)>) {
// CHECK: om.class.field @someReference, %[[REF1]]
firrtl.propassign %someReference, %someReference_in : !firrtl.class<@Class_1(out someInt: !firrtl.integer)>
@ -24,17 +24,17 @@ firrtl.circuit "Component" {
firrtl.propassign %someString, %0 : !firrtl.string
}
// CHECK-LABEL: om.class.extern @ExtClass(%input: !om.string) {
// CHECK-LABEL: om.class.extern @ExtClass(%basepath: !om.basepath, %input: !om.string) {
// CHECK-NEXT: om.class.extern.field @field : !om.string
// CHECK-NEXT: }
firrtl.extclass private @ExtClass(in input: !firrtl.string, out field: !firrtl.string)
// CHECK-LABEL: om.class @ClassEntrypoint
firrtl.class private @ClassEntrypoint(out %obj_0_out: !firrtl.class<@Class_1(out someInt: !firrtl.integer)>) {
// CHECK: %[[OBJ1:.+]] = om.object @Class_1() : () -> !om.class.type<@Class_1>
// CHECK: %[[OBJ1:.+]] = om.object @Class_1(%basepath) : (!om.basepath) -> !om.class.type<@Class_1>
%obj1 = firrtl.object @Class_1(out someInt: !firrtl.integer)
// CHECK: %[[OBJ0:.+]] = om.object @Class_0(%[[OBJ1]]) : (!om.class.type<@Class_1>) -> !om.class.type<@Class_0>
// CHECK: %[[OBJ0:.+]] = om.object @Class_0(%basepath, %[[OBJ1]]) : (!om.basepath, !om.class.type<@Class_1>) -> !om.class.type<@Class_0>
%obj0 = firrtl.object @Class_0(in someReference_in: !firrtl.class<@Class_1(out someInt: !firrtl.integer)>, out someReference: !firrtl.class<@Class_1(out someInt: !firrtl.integer)>)
%obj0_someReference_in = firrtl.object.subfield %obj0[someReference_in] : !firrtl.class<@Class_0(in someReference_in: !firrtl.class<@Class_1(out someInt: !firrtl.integer)>, out someReference: !firrtl.class<@Class_1(out someInt: !firrtl.integer)>)>
firrtl.propassign %obj0_someReference_in, %obj1 : !firrtl.class<@Class_1(out someInt: !firrtl.integer)>
@ -45,9 +45,9 @@ firrtl.circuit "Component" {
firrtl.propassign %obj_0_out, %obj0_someReference: !firrtl.class<@Class_1(out someInt: !firrtl.integer)>
}
// CHECK-LABEL: om.class @ReadOutputPort()
// CHECK-LABEL: om.class @ReadOutputPort(%basepath: !om.basepath)
firrtl.class @ReadOutputPort(out %output : !firrtl.integer) {
// CHECK: %[[OBJ:.+]] = om.object @Class_1() : () -> !om.class.type<@Class_1>
// CHECK: %[[OBJ:.+]] = om.object @Class_1(%basepath) : (!om.basepath) -> !om.class.type<@Class_1>
// CHECK: %[[FIELD:.+]] = om.object.field %[[OBJ]], [@someInt] : (!om.class.type<@Class_1>) -> !om.integer
// CHECK: om.class.field @output, %[[FIELD]] : !om.integer
%obj = firrtl.object @Class_1(out someInt: !firrtl.integer)
@ -60,7 +60,7 @@ firrtl.circuit "Component" {
firrtl.class @AssignInputsInOrder() {
// CHECK: %0 = om.constant #om.integer<123 : si12> : !om.integer
// CHECK: %1 = om.constant #om.integer<456 : si12> : !om.integer
// CHECK: %2 = om.object @TwoInputs(%0, %1) : (!om.integer, !om.integer) -> !om.class.type<@TwoInputs>
// CHECK: %2 = om.object @TwoInputs(%basepath, %0, %1) : (!om.basepath, !om.integer, !om.integer) -> !om.class.type<@TwoInputs>
%x = firrtl.integer 123
%y = firrtl.integer 456
%obj = firrtl.object @TwoInputs(in a: !firrtl.integer, in b: !firrtl.integer)
@ -75,7 +75,7 @@ firrtl.circuit "Component" {
firrtl.class @AssignInputsOutOfOrder() {
// CHECK: %0 = om.constant #om.integer<123 : si12> : !om.integer
// CHECK: %1 = om.constant #om.integer<456 : si12> : !om.integer
// CHECK: %2 = om.object @TwoInputs(%0, %1) : (!om.integer, !om.integer) -> !om.class.type<@TwoInputs>
// CHECK: %2 = om.object @TwoInputs(%basepath, %0, %1) : (!om.basepath, !om.integer, !om.integer) -> !om.class.type<@TwoInputs>
%x = firrtl.integer 123
%y = firrtl.integer 456
%obj = firrtl.object @TwoInputs(in a: !firrtl.integer, in b: !firrtl.integer)
@ -135,7 +135,7 @@ firrtl.circuit "Component" {
// CHECK-NEXT: om.class.field @out_objs, %[[OBJS]] : !om.list<!om.class.type<@ClassTest>
}
// CHECK-LABEL: om.class @BoolTest()
// CHECK-LABEL: om.class @BoolTest
firrtl.class @BoolTest(out %b : !firrtl.bool) {
// CHECK-NEXT: %[[TRUE:.+]] = om.constant true
// CHECK-NEXT: om.class.field @b, %[[TRUE]] : i1
@ -143,7 +143,7 @@ firrtl.circuit "Component" {
firrtl.propassign %b, %true : !firrtl.bool
}
// CHECK-LABEL: om.class @DoubleTest()
// CHECK-LABEL: om.class @DoubleTest
firrtl.class @DoubleTest(out %d : !firrtl.double) {
// CHECK-NEXT: %[[DBL:.+]] = om.constant 4.0{{0*[eE]}}-01 : f64
// CHECK-NEXT: om.class.field @d, %[[DBL]] : f64
@ -217,38 +217,38 @@ firrtl.circuit "PathModule" {
// CHECK: %non_local = firrtl.wire sym [[NONLOCAL_SYM]] : !firrtl.uint<8>
%non_local = firrtl.wire {annotations = [{circt.nonlocal = @NonLocal, class = "circt.tracker", id = distinct[3]<>}]} : !firrtl.uint<8>
}
// CHECK: om.class @PathTest
// CHECK: om.class @PathTest(%basepath: !om.basepath)
firrtl.class @PathTest() {
// CHECK: om.path reference [[PORT_PATH]]
// CHECK: om.path_create reference %basepath [[PORT_PATH]]
%port_path = firrtl.path reference distinct[0]<>
// CHECK: om.constant #om.path<"OMDeleted"> : !om.path
// CHECK: om.path_empty
%deleted_path = firrtl.path reference distinct[99]<>
// CHECK: om.path reference [[WIRE_PATH]]
// CHECK: om.path member_reference [[WIRE_PATH]]
// CHECK: om.path member_reference [[WIRE_PATH]]
// CHECK: om.path dont_touch [[WIRE_PATH]]
// CHECK: om.path_create reference %basepath [[WIRE_PATH]]
// CHECK: om.path_create member_reference %basepath [[WIRE_PATH]]
// CHECK: om.path_create member_reference %basepath [[WIRE_PATH]]
// CHECK: om.path_create dont_touch %basepath [[WIRE_PATH]]
%wire_reference = firrtl.path reference distinct[1]<>
%wire_member_instance = firrtl.path member_instance distinct[1]<>
%wire_member_reference = firrtl.path member_reference distinct[1]<>
%wire_dont_touch = firrtl.path dont_touch distinct[1]<>
// CHECK: om.path reference [[VECTOR_PATH]]
// CHECK: om.path_create reference %basepath [[VECTOR_PATH]]
%vector_reference = firrtl.path reference distinct[2]<>
// CHECK: om.path reference [[NONLOCAL_PATH]]
// CHECK: om.path_create reference %basepath [[NONLOCAL_PATH]]
%non_local_path = firrtl.path reference distinct[3]<>
// CHECK: om.path member_instance [[INST_PATH]]
// CHECK: om.path member_instance [[INST_PATH]]
// CHECK: om.path instance [[INST_PATH]]
// CHECK: om.path_create member_instance %basepath [[INST_PATH]]
// CHECK: om.path_create member_instance %basepath [[INST_PATH]]
// CHECK: om.path_create instance %basepath [[INST_PATH]]
%instance_member_instance = firrtl.path member_instance distinct[4]<>
%instance_member_reference = firrtl.path member_reference distinct[4]<>
%instance = firrtl.path instance distinct[4]<>
// CHECK: om.path reference [[MODULE_PATH]]
// CHECK: om.path_create reference %basepath [[MODULE_PATH]]
%module_path = firrtl.path reference distinct[5]<>
}
firrtl.module @ListCreate(in %propIn: !firrtl.integer, out %propOut: !firrtl.list<integer>) attributes {convention = #firrtl<convention scalarized>} {
@ -264,7 +264,7 @@ firrtl.circuit "PathModule" {
// CHECK-LABEL: firrtl.circuit "WireProp"
firrtl.circuit "WireProp" {
// CHECK: om.class @WireProp
// CHECK-SAME: %[[IN:.+]]: !om.string
// CHECK-SAME: %[[IN:[^ ]+]]: !om.string
firrtl.module @WireProp(in %in: !firrtl.string, out %out: !firrtl.string) attributes {convention = #firrtl<convention scalarized>} {
// CHECK-NOT: firrtl.wire
// CHECK-NOT: firrtl.propassign
@ -329,20 +329,20 @@ firrtl.circuit "ModuleInstances" {
firrtl.propassign %outputProp, %mod.outputProp : !firrtl.string
}
// CHECK: om.class.extern @ExtModule_Class(%inputProp: !om.string)
// CHECK: om.class.extern @ExtModule_Class(%basepath: !om.basepath, %inputProp: !om.string)
// CHECK: om.class.extern.field @outputProp : !om.string
// CHECK: om.class.extern @TheRealName_Class(%inputProp: !om.string)
// CHECK: om.class.extern @TheRealName_Class(%basepath: !om.basepath, %inputProp: !om.string)
// CHECK: om.class.extern.field @outputProp : !om.string
// CHECK: om.class @Module_Class(%[[IN_PROP0:.+]]: !om.string)
// CHECK: om.class @Module_Class(%basepath: !om.basepath, %[[IN_PROP0:.+]]: !om.string)
// CHECK: om.class.field @outputProp, %[[IN_PROP0]] : !om.string
// CHECK: om.class @ModuleInstances_Class(%[[IN_PROP1:.+]]: !om.string)
// CHECK: %[[O0:.+]] = om.object @ExtModule_Class(%[[IN_PROP1]])
// CHECK: om.class @ModuleInstances_Class(%basepath: !om.basepath, %[[IN_PROP1:.+]]: !om.string)
// CHECK: %[[O0:.+]] = om.object @ExtModule_Class(%basepath, %[[IN_PROP1]])
// CHECK: %[[F0:.+]] = om.object.field %[[O0]], [@outputProp]
// CHECK: om.object @TheRealName_Class
// CHECK: %[[O1:.+]] = om.object @Module_Class(%[[F0]])
// CHECK: %[[O1:.+]] = om.object @Module_Class(%basepath, %[[F0]])
// CHECK: %[[F1:.+]] = om.object.field %[[O1]], [@outputProp]
// CHECK: om.class.field @outputProp, %[[F1]]
}

View File

@ -120,7 +120,7 @@ om.class @MapConstant() {
// -----
om.class @Thing() { }
om.class @BadPath() {
om.class @BadPath(%basepath: !om.basepath) {
// expected-error @below {{invalid symbol reference}}
%0 = om.path reference @Thing
%0 = om.path_create reference %basepath @Thing
}

View File

@ -4,21 +4,37 @@ hw.hierpath private @nla [@Top::@sym]
hw.module @Top() {
// expected-note @below {{component here}}
%wire = hw.wire %wire sym @sym : i8
// expected-error @below {{component does not have verilog name}}
%path = om.path reference @nla
hw.output
}
om.class @OM(%basepath: !om.basepath) {
// expected-error @below {{component does not have verilog name}}
%path = om.path_create reference %basepath @nla
}
// -----
hw.hierpath private @nla [@Child]
hw.module private @Child() {}
hw.module @Top() {
// expected-error @below {{unable to uniquely resolve target due to multiple instantiation}}
%path = om.path reference @nla
// expected-note @below {{instance here}}
hw.instance "child0" @Child() -> ()
// expected-note @below {{instance here}}
hw.instance "child1" @Child() -> ()
hw.output
}
om.class @OM(%basepath: !om.basepath) {
// expected-error @below {{unable to uniquely resolve target due to multiple instantiation}}
%path = om.path_create reference %basepath @nla
}
// -----
hw.hierpath private @nla [@Top::@sym]
hw.module @Top() {
%wire = hw.wire %wire sym @sym {hw.verilogName = "wire"} : i8
hw.output
}
om.class @OM(%basepath: !om.basepath) {
// expected-error @below {{basepath must target an instance}}
%path = om.basepath_create %basepath @nla
}

View File

@ -29,37 +29,43 @@ hw.module @PublicMiddle() {
hw.module private @PublicLeaf() {}
// CHECK-LABEL om.class @PathTest()
om.class @PathTest() {
// CHECK: om.constant #om.path<"OMReferenceTarget:PathModule>in"> : !om.path
%0 = om.path reference @nla
om.class @PathTest(%basepath : !om.basepath, %path : !om.path) {
// CHECK: om.frozenpath_create reference %basepath "PathModule>in"
%0 = om.path_create reference %basepath @nla
// CHECK: om.constant #om.path<"OMReferenceTarget:PathModule>wire"> : !om.path
%1 = om.path reference @nla_0
// CHECK: om.frozenpath_create reference %basepath "PathModule>wire"
%1 = om.path_create reference %basepath @nla_0
// CHECK: om.constant #om.path<"OMMemberReferenceTarget:PathModule>wire"> : !om.path
%2 = om.path member_reference @nla_0
// CHECK: om.frozenpath_create member_reference %basepath "PathModule>wire"
%2 = om.path_create member_reference %basepath @nla_0
// CHECK: om.constant #om.path<"OMDontTouchedReferenceTarget:PathModule>wire"> : !om.path
%4 = om.path dont_touch @nla_0
// CHECK: om.frozenpath_create dont_touch %basepath "PathModule>wire"
%4 = om.path_create dont_touch %basepath @nla_0
// CHECK: om.constant #om.path<"OMReferenceTarget:PathModule>array[0]"> : !om.path
%5 = om.path reference @nla_1
// CHECK: om.frozenpath_create reference %basepath "PathModule>array[0]"
%5 = om.path_create reference %basepath @nla_1
// CHECK: om.constant #om.path<"OMReferenceTarget:PathModule>struct.a"> : !om.path
%6 = om.path reference @nla_2
// CHECK: om.frozenpath_create reference %basepath "PathModule>struct.a"
%6 = om.path_create reference %basepath @nla_2
// CHECK: om.constant #om.path<"OMReferenceTarget:PathModule/child:Child>non_local"> : !om.path
%7 = om.path reference @nla_5
// CHECK: om.frozenpath_create reference %basepath "PathModule/child:Child>non_local"
%7 = om.path_create reference %basepath @nla_5
// CHECK: om.constant #om.path<"OMInstanceTarget:PathModule/child:Child"> : !om.path
%8 = om.path instance @nla_3
// CHECK: om.frozenpath_create instance %basepath "PathModule/child:Child"
%8 = om.path_create instance %basepath @nla_3
// CHECK: om.constant #om.path<"OMMemberInstanceTarget:PathModule/child:Child"> : !om.path
%9 = om.path member_instance @nla_3
// CHECK: om.frozenpath_create member_instance %basepath "PathModule/child:Child"
%9 = om.path_create member_instance %basepath @nla_3
// CHECK: om.constant #om.path<"OMReferenceTarget:PathModule/child:Child"> : !om.path
%10 = om.path reference @nla_4
// CHECK: om.frozenpath_create reference %basepath "PathModule/child:Child"
%10 = om.path_create reference %basepath @nla_4
// CHECK: om.constant #om.path<"OMReferenceTarget:PublicMiddle/leaf:PublicLeaf"> : !om.path
%11 = om.path reference @nla_6
// CHECK: om.frozenpath_create reference %basepath "PublicMiddle/leaf:PublicLeaf"
%11 = om.path_create reference %basepath @nla_6
// CHECK: om.frozenpath_empty
%12 = om.path_empty
// CHECK: om.frozenbasepath_create %basepath "PathModule/child"
%13 = om.basepath_create %basepath @nla_3
}

View File

@ -252,13 +252,18 @@ hw.module @PathModule() {
%wire = hw.wire %wire sym @wire : i1
}
// CHECK-LABEL: @Path
om.class @Path(%p: !om.path) {
// CHECK: %[[v0:.+]] = om.path reference @HierPath
%0 = om.path reference @HierPath
// CHECK: %[[v1:.+]] = om.path_append %p, %[[v0]]
%1 = om.path_append %p, %0
// CHECK: om.class.field @path1, %[[v1]] : !om.path
om.class.field @path1, %1 : !om.path
om.class @Path(%basepath: !om.basepath) {
// CHECK: %[[v0:.+]] = om.basepath_create %basepath @HierPath
%0 = om.basepath_create %basepath @HierPath
// CHECK: %[[v1:.+]] = om.path_create reference %basepath @HierPath
%1 = om.path_create reference %basepath @HierPath
}
om.class @FrozenPath(%basepath: !om.frozenbasepath) {
// CHECK: %[[v0:.+]] = om.frozenbasepath_create %basepath "Foo/bar"
%0 = om.frozenbasepath_create %basepath "Foo/bar"
// CHECK: %[[v1:.+]] = om.frozenpath_create reference %basepath "Foo/bar:Bar>w.a"
%1 = om.frozenpath_create reference %basepath "Foo/bar:Bar>w.a"
}
// CHECK-LABEL: @Enum