[FIRRTL] Add start of FIRRTL types lowering (#157)

* [FIRRTL] Add start of FIRRTL types lowering

This adds a new pass to lower FIRRTL types, initially focused on
bundle types. This flattens bundle types in module and instance
ports, and deals with the flattened values in subfield and connect
operations. Starts to resolve #123.
This commit is contained in:
mikeurbach 2020-11-16 10:47:42 -07:00 committed by GitHub
parent 66da348c3b
commit a0f04b14ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 503 additions and 3 deletions

View File

@ -24,6 +24,7 @@ namespace firrtl {
std::unique_ptr<mlir::Pass> createLowerFIRRTLToRTLModulePass();
std::unique_ptr<mlir::Pass> createLowerFIRRTLToRTLPass();
std::unique_ptr<mlir::Pass> createLowerFIRRTLTypesPass();
} // namespace firrtl
} // namespace circt

View File

@ -9,6 +9,12 @@
include "mlir/Pass/PassBase.td"
/// Lower firrtl.module port types to ground types.
def LowerFIRRTLTypes : Pass<"lower-firrtl-types", "firrtl::FModuleOp"> {
let summary = "Lower FIRRTL types to ground types";
let constructor = "circt::firrtl::createLowerFIRRTLTypesPass()";
}
/// Lower firrtl.module's to rtl.module.
def LowerFIRRTLToRTLModule
: Pass<"lower-firrtl-to-rtl-module", "ModuleOp"> {

View File

@ -2,6 +2,7 @@
//
//===----------------------------------------------------------------------===//
#include "PassDetails.h"
#include "circt/Dialect/FIRRTL/Ops.h"
#include "circt/Dialect/FIRRTL/Passes.h"
#include "circt/Dialect/FIRRTL/Visitors.h"
@ -19,9 +20,6 @@ static T getTypeOf(Value v) {
return v.getType().cast<T>();
}
#define GEN_PASS_CLASSES
#include "circt/Dialect/FIRRTL/FIRRTLPasses.h.inc"
/// Given a FIRRTL type, return the corresponding type for the RTL dialect.
/// This returns a null type if it cannot be lowered.
static Type lowerType(Type type) {

View File

@ -0,0 +1,370 @@
//===- LowerTypes.cpp - Lower FIRRTL types to ground types ------*- C++ -*-===//
//
// 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 "PassDetails.h"
#include "circt/Dialect/FIRRTL/Ops.h"
#include "circt/Dialect/FIRRTL/Passes.h"
#include "circt/Dialect/FIRRTL/Types.h"
#include "circt/Dialect/FIRRTL/Visitors.h"
#include "circt/Support/ImplicitLocOpBuilder.h"
using namespace circt;
using namespace firrtl;
// This represents a flattened bundle field element.
struct FlatBundleFieldEntry {
// This is the underlying ground type of the field.
FIRRTLType type;
// This is a suffix to add to the field name to make it unique.
std::string suffix;
// This indicates whether the field was flipped to be an output.
bool isOutput;
// Helper to determine if a fully flattened type needs to be flipped.
FIRRTLType getPortType() { return isOutput ? FlipType::get(type) : type; }
};
// Convert a nested bundle of fields into a flat list of fields. This is used
// when working with instances and mems to flatten them.
static void flattenBundleTypes(FIRRTLType type, StringRef suffixSoFar,
bool isFlipped,
SmallVectorImpl<FlatBundleFieldEntry> &results) {
if (auto flip = type.dyn_cast<FlipType>())
return flattenBundleTypes(flip.getElementType(), suffixSoFar, !isFlipped,
results);
// In the base case we record this field.
auto bundle = type.dyn_cast<BundleType>();
if (!bundle) {
results.push_back({type, suffixSoFar.str(), isFlipped});
return;
}
SmallString<16> tmpSuffix(suffixSoFar);
// Otherwise, we have a bundle type. Break it down.
for (auto &elt : bundle.getElements()) {
// Construct the suffix to pass down.
tmpSuffix.resize(suffixSoFar.size());
tmpSuffix.push_back('_');
tmpSuffix.append(elt.first.strref());
// Recursively process subelements.
flattenBundleTypes(elt.second, tmpSuffix, isFlipped, results);
}
}
//===----------------------------------------------------------------------===//
// Pass Infrastructure
//===----------------------------------------------------------------------===//
namespace {
struct FIRRTLTypesLowering : public LowerFIRRTLTypesBase<FIRRTLTypesLowering>,
public FIRRTLVisitor<FIRRTLTypesLowering> {
using ValueIdentifier = std::pair<Value, Identifier>;
using FIRRTLVisitor<FIRRTLTypesLowering>::visitDecl;
using FIRRTLVisitor<FIRRTLTypesLowering>::visitExpr;
using FIRRTLVisitor<FIRRTLTypesLowering>::visitStmt;
void runOnOperation() override;
// Lowering operations.
void visitDecl(InstanceOp op);
void visitExpr(SubfieldOp op);
void visitStmt(ConnectOp op);
private:
// Lowering module block arguments.
void lowerArg(BlockArgument arg, FIRRTLType type);
// Helpers to manage state.
Value addArg(Type type, unsigned oldArgNumber, StringRef nameSuffix = "");
void setBundleLowering(Value oldValue, StringRef flatField, Value newValue);
Value getBundleLowering(Value oldValue, StringRef flatField);
void getAllBundleLowerings(Value oldValue, SmallVectorImpl<Value> &results);
// The builder is set and maintained in the main loop.
ImplicitLocOpBuilder *builder;
// State to keep track of arguments and operations to clean up at the end.
SmallVector<unsigned, 8> argsToRemove;
SmallVector<Operation *, 16> opsToRemove;
// State to keep a mapping from (Value, Identifier) pairs to flattened values.
DenseMap<ValueIdentifier, Value> loweredBundleValues;
};
} // end anonymous namespace
/// This is the pass constructor.
std::unique_ptr<mlir::Pass> circt::firrtl::createLowerFIRRTLTypesPass() {
return std::make_unique<FIRRTLTypesLowering>();
}
// This is the main entrypoint for the lowering pass.
void FIRRTLTypesLowering::runOnOperation() {
auto module = getOperation();
auto *body = module.getBodyBlock();
ImplicitLocOpBuilder theBuilder(module.getLoc(), &getContext());
builder = &theBuilder;
// Lower the module block arguments.
SmallVector<BlockArgument, 8> args(body->args_begin(), body->args_end());
for (auto arg : args)
if (auto type = arg.getType().dyn_cast<FIRRTLType>())
lowerArg(arg, type);
// Lower the operations.
for (auto &op : body->getOperations()) {
builder->setInsertionPoint(&op);
builder->setLoc(op.getLoc());
dispatchVisitor(&op);
}
// Remove ops that have been lowered.
while (!opsToRemove.empty())
opsToRemove.pop_back_val()->erase();
// Remove args that have been lowered.
getOperation().eraseArguments(argsToRemove);
argsToRemove.clear();
// Reset lowered state.
loweredBundleValues.clear();
}
//===----------------------------------------------------------------------===//
// Lowering module block arguments.
//===----------------------------------------------------------------------===//
// Lower arguments with bundle type by flattening them.
void FIRRTLTypesLowering::lowerArg(BlockArgument arg, FIRRTLType type) {
unsigned argNumber = arg.getArgNumber();
// Flatten any bundle types.
SmallVector<FlatBundleFieldEntry, 8> fieldTypes;
flattenBundleTypes(type, "", false, fieldTypes);
for (auto field : fieldTypes) {
// Create new block arguments.
auto type = field.getPortType();
auto newValue = addArg(type, argNumber, field.suffix);
// If this field was flattened from a bundle.
if (!field.suffix.empty()) {
// Remove field separator prefix for consitency with the rest of the pass.
auto fieldName = StringRef(field.suffix).drop_front(1);
// Map the flattened suffix for the original bundle to the new value.
setBundleLowering(arg, fieldName, newValue);
} else {
// Lower any other arguments by copying them to keep the relative order.
arg.replaceAllUsesWith(newValue);
}
}
// Remember to remove the original block argument.
argsToRemove.push_back(argNumber);
}
//===----------------------------------------------------------------------===//
// Lowering operations.
//===----------------------------------------------------------------------===//
// Lower instance operations in the same way as module block arguments. Bundles
// are flattened, and other arguments are copied to keep the relative order. By
// ensuring both lowerings are the same, we can process every module in the
// circuit in parallel, and every instance will have the correct ports.
void FIRRTLTypesLowering::visitDecl(InstanceOp op) {
// The instance's ports are represented as one value with a bundle type.
BundleType originalType = op.result().getType().cast<BundleType>();
// Create a new, flat bundle type for the new result
SmallVector<BundleType::BundleElement, 8> bundleElements;
for (auto element : originalType.getElements()) {
// Flatten any nested bundle types the usual way.
SmallVector<FlatBundleFieldEntry, 8> fieldTypes;
flattenBundleTypes(element.second, element.first, false, fieldTypes);
for (auto field : fieldTypes) {
// Store the flat type for the new bundle type.
auto flatName = builder->getIdentifier(field.suffix);
auto flatType = field.getPortType();
auto newElement = BundleType::BundleElement(flatName, flatType);
bundleElements.push_back(newElement);
}
}
// Get the new bundle type and create a new instance.
auto newType = BundleType::get(bundleElements, &getContext());
auto newInstance =
builder->create<InstanceOp>(newType, op.moduleNameAttr(), op.nameAttr());
// Create new subfield ops for each field of the instance.
for (auto element : bundleElements) {
auto newSubfield =
builder->create<SubfieldOp>(element.second, newInstance, element.first);
// Map the flattened suffix for the original bundle to the new value.
setBundleLowering(op, element.first, newSubfield);
}
// Remember to remove the original op.
opsToRemove.push_back(op);
}
// Lowering subfield operations has to deal with three different cases:
// a) the input value is from a module block argument
// b) the input value is from another subfield operation's result
// c) the input value is from an instance
//
// This is accomplished by storing value and suffix mappings that point to the
// flattened value. If the subfield op is accessing the leaf field of a bundle,
// it replaces all uses with the flattened value. Otherwise, it flattens the
// rest of the bundle and adds the flattened values to the mapping for each
// partial suffix.
void FIRRTLTypesLowering::visitExpr(SubfieldOp op) {
Value input = op.input();
Value result = op.result();
StringRef fieldname = op.fieldname();
FIRRTLType resultType = result.getType().cast<FIRRTLType>();
// Flatten any nested bundle types the usual way.
SmallVector<FlatBundleFieldEntry, 8> fieldTypes;
flattenBundleTypes(resultType, fieldname, false, fieldTypes);
for (auto field : fieldTypes) {
// Look up the mapping for this suffix.
auto newValue = getBundleLowering(input, field.suffix);
// The prefix is the field name and possibly field separator.
auto prefixSize = fieldname.size();
if (field.suffix.size() > fieldname.size())
prefixSize += 1;
// Get the remaining field suffix by removing the prefix.
auto partialSuffix = StringRef(field.suffix).drop_front(prefixSize);
// If we are at the leaf of a bundle.
if (partialSuffix.empty())
// Replace the result with the flattened value.
result.replaceAllUsesWith(newValue);
else
// Map the partial suffix for the result value to the flattened value.
setBundleLowering(result, partialSuffix, newValue);
}
// Remember to remove the original op.
opsToRemove.push_back(op);
}
// Lowering connects only has to deal with one special case: connecting two
// bundles. This situation should only arise when both of the arguments are a
// bundle that was:
// a) originally a block argument
// b) originally an instance's port
//
// When two such bundles are connected, none of the subfield visits have a
// chance to lower them, so we must ensure they have the same number of
// flattened values and flatten out this connect into multiple connects.
void FIRRTLTypesLowering::visitStmt(ConnectOp op) {
Value dest = op.dest();
Value src = op.src();
// If we aren't connecting two bundles, there is nothing to do.
if (!dest.getType().isa<BundleType>() || !src.getType().isa<BundleType>())
return;
// Get the lowered values for each side.
SmallVector<Value, 8> destValues;
getAllBundleLowerings(dest, destValues);
SmallVector<Value, 8> srcValues;
getAllBundleLowerings(src, srcValues);
// Check that we got out the same number of values from each bundle.
assert(destValues.size() == srcValues.size() &&
"connected bundles don't match");
for (auto tuple : llvm::zip_first(destValues, srcValues)) {
Value newDest = std::get<0>(tuple);
Value newSrc = std::get<1>(tuple);
builder->create<ConnectOp>(newDest, newSrc);
}
// Remember to remove the original op.
opsToRemove.push_back(op);
}
//===----------------------------------------------------------------------===//
// Helpers to manage state.
//===----------------------------------------------------------------------===//
static DictionaryAttr getArgAttrs(StringAttr nameAttr, StringRef suffix,
OpBuilder *builder) {
SmallString<16> newName(nameAttr.getValue());
newName += suffix;
StringAttr newNameAttr = builder->getStringAttr(newName);
auto attr = builder->getNamedAttr("firrtl.name", newNameAttr);
return builder->getDictionaryAttr(attr);
}
// Creates and returns a new block argument of the specified type to the module.
// This also maintains the name attribute for the new argument, possibly with a
// new suffix appended.
Value FIRRTLTypesLowering::addArg(Type type, unsigned oldArgNumber,
StringRef nameSuffix) {
FModuleOp module = getOperation();
Block *body = module.getBodyBlock();
// Append the new argument.
auto newValue = body->addArgument(type);
// Keep the module's type up-to-date.
module.setType(builder->getFunctionType(body->getArgumentTypes(), {}));
// Copy over the name attribute for the new argument.
StringAttr nameAttr = getFIRRTLNameAttr(module.getArgAttrs(oldArgNumber));
if (nameAttr) {
auto newArgAttrs = getArgAttrs(nameAttr, nameSuffix, builder);
module.setArgAttrs(newValue.getArgNumber(), newArgAttrs);
}
return newValue;
}
// Store the mapping from a bundle typed value to a mapping from its field names
// to flat values.
void FIRRTLTypesLowering::setBundleLowering(Value oldValue, StringRef flatField,
Value newValue) {
auto flatFieldId = builder->getIdentifier(flatField);
auto &entry = loweredBundleValues[ValueIdentifier(oldValue, flatFieldId)];
assert(!entry && "bundle lowering has already been set");
entry = newValue;
}
// For a mapped bundle typed value and a flat subfield name, retrieve and return
// the flat value if it exists.
Value FIRRTLTypesLowering::getBundleLowering(Value oldValue,
StringRef flatField) {
auto flatFieldId = builder->getIdentifier(flatField);
auto &entry = loweredBundleValues[ValueIdentifier(oldValue, flatFieldId)];
assert(entry && "bundle lowering was not set");
return entry;
}
// For a mapped bundle typed value, retrieve and return the flat values for each
// field.
void FIRRTLTypesLowering::getAllBundleLowerings(
Value value, SmallVectorImpl<Value> &results) {
BundleType bundleType = value.getType().cast<BundleType>();
for (auto element : bundleType.getElements())
results.push_back(getBundleLowering(value, element.first));
}

View File

@ -0,0 +1,24 @@
//===- PassDetails.h - FIRRTL pass class details ----------------*- C++ -*-===//
//
//===----------------------------------------------------------------------===//
// clang-tidy seems to expect the absolute path in the header guard on some
// systems, so just disable it.
// NOLINTNEXTLINE(llvm-header-guard)
#ifndef DIALECT_FIRRTL_PASSDETAILS_H
#define DIALECT_FIRRTL_PASSDETAILS_H
#include "circt/Dialect/FIRRTL/Ops.h"
#include "circt/Dialect/RTL/Ops.h"
#include "mlir/Pass/Pass.h"
namespace circt {
namespace firrtl {
#define GEN_PASS_CLASSES
#include "circt/Dialect/FIRRTL/FIRRTLPasses.h.inc"
} // namespace firrtl
} // namespace circt
#endif // DIALECT_FIRRTL_PASSDETAILS_H

View File

@ -0,0 +1,99 @@
// RUN: circt-opt -pass-pipeline='firrtl.circuit(lower-firrtl-types)' %s | FileCheck %s
firrtl.circuit "TopLevel" {
// CHECK-LABEL: firrtl.module @Simple
// CHECK-SAME: %[[SOURCE_VALID_NAME:source_valid]]: [[SOURCE_VALID_TYPE:!firrtl.uint<1>]]
// CHECK-SAME: %[[SOURCE_READY_NAME:source_ready]]: [[SOURCE_READY_TYPE:!firrtl.flip<uint<1>>]]
// CHECK-SAME: %[[SOURCE_DATA_NAME:source_data]]: [[SOURCE_DATA_TYPE:!firrtl.uint<64>]]
// CHECK-SAME: %[[SINK_VALID_NAME:sink_valid]]: [[SINK_VALID_TYPE:!firrtl.flip<uint<1>>]]
// CHECK-SAME: %[[SINK_READY_NAME:sink_ready]]: [[SINK_READY_TYPE:!firrtl.uint<1>]]
// CHECK-SAME: %[[SINK_DATA_NAME:sink_data]]: [[SINK_DATA_TYPE:!firrtl.flip<uint<64>>]]
firrtl.module @Simple(%source: !firrtl.bundle<valid: uint<1>, ready: flip<uint<1>>, data: uint<64>>,
%sink: !firrtl.bundle<valid: flip<uint<1>>, ready: uint<1>, data: flip<uint<64>>>) {
// CHECK-NEXT: firrtl.when %[[SOURCE_VALID_NAME]]
// CHECK-NEXT: firrtl.connect %[[SINK_DATA_NAME]], %[[SOURCE_DATA_NAME]] : [[SINK_DATA_TYPE]], [[SOURCE_DATA_TYPE]]
// CHECK-NEXT: firrtl.connect %[[SINK_VALID_NAME]], %[[SOURCE_VALID_NAME]] : [[SINK_VALID_TYPE]], [[SOURCE_VALID_TYPE]]
// CHECK-NEXT: firrtl.connect %[[SOURCE_READY_NAME]], %[[SINK_READY_NAME]] : [[SOURCE_READY_TYPE]], [[SINK_READY_TYPE]]
%0 = firrtl.subfield %source("valid") : (!firrtl.bundle<valid: uint<1>, ready: flip<uint<1>>, data: uint<64>>) -> !firrtl.uint<1>
%1 = firrtl.subfield %source("ready") : (!firrtl.bundle<valid: uint<1>, ready: flip<uint<1>>, data: uint<64>>) -> !firrtl.flip<uint<1>>
%2 = firrtl.subfield %source("data") : (!firrtl.bundle<valid: uint<1>, ready: flip<uint<1>>, data: uint<64>>) -> !firrtl.uint<64>
%3 = firrtl.subfield %sink("valid") : (!firrtl.bundle<valid: flip<uint<1>>, ready: uint<1>, data: flip<uint<64>>>) -> !firrtl.flip<uint<1>>
%4 = firrtl.subfield %sink("ready") : (!firrtl.bundle<valid: flip<uint<1>>, ready: uint<1>, data: flip<uint<64>>>) -> !firrtl.uint<1>
%5 = firrtl.subfield %sink("data") : (!firrtl.bundle<valid: flip<uint<1>>, ready: uint<1>, data: flip<uint<64>>>) -> !firrtl.flip<uint<64>>
firrtl.when %0 {
firrtl.connect %5, %2 : !firrtl.flip<uint<64>>, !firrtl.uint<64>
firrtl.connect %3, %0 : !firrtl.flip<uint<1>>, !firrtl.uint<1>
firrtl.connect %1, %4 : !firrtl.flip<uint<1>>, !firrtl.uint<1>
}
}
// CHECK-LABEL: firrtl.module @Recursive
// CHECK-SAME: %[[FLAT_ARG_1_NAME:arg_foo_bar_baz]]: [[FLAT_ARG_1_TYPE:!firrtl.uint<1>]]
// CHECK-SAME: %[[FLAT_ARG_2_NAME:arg_foo_qux]]: [[FLAT_ARG_2_TYPE:!firrtl.sint<64>]]
// CHECK-SAME: %[[OUT_1_NAME:out1]]: [[OUT_1_TYPE:!firrtl.flip<uint<1>>]]
// CHECK-SAME: %[[OUT_2_NAME:out2]]: [[OUT_2_TYPE:!firrtl.flip<sint<64>>]]
firrtl.module @Recursive(%arg: !firrtl.bundle<foo: bundle<bar: bundle<baz: uint<1>>, qux: sint<64>>>,
%out1: !firrtl.flip<uint<1>>, %out2: !firrtl.flip<sint<64>>) {
// CHECK-NEXT: firrtl.connect %[[OUT_1_NAME]], %[[FLAT_ARG_1_NAME]] : [[OUT_1_TYPE]], [[FLAT_ARG_1_TYPE]]
// CHECK-NEXT: firrtl.connect %[[OUT_2_NAME]], %[[FLAT_ARG_2_NAME]] : [[OUT_2_TYPE]], [[FLAT_ARG_2_TYPE]]
%0 = firrtl.subfield %arg("foo") : (!firrtl.bundle<foo: bundle<bar: bundle<baz: uint<1>>, qux: sint<64>>>) -> !firrtl.bundle<bar: bundle<baz: uint<1>>, qux: sint<64>>
%1 = firrtl.subfield %0("bar") : (!firrtl.bundle<bar: bundle<baz: uint<1>>, qux: sint<64>>) -> !firrtl.bundle<baz: uint<1>>
%2 = firrtl.subfield %1("baz") : (!firrtl.bundle<baz: uint<1>>) -> !firrtl.uint<1>
%3 = firrtl.subfield %0("qux") : (!firrtl.bundle<bar: bundle<baz: uint<1>>, qux: sint<64>>) -> !firrtl.sint<64>
firrtl.connect %out1, %2 : !firrtl.flip<uint<1>>, !firrtl.uint<1>
firrtl.connect %out2, %3 : !firrtl.flip<sint<64>>, !firrtl.sint<64>
}
// CHECK-LABEL: firrtl.module @Uniquification
// CHECK-SAME: %[[FLATTENED_ARG:a_b]]: [[FLATTENED_TYPE:!firrtl.uint<1>]],
// CHECK-NOT: %[[FLATTENED_ARG]]
// CHECK-SAME: %[[RENAMED_ARG:a_b.+]]: [[RENAMED_TYPE:!firrtl.uint<1>]] {firrtl.name = "[[FLATTENED_ARG]]"}
firrtl.module @Uniquification(%a: !firrtl.bundle<b: uint<1>>, %a_b: !firrtl.uint<1>) {
}
// CHECK-LABEL: firrtl.module @TopLevel
// CHECK-SAME: %[[SOURCE_VALID_NAME:source_valid]]: [[SOURCE_VALID_TYPE:!firrtl.uint<1>]]
// CHECK-SAME: %[[SOURCE_READY_NAME:source_ready]]: [[SOURCE_READY_TYPE:!firrtl.flip<uint<1>>]]
// CHECK-SAME: %[[SOURCE_DATA_NAME:source_data]]: [[SOURCE_DATA_TYPE:!firrtl.uint<64>]]
// CHECK-SAME: %[[SINK_VALID_NAME:sink_valid]]: [[SINK_VALID_TYPE:!firrtl.flip<uint<1>>]]
// CHECK-SAME: %[[SINK_READY_NAME:sink_ready]]: [[SINK_READY_TYPE:!firrtl.uint<1>]]
// CHECK-SAME: %[[SINK_DATA_NAME:sink_data]]: [[SINK_DATA_TYPE:!firrtl.flip<uint<64>>]]
firrtl.module @TopLevel(%source: !firrtl.bundle<valid: uint<1>, ready: flip<uint<1>>, data: uint<64>>,
%sink: !firrtl.bundle<valid: flip<uint<1>>, ready: uint<1>, data: flip<uint<64>>>) {
// CHECK-NEXT: %[[INSTANCE:.+]] = firrtl.instance @Simple
// CHECK-SAME: !firrtl.bundle
// CHECK-SAME: [[SUB_SOURCE_VALID_NAME:source_valid]]: [[SUB_SOURCE_VALID_TYPE:flip<uint<1>>]]
// CHECK-SAME: [[SUB_SOURCE_READY_NAME:source_ready]]: [[SUB_SOURCE_READY_TYPE:uint<1>]]
// CHECK-SAME: [[SUB_SOURCE_DATA_NAME:source_data]]: [[SUB_SOURCE_DATA_TYPE:flip<uint<64>>]]
// CHECK-SAME: [[SUB_SINK_VALID_NAME:sink_valid]]: [[SUB_SINK_VALID_TYPE:uint<1>]]
// CHECK-SAME: [[SUB_SINK_READY_NAME:sink_ready]]: [[SUB_SINK_READY_TYPE:flip<uint<1>>]]
// CHECK-SAME: [[SUB_SINK_DATA_NAME:sink_data]]: [[SUB_SINK_DATA_TYPE:uint<64>]]
%0 = firrtl.instance @Simple {name = ""} : !firrtl.bundle<source: bundle<valid: flip<uint<1>>, ready: uint<1>, data: flip<uint<64>>>, sink: bundle<valid: uint<1>, ready: flip<uint<1>>, data: uint<64>>>
// CHECK-NEXT: %[[FIELD1:.+]] = firrtl.subfield %[[INSTANCE]]("[[SUB_SOURCE_VALID_NAME]]") {{.*}} [[SUB_SOURCE_VALID_TYPE]]
// CHECK-NEXT: %[[FIELD2:.+]] = firrtl.subfield %[[INSTANCE]]("[[SUB_SOURCE_READY_NAME]]") {{.*}} [[SUB_SOURCE_READY_TYPE]]
// CHECK-NEXT: %[[FIELD3:.+]] = firrtl.subfield %[[INSTANCE]]("[[SUB_SOURCE_DATA_NAME]]") {{.*}} [[SUB_SOURCE_DATA_TYPE]]
// CHECK-NEXT: %[[FIELD4:.+]] = firrtl.subfield %[[INSTANCE]]("[[SUB_SINK_VALID_NAME]]") {{.*}} [[SUB_SINK_VALID_TYPE]]
// CHECK-NEXT: %[[FIELD5:.+]] = firrtl.subfield %[[INSTANCE]]("[[SUB_SINK_READY_NAME]]") {{.*}} [[SUB_SINK_READY_TYPE]]
// CHECK-NEXT: %[[FIELD6:.+]] = firrtl.subfield %[[INSTANCE]]("[[SUB_SINK_DATA_NAME]]") {{.*}} [[SUB_SINK_DATA_TYPE]]
// CHECK-NEXT: firrtl.connect %[[FIELD1]], %[[SOURCE_VALID_NAME]] : !firrtl.[[SUB_SOURCE_VALID_TYPE]], [[SOURCE_VALID_TYPE]]
// CHECK-NEXT: firrtl.connect %[[FIELD2]], %[[SOURCE_READY_NAME]] : !firrtl.[[SUB_SOURCE_READY_TYPE]], [[SOURCE_READY_TYPE]]
// CHECK-NEXT: firrtl.connect %[[FIELD3]], %[[SOURCE_DATA_NAME]] : !firrtl.[[SUB_SOURCE_DATA_TYPE]], [[SOURCE_DATA_TYPE]]
// CHECK-NEXT: firrtl.connect %[[SINK_VALID_NAME]], %[[FIELD4]] : [[SINK_VALID_TYPE]], !firrtl.[[SUB_SINK_VALID_TYPE]]
// CHECK-NEXT: firrtl.connect %[[SINK_READY_NAME]], %[[FIELD5]] : [[SINK_READY_TYPE]], !firrtl.[[SUB_SINK_READY_TYPE]]
// CHECK-NEXT: firrtl.connect %[[SINK_DATA_NAME]], %[[FIELD6]] : [[SINK_DATA_TYPE]], !firrtl.[[SUB_SINK_DATA_TYPE]]
%1 = firrtl.subfield %0("source") : (!firrtl.bundle<source: bundle<valid: flip<uint<1>>, ready: uint<1>, data: flip<uint<64>>>, sink: bundle<valid: uint<1>, ready: flip<uint<1>>, data: uint<64>>>) -> !firrtl.bundle<valid: flip<uint<1>>, ready: uint<1>, data: flip<uint<64>>>
firrtl.connect %1, %source : !firrtl.bundle<valid: flip<uint<1>>, ready: uint<1>, data: flip<uint<64>>>, !firrtl.bundle<valid: uint<1>, ready: flip<uint<1>>, data: uint<64>>
%2 = firrtl.subfield %0("sink") : (!firrtl.bundle<source: bundle<valid: flip<uint<1>>, ready: uint<1>, data: flip<uint<64>>>, sink: bundle<valid: uint<1>, ready: flip<uint<1>>, data: uint<64>>>) -> !firrtl.bundle<valid: uint<1>, ready: flip<uint<1>>, data: uint<64>>
firrtl.connect %sink, %2 : !firrtl.bundle<valid: flip<uint<1>>, ready: uint<1>, data: flip<uint<64>>>, !firrtl.bundle<valid: uint<1>, ready: flip<uint<1>>, data: uint<64>>
}
}

View File

@ -112,6 +112,8 @@ processBuffer(std::unique_ptr<llvm::MemoryBuffer> ownedBuffer,
// Run the lower-to-rtl pass if requested.
if (lowerToRTL) {
pm.nest<firrtl::CircuitOp>().nest<firrtl::FModuleOp>().addPass(
firrtl::createLowerFIRRTLTypesPass());
pm.addPass(firrtl::createLowerFIRRTLToRTLModulePass());
pm.nest<rtl::RTLModuleOp>().addPass(firrtl::createLowerFIRRTLToRTLPass());
}