Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions lib/Dialect/FIRRTL/Transforms/LowerClasses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -377,14 +377,16 @@ PathTracker::getOrComputeNeedsAltBasePath(Location loc, StringAttr moduleName,
break;
}
// If there is more than one instance of this module, then the path
// operation is ambiguous, which is an error.
// operation is ambiguous, which is a warning. This should become an error
// once user code is properly enforcing single instantiation, but in
// practice this generates the same outputs as the original flow for now.
// See https://github.com/llvm/circt/issues/7128.
if (!node->hasOneUse()) {
auto diag = mlir::emitError(loc)
auto diag = mlir::emitWarning(loc)
<< "unable to uniquely resolve target due "
"to multiple instantiation";
for (auto *use : node->uses())
diag.attachNote(use->getInstance().getLoc()) << "instance here";
return diag;
}
node = (*node->usesBegin())->getParent();
}
Expand Down
150 changes: 33 additions & 117 deletions lib/Dialect/FIRRTL/Transforms/ResolvePaths.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,14 @@ struct PathResolver {
/// be a reference to the HierPathOp, or null if no HierPathOp was needed.
LogicalResult resolveHierPath(Location loc, FModuleOp owningModule,
const AnnoPathValue &target,
bool canDisambiguate,
SmallVectorImpl<FlatSymbolRefAttr> &results) {
FlatSymbolRefAttr &result) {

// We want to root this path at the top level module, or in the case of an
// unreachable module, we settle for as high as we can get.
auto module = target.ref.getModule();
if (!target.instances.empty())
module = target.instances.front()->getParentOfType<FModuleLike>();
auto *node = instanceGraph[module];
bool needsDisambiguation = false;
while (true) {
// If the path is rooted at the owning module, we're done.
if (node->getModule() == owningModule)
Expand All @@ -51,44 +49,30 @@ struct PathResolver {
<< "unable to resolve path relative to owning module "
<< owningModule.getModuleNameAttr();
// If there is more than one instance of this module, then the path
// operation is ambiguous.
// operation is ambiguous, which is a warning. This should become an error
// once user code is properly enforcing single instantiation, but in
// practice this generates the same outputs as the original flow for now.
// See https://github.com/llvm/circt/issues/7128.
if (!node->hasOneUse()) {
// If we can disambiguate by duplicating paths, stop here, otherwise
// this is an error.
if (canDisambiguate) {
needsDisambiguation = true;
break;
}

auto diag = emitError(loc) << "unable to uniquely resolve target due "
"to multiple instantiation";
auto diag = emitWarning(loc) << "unable to uniquely resolve target due "
"to multiple instantiation";
for (auto *use : node->uses())
diag.attachNote(use->getInstance().getLoc()) << "instance here";
return diag;
}
node = (*node->usesBegin())->getParent();
}

// Find the minimal uniquely-identifying path to the operation. We scan
// through the list of instances looking for the first module which is
// multiply instantiated. We will start our HierPathOp at this instance.
auto *it = llvm::find_if(target.instances, [&](InstanceOp instance) {
auto *node = instanceGraph.lookup(instance.getReferencedModuleNameAttr());
return !node->hasOneUse();
});

// If the path is empty and we don't need to disambiguate, then this is a
// local reference and we should not construct a HierPathOp.
auto pathLength = std::distance(it, target.instances.end());
if (pathLength == 0 && !needsDisambiguation) {
// If the path is empty then this is a local reference and we should not
// construct a HierPathOp.
if (target.instances.empty()) {
return success();
}

// Transform the instances into a list of FlatSymbolRefs.
SmallVector<Attribute> insts;
insts.reserve(pathLength);
std::transform(it, target.instances.end(), std::back_inserter(insts),
[&](InstanceOp instance) {
insts.reserve(target.instances.size());
std::transform(target.instances.begin(), target.instances.end(),
std::back_inserter(insts), [&](InstanceOp instance) {
return OpAnnoTarget(instance).getNLAReference(
namespaces[instance->getParentOfType<FModuleLike>()]);
});
Expand All @@ -97,45 +81,10 @@ struct PathResolver {
insts.push_back(
FlatSymbolRefAttr::get(target.ref.getModule().getModuleNameAttr()));

// If we need to disambiguate this path, create new paths with every unique
// prefix from the owning module to the point where the path suffix starts.
if (needsDisambiguation) {
ArrayRef<igraph::InstancePath> instancePaths =
instancePathCache.getAbsolutePaths(node->getModule(),
instanceGraph[owningModule]);
for (auto instancePath : instancePaths) {
// For each unique prefix from the owning module, build up a new path.
SmallVector<Attribute> fullInsts;
fullInsts.reserve(instancePath.size() + target.instances.size() + 1);
std::transform(
instancePath.begin(), instancePath.end(),
std::back_inserter(fullInsts),
[&](igraph::InstanceOpInterface inst) {
return OpAnnoTarget(cast<InstanceOp>(inst))
.getNLAReference(
namespaces[inst->getParentOfType<FModuleLike>()]);
});

// If present, include the start of the target's instance path to tie
// the prefix to the suffix.
if (!target.instances.empty())
for (InstanceOp inst : target.instances)
fullInsts.push_back(OpAnnoTarget(inst).getNLAReference(
namespaces[inst->getParentOfType<FModuleOp>()]));

// Include the suffix, starting from the module which was multiply
// instantiated.
fullInsts.append(insts);

// Return the disambiguated hierchical path.
auto instAttr = ArrayAttr::get(circuit.getContext(), fullInsts);
results.push_back(hierPathCache.getRefFor(instAttr));
}
} else {
// Return the one hierchical path.
auto instAttr = ArrayAttr::get(circuit.getContext(), insts);
results.push_back(hierPathCache.getRefFor(instAttr));
}
// Return the hierchical path.
auto instAttr = ArrayAttr::get(circuit.getContext(), insts);

result = hierPathCache.getRefFor(instAttr);

return success();
}
Expand Down Expand Up @@ -220,22 +169,12 @@ struct PathResolver {
if (!owningModule)
return unresolved->emitError("path does not have a single owning module");

// If the UnresolvedPathOp is ambiguous due to multiple instantiation, we
// can disambiguate by expanding it into multiple unambiguous paths, but
// only if the UnresolvedPathOp is already being used in a list.
bool canDisambiguate =
!unresolved->use_empty() &&
llvm::all_of(unresolved->getUsers(),
[](Operation *user) { return isa<ListCreateOp>(user); });

// Resolve a unique path to the operation in question, or multiple paths if
// necessary and it is legal to disambiguate.
SmallVector<FlatSymbolRefAttr> hierPathNames;
if (failed(resolveHierPath(loc, owningModule, *path, canDisambiguate,
hierPathNames)))
// Resolve a path to the operation in question.
FlatSymbolRefAttr hierPathName;
if (failed(resolveHierPath(loc, owningModule, *path, hierPathName)))
return failure();

auto createAnnotation = [&](std::optional<FlatSymbolRefAttr> hierPathName) {
auto createAnnotation = [&](FlatSymbolRefAttr hierPathName) {
// Create a unique ID.
auto id = DistinctAttr::create(UnitAttr::get(context));

Expand All @@ -244,56 +183,33 @@ struct PathResolver {
fields.append("id", id);
fields.append("class", StringAttr::get(context, "circt.tracker"));
if (hierPathName)
fields.append("circt.nonlocal", hierPathName.value());
fields.append("circt.nonlocal", hierPathName);
if (path->fieldIdx != 0)
fields.append("circt.fieldID", b.getI64IntegerAttr(path->fieldIdx));

return DictionaryAttr::get(context, fields);
};

// Create the annotation(s).
SmallVector<Attribute> annotations;
if (hierPathNames.empty()) {
annotations.push_back(createAnnotation(std::nullopt));
} else {
for (auto hierPathName : hierPathNames)
annotations.push_back(createAnnotation(hierPathName));
}
Attribute annotation = createAnnotation(hierPathName);

// Attach the annotation(s) to the target.
auto annoTarget = path->ref;
auto targetAnnotations = annoTarget.getAnnotations();
targetAnnotations.addAnnotations(annotations);
targetAnnotations.addAnnotations({annotation});
if (targetKindAttr.getValue() == TargetKind::DontTouch)
targetAnnotations.addDontTouch();
annoTarget.setAnnotations(targetAnnotations);

// Create the path operation(s).
size_t lastAnnotationIdx = annotations.size() - 1;
for (auto [i, annotation] : llvm::enumerate(annotations)) {
// Create a PathOp using the id in the annotation we added to the target.
auto dictAttr = cast<DictionaryAttr>(annotation);
auto id = cast<DistinctAttr>(dictAttr.get("id"));
auto resolved = b.create<PathOp>(targetKindAttr, id);

if (!canDisambiguate || i == lastAnnotationIdx) {
// If we didn't need to disambiguate, always just replace the unresolved
// path with the resolved path. If we did disambiguate, and this is the
// last path, replace the unresolved path. In the case of multiple
// paths, we save the final replacement for last so the other paths can
// find the uses to update.
unresolved->replaceAllUsesWith(resolved);
unresolved.erase();
} else {
// If we are handling one of several paths after disambiguation, insert
// a use of the new path next to where the unresolved path was used.
for (auto &use : unresolved->getUses()) {
assert(isa<ListCreateOp>(use.getOwner()));
use.getOwner()->insertOperands(use.getOperandNumber(),
resolved.getResult());
}
}
}
// Create a PathOp using the id in the annotation we added to the target.
auto dictAttr = cast<DictionaryAttr>(annotation);
auto id = cast<DistinctAttr>(dictAttr.get("id"));
auto resolved = b.create<PathOp>(targetKindAttr, id);

// Replace the unresolved path with the PathOp.
unresolved->replaceAllUsesWith(resolved);
unresolved.erase();

return success();
}

Expand Down
2 changes: 1 addition & 1 deletion test/Dialect/FIRRTL/resolve-paths-errors.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ firrtl.module @VectorTarget(in %a : !firrtl.vector<uint<1>, 1>) {

firrtl.circuit "AmbiguousPath" {
firrtl.module @AmbiguousPath() {
// expected-error @below {{unable to uniquely resolve target due to multiple instantiation}}
// expected-warning @below {{unable to uniquely resolve target due to multiple instantiation}}
%0 = firrtl.unresolved_path "OMReferenceTarget:~AmbiguousPath|Child"
// expected-note @below {{instance here}}
firrtl.instance child0 @Child()
Expand Down
63 changes: 28 additions & 35 deletions test/Dialect/FIRRTL/resolve-paths.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,13 @@ firrtl.circuit "PathMinimization" {
// CHECK: firrtl.module @PathMinimization()
firrtl.module @PathMinimization() {
// CHECK: %0 = firrtl.path reference distinct[0]<>
// CHECK: firrtl.instance child @Child()
// CHECK: firrtl.instance child {{.+}} @Child()
%0 = firrtl.unresolved_path "OMReferenceTarget:~PathMinimization|PathMinimization/child:Child>wire"
firrtl.instance child @Child()
}
// CHECK: firrtl.module @Child()
firrtl.module @Child() {
// CHECK: %wire = firrtl.wire {annotations = [{class = "circt.tracker", id = distinct[0]<>}]} : !firrtl.uint<8>
// CHECK: %wire = firrtl.wire {annotations = [{{{.+}} class = "circt.tracker", id = distinct[0]<>}]} : !firrtl.uint<8>
%wire = firrtl.wire : !firrtl.uint<8>
}
}
Expand All @@ -116,16 +116,16 @@ firrtl.module @TargetInstance() {
// CHECK: %0 = firrtl.path reference distinct[0]<>
// CHECK: %1 = firrtl.path instance distinct[1]<>
// CHECK: %2 = firrtl.path member_instance distinct[2]<>
// CHECK: firrtl.instance child @Child()
// CHECK: firrtl.instance child {{.+}} @Child()
%0 = firrtl.unresolved_path "OMReferenceTarget:~TargetInstance|TargetInstance/child:Child"
%1 = firrtl.unresolved_path "OMInstanceTarget:~TargetInstance|TargetInstance/child:Child"
%2 = firrtl.unresolved_path "OMMemberInstanceTarget:~TargetInstance|TargetInstance/child:Child"
firrtl.instance child @Child()
}
// CHECK: firrtl.module @Child() attributes {annotations = [
// CHECK-SAME: {class = "circt.tracker", id = distinct[0]<>},
// CHECK-SAME: {class = "circt.tracker", id = distinct[1]<>},
// CHECK-SAME: {class = "circt.tracker", id = distinct[2]<>}
// CHECK-SAME: {{{.+}} class = "circt.tracker", id = distinct[0]<>},
// CHECK-SAME: {{{.+}} class = "circt.tracker", id = distinct[1]<>},
// CHECK-SAME: {{{.+}} class = "circt.tracker", id = distinct[2]<>}
// CHECK-SAME: ]}
firrtl.module @Child() { }
}
Expand All @@ -137,11 +137,11 @@ firrtl.circuit "TargetInstancePort" {
// CHECK: firrtl.module @TargetInstancePort() {
firrtl.module @TargetInstancePort() {
// CHECK: %0 = firrtl.path reference distinct[0]<>
// CHECK: firrtl.instance child @Child(in in: !firrtl.uint<8>)
// CHECK: firrtl.instance child {{.+}} @Child(in in: !firrtl.uint<8>)
%0 = firrtl.unresolved_path "OMReferenceTarget:~TargetInstancePort|TargetInstancePort/child:Child>in"
firrtl.instance child @Child(in in : !firrtl.uint<8>)
}
// CHECK: firrtl.module @Child(in %in: !firrtl.uint<8> [{class = "circt.tracker", id = distinct[0]<>}])
// CHECK: firrtl.module @Child(in %in: !firrtl.uint<8> [{{{.+}} class = "circt.tracker", id = distinct[0]<>}])
firrtl.module @Child(in %in : !firrtl.uint<8>) { }
}

Expand Down Expand Up @@ -228,62 +228,55 @@ firrtl.class @OM() {

// -----

// CHECK-LABEL: firrtl.circuit "DisambiguateLocalPath"
firrtl.circuit "DisambiguateLocalPath" {
// CHECK: hw.hierpath private [[NLA0:@.+]] [@DisambiguateLocalPath::[[SYM0:@.+]], @Child]
// CHECK: hw.hierpath private [[NLA1:@.+]] [@DisambiguateLocalPath::[[SYM1:@.+]], @Child]
firrtl.module @DisambiguateLocalPath() {
// CHECK-LABEL: firrtl.circuit "AmbiguousLocalPath"
firrtl.circuit "AmbiguousLocalPath" {
firrtl.module @AmbiguousLocalPath() {
// CHECK: [[PATH0:%.+]] = firrtl.path reference distinct[[[DISTINCT0:.+]]]<>
// CHECK: [[PATH1:%.+]] = firrtl.path reference distinct[[[DISTINCT1:.+]]]<>
%0 = firrtl.unresolved_path "OMReferenceTarget:~DisambiguateLocalPath|Child"
%0 = firrtl.unresolved_path "OMReferenceTarget:~AmbiguousLocalPath|Child"

// CHECK: firrtl.list.create [[PATH0]], [[PATH1]]
// CHECK: firrtl.list.create [[PATH0]]
%1 = firrtl.list.create %0 : !firrtl.list<path>

// CHECK: firrtl.instance child0 sym [[SYM1]]
// CHECK: firrtl.instance child0
firrtl.instance child0 @Child()
// CHECK: firrtl.instance child1 sym [[SYM0]]
// CHECK: firrtl.instance child1
firrtl.instance child1 @Child()
}
// CHECK: firrtl.module @Child
// CHECK-SAME: {circt.nonlocal = [[NLA0]], class = "circt.tracker", id = distinct[[[DISTINCT0]]]<>}
// CHECK-SAME: {circt.nonlocal = [[NLA1]], class = "circt.tracker", id = distinct[[[DISTINCT1]]]<>}
// CHECK-SAME: {class = "circt.tracker", id = distinct[[[DISTINCT0]]]<>}
firrtl.module @Child() {}
}

// -----

// CHECK-LABEL: firrtl.circuit "DisambiguateNonLocalPath"
firrtl.circuit "DisambiguateNonLocalPath" {
// CHECK: hw.hierpath private [[NLA0:@.+]] [@DisambiguateNonLocalPath::[[SYM0:@.+]], @Child1::[[SYM1_0:@.+]], @Child2::[[SYM2:@.+]], @Child3::[[SYM3:@.+]], @Child4]
// CHECK: hw.hierpath private [[NLA1:@.+]] [@DisambiguateNonLocalPath::[[SYM0]], @Child1::[[SYM1_1:@.+]], @Child2::[[SYM2]], @Child3::[[SYM3]], @Child4]
firrtl.module @DisambiguateNonLocalPath() {
// CHECK-LABEL: firrtl.circuit "AmbiguousNonLocalPath"
firrtl.circuit "AmbiguousNonLocalPath" {
// CHECK: hw.hierpath private [[NLA:@.+]] [@Child2::[[SYM0:@.+]], @Child3::[[SYM1:@.+]], @Child4]
firrtl.module @AmbiguousNonLocalPath() {
// CHECK: [[PATH0:%.+]] = firrtl.path reference distinct[[[DISTINCT0:.+]]]<>
// CHECK: [[PATH1:%.+]] = firrtl.path reference distinct[[[DISTINCT1:.+]]]<>
%0 = firrtl.unresolved_path "OMReferenceTarget:~DisambiguateNonLocalPath|Child2/child3:Child3/child4:Child4"
%0 = firrtl.unresolved_path "OMReferenceTarget:~AmbiguousNonLocalPath|Child2/child3:Child3/child4:Child4"

// CHECK: firrtl.list.create [[PATH0]], [[PATH1]]
// CHECK: firrtl.list.create [[PATH0]]
%1 = firrtl.list.create %0 : !firrtl.list<path>

// CHECK: firrtl.instance child1 sym [[SYM0]]
// CHECK: firrtl.instance child1
firrtl.instance child1 @Child1()
}
firrtl.module @Child1() {
// CHECK: firrtl.instance child2_0 sym [[SYM1_1]]
// CHECK: firrtl.instance child2_0
firrtl.instance child2_0 @Child2()
// CHECK: firrtl.instance child2_1 sym [[SYM1_0]]
// CHECK: firrtl.instance child2_1
firrtl.instance child2_1 @Child2()
}
firrtl.module @Child2() {
// CHECK: firrtl.instance child3 sym [[SYM2]]
// CHECK: firrtl.instance child3 sym [[SYM0]]
firrtl.instance child3 @Child3()
}
firrtl.module @Child3() {
// CHECK: firrtl.instance child4 sym [[SYM3]]
// CHECK: firrtl.instance child4 sym [[SYM1]]
firrtl.instance child4 @Child4()
}
// CHECK: firrtl.module @Child4
// CHECK-SAME: {circt.nonlocal = [[NLA0]], class = "circt.tracker", id = distinct[[[DISTINCT0]]]<>}
// CHECK-SAME: {circt.nonlocal = [[NLA1]], class = "circt.tracker", id = distinct[[[DISTINCT1]]]<>}
// CHECK-SAME: {circt.nonlocal = [[NLA]], class = "circt.tracker", id = distinct[[[DISTINCT0]]]<>}
firrtl.module @Child4() {}
}