diff --git a/include/circt/Dialect/FIRRTL/LayerSet.h b/include/circt/Dialect/FIRRTL/LayerSet.h index 0a4a98c8512f..f1f162afecf4 100644 --- a/include/circt/Dialect/FIRRTL/LayerSet.h +++ b/include/circt/Dialect/FIRRTL/LayerSet.h @@ -42,6 +42,43 @@ struct LayerSetCompare { using LayerSet = SmallSet; +//===----------------------------------------------------------------------===// +// Layer Verification Utilities +//===----------------------------------------------------------------------===// + +/// Get the ambient layers active at the given op. +LayerSet getAmbientLayersAt(Operation *op); + +/// Get the ambient layer requirements at the definition site of the value. +LayerSet getAmbientLayersFor(Value value); + +/// Get the effective layer requirements for the given value. +/// The effective layers for a value is the union of +/// - the ambient layers for the cannonical storage location. +/// - any explicit layer annotations in the value's type. +LayerSet getLayersFor(Value value); + +/// Check that the source layer is compatible with the destination layer. +/// Either the source and destination are identical, or the source-layer +/// is a parent of the destination. For example `A` is compatible with `A.B.C`, +/// because any definition valid in `A` is also valid in `A.B.C`. +bool isLayerCompatibleWith(mlir::SymbolRefAttr srcLayer, + mlir::SymbolRefAttr dstLayer); + +/// Check that the source layer is present in the destination layers. +bool isLayerCompatibleWith(SymbolRefAttr srcLayer, const LayerSet &dstLayers); + +/// Check that the source layers are all present in the destination layers. +/// True if all source layers are present in the destination. +/// Outputs the set of source layers that are missing in the destination. +bool isLayerSetCompatibleWith(const LayerSet &src, const LayerSet &dst, + SmallVectorImpl &missing); + +LogicalResult checkLayerCompatibility( + Operation *op, const LayerSet &src, const LayerSet &dst, + const Twine &errorMsg, + const Twine ¬eMsg = Twine("missing layer requirements")); + } // namespace firrtl } // namespace circt diff --git a/lib/Dialect/FIRRTL/CMakeLists.txt b/lib/Dialect/FIRRTL/CMakeLists.txt index 22a963f2bc75..d8827ca3e31a 100644 --- a/lib/Dialect/FIRRTL/CMakeLists.txt +++ b/lib/Dialect/FIRRTL/CMakeLists.txt @@ -19,6 +19,7 @@ set(CIRCT_FIRRTL_Sources FIRRTLOps.cpp FIRRTLTypes.cpp FIRRTLUtils.cpp + LayerSet.cpp NLATable.cpp ) diff --git a/lib/Dialect/FIRRTL/FIRRTLOps.cpp b/lib/Dialect/FIRRTL/FIRRTLOps.cpp index a086d3cb1c98..8f620693aa8d 100644 --- a/lib/Dialect/FIRRTL/FIRRTLOps.cpp +++ b/lib/Dialect/FIRRTL/FIRRTLOps.cpp @@ -313,112 +313,6 @@ void getAsmBlockArgumentNamesImpl(Operation *op, mlir::Region ®ion, static ParseResult parseNameKind(OpAsmParser &parser, firrtl::NameKindEnumAttr &result); -//===----------------------------------------------------------------------===// -// Layer Verification Utilities -//===----------------------------------------------------------------------===// - -/// Get the ambient layers active at the given op. -static LayerSet getAmbientLayersAt(Operation *op) { - // Crawl through the parent ops, accumulating all ambient layers at the given - // operation. - LayerSet result; - for (; op != nullptr; op = op->getParentOp()) { - if (auto module = dyn_cast(op)) { - auto layers = module.getLayersAttr().getAsRange(); - result.insert(layers.begin(), layers.end()); - break; - } - if (auto layerblock = dyn_cast(op)) { - result.insert(layerblock.getLayerName()); - continue; - } - } - return result; -} - -/// Get the ambient layer requirements at the definition site of the value. -static LayerSet getAmbientLayersFor(Value value) { - return getAmbientLayersAt(getFieldRefFromValue(value).getDefiningOp()); -} - -/// Get the effective layer requirements for the given value. -/// The effective layers for a value is the union of -/// - the ambient layers for the cannonical storage location. -/// - any explicit layer annotations in the value's type. -static LayerSet getLayersFor(Value value) { - auto result = getAmbientLayersFor(value); - if (auto type = dyn_cast(value.getType())) - if (auto layer = type.getLayer()) - result.insert(type.getLayer()); - return result; -} - -/// Check that the source layer is compatible with the destination layer. -/// Either the source and destination are identical, or the source-layer -/// is a parent of the destination. For example `A` is compatible with `A.B.C`, -/// because any definition valid in `A` is also valid in `A.B.C`. -static bool isLayerCompatibleWith(mlir::SymbolRefAttr srcLayer, - mlir::SymbolRefAttr dstLayer) { - // A non-colored probe may be cast to any colored probe. - if (!srcLayer) - return true; - - // A colored probe cannot be cast to an uncolored probe. - if (!dstLayer) - return false; - - // Return true if the srcLayer is a prefix of the dstLayer. - if (srcLayer.getRootReference() != dstLayer.getRootReference()) - return false; - - auto srcNames = srcLayer.getNestedReferences(); - auto dstNames = dstLayer.getNestedReferences(); - if (dstNames.size() < srcNames.size()) - return false; - - return llvm::all_of(llvm::zip_first(srcNames, dstNames), - [](auto x) { return std::get<0>(x) == std::get<1>(x); }); -} - -/// Check that the source layer is present in the destination layers. -static bool isLayerCompatibleWith(SymbolRefAttr srcLayer, - const LayerSet &dstLayers) { - // fast path: the required layer is directly listed in the provided layers. - if (dstLayers.contains(srcLayer)) - return true; - - // Slow path: the required layer is not directly listed in the provided - // layers, but the layer may still be provided by a nested layer. - return any_of(dstLayers, [=](SymbolRefAttr dstLayer) { - return isLayerCompatibleWith(srcLayer, dstLayer); - }); -} - -/// Check that the source layers are all present in the destination layers. -/// True if all source layers are present in the destination. -/// Outputs the set of source layers that are missing in the destination. -static bool isLayerSetCompatibleWith(const LayerSet &src, const LayerSet &dst, - SmallVectorImpl &missing) { - for (auto srcLayer : src) - if (!isLayerCompatibleWith(srcLayer, dst)) - missing.push_back(srcLayer); - - llvm::sort(missing, LayerSetCompare()); - return missing.empty(); -} - -static LogicalResult checkLayerCompatibility( - Operation *op, const LayerSet &src, const LayerSet &dst, - const Twine &errorMsg, - const Twine ¬eMsg = Twine("missing layer requirements")) { - SmallVector missing; - if (isLayerSetCompatibleWith(src, dst, missing)) - return success(); - interleaveComma(missing, op->emitOpError(errorMsg).attachNote() - << noteMsg << ": "); - return failure(); -} - //===----------------------------------------------------------------------===// // CircuitOp //===----------------------------------------------------------------------===// diff --git a/lib/Dialect/FIRRTL/LayerSet.cpp b/lib/Dialect/FIRRTL/LayerSet.cpp new file mode 100644 index 000000000000..54332ab8bc54 --- /dev/null +++ b/lib/Dialect/FIRRTL/LayerSet.cpp @@ -0,0 +1,106 @@ +//===----------------------------------------------------------------------===// +// +// 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/FIRRTL/LayerSet.h" +#include "circt/Dialect/FIRRTL/FIRRTLOps.h" +#include "circt/Dialect/FIRRTL/FIRRTLUtils.h" +#include "llvm/ADT/SmallSet.h" + +using namespace mlir; +using namespace circt; +using namespace firrtl; + +LayerSet circt::firrtl::getAmbientLayersAt(Operation *op) { + // Crawl through the parent ops, accumulating all ambient layers at the given + // operation. + LayerSet result; + for (; op != nullptr; op = op->getParentOp()) { + if (auto module = dyn_cast(op)) { + auto layers = module.getLayersAttr().getAsRange(); + result.insert(layers.begin(), layers.end()); + break; + } + if (auto layerblock = dyn_cast(op)) { + result.insert(layerblock.getLayerName()); + continue; + } + } + return result; +} + +LayerSet circt::firrtl::getAmbientLayersFor(Value value) { + return getAmbientLayersAt(getFieldRefFromValue(value).getDefiningOp()); +} + +LayerSet circt::firrtl::getLayersFor(Value value) { + auto result = getAmbientLayersFor(value); + if (auto type = dyn_cast(value.getType())) + if (auto layer = type.getLayer()) + result.insert(type.getLayer()); + return result; +} + +bool circt::firrtl::isLayerCompatibleWith(mlir::SymbolRefAttr srcLayer, + mlir::SymbolRefAttr dstLayer) { + // A non-colored probe may be cast to any colored probe. + if (!srcLayer) + return true; + + // A colored probe cannot be cast to an uncolored probe. + if (!dstLayer) + return false; + + // Return true if the srcLayer is a prefix of the dstLayer. + if (srcLayer.getRootReference() != dstLayer.getRootReference()) + return false; + + auto srcNames = srcLayer.getNestedReferences(); + auto dstNames = dstLayer.getNestedReferences(); + if (dstNames.size() < srcNames.size()) + return false; + + return llvm::all_of(llvm::zip_first(srcNames, dstNames), + [](auto x) { return std::get<0>(x) == std::get<1>(x); }); +} + +bool circt::firrtl::isLayerCompatibleWith(SymbolRefAttr srcLayer, + const LayerSet &dstLayers) { + // fast path: the required layer is directly listed in the provided layers. + if (dstLayers.contains(srcLayer)) + return true; + + // Slow path: the required layer is not directly listed in the provided + // layers, but the layer may still be provided by a nested layer. + return any_of(dstLayers, [=](SymbolRefAttr dstLayer) { + return isLayerCompatibleWith(srcLayer, dstLayer); + }); +} + +bool circt::firrtl::isLayerSetCompatibleWith( + const LayerSet &src, const LayerSet &dst, + SmallVectorImpl &missing) { + for (auto srcLayer : src) + if (!isLayerCompatibleWith(srcLayer, dst)) + missing.push_back(srcLayer); + + llvm::sort(missing, LayerSetCompare()); + return missing.empty(); +} + +LogicalResult circt::firrtl::checkLayerCompatibility(Operation *op, + const LayerSet &src, + const LayerSet &dst, + const Twine &errorMsg, + const Twine ¬eMsg) { + SmallVector missing; + if (isLayerSetCompatibleWith(src, dst, missing)) + return success(); + interleaveComma(missing, op->emitOpError(errorMsg).attachNote() + << noteMsg << ": "); + return failure(); +} diff --git a/lib/Dialect/FIRRTL/Transforms/LowerLayers.cpp b/lib/Dialect/FIRRTL/Transforms/LowerLayers.cpp index 632bae66281e..4c8726ddafeb 100644 --- a/lib/Dialect/FIRRTL/Transforms/LowerLayers.cpp +++ b/lib/Dialect/FIRRTL/Transforms/LowerLayers.cpp @@ -13,6 +13,7 @@ #include "circt/Dialect/FIRRTL/FIRRTLInstanceGraph.h" #include "circt/Dialect/FIRRTL/FIRRTLOps.h" #include "circt/Dialect/FIRRTL/FIRRTLUtils.h" +#include "circt/Dialect/FIRRTL/LayerSet.h" #include "circt/Dialect/FIRRTL/Namespace.h" #include "circt/Dialect/FIRRTL/Passes.h" #include "circt/Dialect/HW/HierPathCache.h" @@ -22,6 +23,7 @@ #include "mlir/Pass/Pass.h" #include "llvm/ADT/PostOrderIterator.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Mutex.h" @@ -231,7 +233,8 @@ class LowerLayersPass FailureOr runOnModuleLike(FModuleLike moduleLike); /// Extract layerblocks and strip probe colors from all ops under the module. - LogicalResult runOnModuleBody(FModuleOp moduleOp, InnerRefMap &innerRefMap); + LogicalResult runOnModuleBody(FModuleOp moduleOp, InnerRefMap &innerRefMap, + const LayerSet &enabledLayers); /// Update the module's port types to remove any explicit layer requirements /// from any probe types. @@ -243,6 +246,9 @@ class LowerLayersPass /// Lower an inline layerblock to an ifdef block. void lowerInlineLayerBlock(LayerOp layer, LayerBlockOp layerBlock); + /// Inline an enabled layerblock directly into its parent. + void inlineLayerBlock(LayerBlockOp layerBlock); + /// Build macro declarations and cache information about the layers. void preprocessLayers(CircuitNamespace &ns, OpBuilder &b, LayerOp layer, StringRef circuitName, @@ -364,9 +370,12 @@ LowerLayersPass::runOnModuleLike(FModuleLike moduleLike) { auto result = TypeSwitch(moduleLike.getOperation()) .Case([&](auto op) { + LayerSet enabledLayers; + enabledLayers.insert_range( + op.getLayersAttr().template getAsRange()); op.setLayers({}); removeLayersFromPorts(op); - return runOnModuleBody(op, innerRefMap); + return runOnModuleBody(op, innerRefMap, enabledLayers); }) .Case([&](auto op) { op.setKnownLayers({}); @@ -400,8 +409,15 @@ void LowerLayersPass::lowerInlineLayerBlock(LayerOp layer, layerBlock.erase(); } +void LowerLayersPass::inlineLayerBlock(LayerBlockOp layerBlock) { + layerBlock->getBlock()->getOperations().splice( + Block::iterator(layerBlock), layerBlock.getBody()->getOperations()); + layerBlock.erase(); +} + LogicalResult LowerLayersPass::runOnModuleBody(FModuleOp moduleOp, - InnerRefMap &innerRefMap) { + InnerRefMap &innerRefMap, + const LayerSet &enabledLayers) { hw::InnerSymbolNamespace ns(moduleOp); // Get or create a node op for a value captured by a layer block. @@ -694,6 +710,11 @@ LogicalResult LowerLayersPass::runOnModuleBody(FModuleOp moduleOp, // After this point, we are dealing with a layer block. auto layer = symbolToLayer.lookup(layerBlock.getLayerName()); + if (isLayerCompatibleWith(layerBlock.getLayerName(), enabledLayers)) { + inlineLayerBlock(layerBlock); + return WalkResult::advance(); + } + if (layer.getConvention() == LayerConvention::Inline) { lowerInlineLayerBlock(layer, layerBlock); return WalkResult::advance(); diff --git a/test/Dialect/FIRRTL/lower-layers.mlir b/test/Dialect/FIRRTL/lower-layers.mlir index b843d5435fb7..be318e5d3e8b 100644 --- a/test/Dialect/FIRRTL/lower-layers.mlir +++ b/test/Dialect/FIRRTL/lower-layers.mlir @@ -1142,5 +1142,72 @@ firrtl.circuit "DoNotIncludeDummyBindfiles" { // CHECK-NOT: sv.include local "layers-Child1-A.sv" // CHECK: sv.include local "layers-Child2-A.sv" - // CHECK-NOT: sv.include local "layers-Child1-A.sv" + // CHECK-NOT: sv.include local "layers-Child1-A.sv" +} + +// ----- + +firrtl.circuit "EnabledLayers" { + firrtl.layer @Bind bind { + firrtl.layer @Child bind { + firrtl.layer @Child bind {} + } + } + + firrtl.layer @Inline inline { + firrtl.layer @Child inline { + firrtl.layer @Child inline {} + } + } + + firrtl.module @EnabledLayers() {} + + // CHECK-LABEL: firrtl.module @ModuleWithEnabledBind + firrtl.module @ModuleWithEnabledBind() attributes {layers = [@Bind::@Child]} { + // CHECK: %c2_ui8 = firrtl.constant 2 + // CHECK: %w1 = firrtl.wire + // CHECK: firrtl.connect %w1, %c2_ui8 + // CHECK: %w2 = firrtl.wire sym @sym + // CHECK: firrtl.connect %w2, %w1 + // CHECK: firrtl.instance bind_child_child {{.*}} @ModuleWithEnabledBind_Bind_Child_Child() + %c2_ui8 = firrtl.constant 2 : !firrtl.uint<8> + firrtl.layerblock @Bind { + %w1 = firrtl.wire : !firrtl.uint<8> + firrtl.connect %w1, %c2_ui8 : !firrtl.uint<8> + firrtl.layerblock @Bind::@Child { + %w2 = firrtl.wire : !firrtl.uint<8> + firrtl.connect %w2, %w1 : !firrtl.uint<8> + firrtl.layerblock @Bind::@Child::@Child { + %w3 = firrtl.wire : !firrtl.uint<8> + firrtl.connect %w3, %w2 : !firrtl.uint<8> + } + } + } + } + + // CHECK-LABEL: firrtl.module @ModuleWithEnabledInline + firrtl.module @ModuleWithEnabledInline() attributes {layers = [@Inline::@Child]} { + // CHECK: %c2_ui8 = firrtl.constant 2 + // CHECK: %w1 = firrtl.wire + // CHECK: firrtl.connect %w1, %c2_ui8 + // CHECK: %w2 = firrtl.wire + // CHECK: firrtl.connect %w2, %w1 + // CHECK: sv.ifdef @layer$Inline$Child$Child { + // CHECK: %w3 = firrtl.wire + // CHECK: firrtl.connect %w3, %w2 + // CHECK: } + %c2_ui8 = firrtl.constant 2 : !firrtl.uint<8> + firrtl.layerblock @Inline { + %w1 = firrtl.wire : !firrtl.uint<8> + firrtl.connect %w1, %c2_ui8 : !firrtl.uint<8> + firrtl.layerblock @Inline::@Child { + %w2 = firrtl.wire : !firrtl.uint<8> + firrtl.connect %w2, %w1 : !firrtl.uint<8> + firrtl.layerblock @Inline::@Child::@Child { + %w3 = firrtl.wire : !firrtl.uint<8> + firrtl.connect %w3, %w2 : !firrtl.uint<8> + } + } + } + } }