Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIRRTL] Refactor class lowering to avoid unnecessary cloning #7823

Merged
merged 2 commits into from
Nov 15, 2024
Merged
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
86 changes: 62 additions & 24 deletions lib/Dialect/FIRRTL/Transforms/LowerClasses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1021,6 +1021,7 @@ static om::ClassLike convertClass(FModuleLike moduleLike, OpBuilder builder,
fieldTypes.push_back(
NamedAttribute(name, TypeAttr::get(op.getSrc().getType())));
}

checkAddContainingModulePorts(hasContainingModule, builder, fieldNames,
fieldTypes);
return builder.create<om::ClassOp>(moduleLike.getLoc(), name,
Expand Down Expand Up @@ -1143,20 +1144,20 @@ void LowerClassesPass::lowerClass(om::ClassOp classOp, FModuleLike moduleLike,
for (size_t i = 0; i < nAltBasePaths; ++i)
classBody->addArgument(basePathType, unknownLoc);

// Mapping from block arguments or instance results to new values.
DenseMap<Value, Value> valueMapping;
for (auto inputProperty : inputProperties) {
BlockArgument parameterValue =
classBody->addArgument(inputProperty.type, inputProperty.loc);
BlockArgument inputValue =
moduleLike->getRegion(0).getArgument(inputProperty.index);
mapping.map(inputValue, parameterValue);

valueMapping[inputValue] = parameterValue;
}

// Clone the property ops from the FIRRTL Class or Module to the OM Class.
SmallVector<Operation *> opsToErase;
OpBuilder builder = OpBuilder::atBlockBegin(classOp.getBodyBlock());
llvm::SmallVector<mlir::Location> fieldLocs;
llvm::SmallVector<mlir::Value> fieldValues;
for (auto &op : moduleLike->getRegion(0).getOps()) {
// Helper to check if an op is a property op, which should be removed in the
// module like, or kept in the OM class.
auto isPropertyOp = [&](Operation &op) {
// Check if any operand is a property.
auto propertyOperands = llvm::any_of(op.getOperandTypes(), [](Type type) {
return isa<PropertyType>(type);
Expand All @@ -1169,28 +1170,58 @@ void LowerClassesPass::lowerClass(om::ClassOp classOp, FModuleLike moduleLike,
auto propertyResults = llvm::any_of(
op.getResultTypes(), [](Type type) { return isa<PropertyType>(type); });

// If there are no properties here, move along.
if (!needsClone && !propertyOperands && !propertyResults)
return needsClone || propertyOperands || propertyResults;
};

// Move operations from the module like to the OM class.
// We need to clone only instances and register their results to the value
// mapping. Operands must be remapped using the value mapping.
for (auto &op : llvm::make_early_inc_range(
moduleLike->getRegion(0).front().getOperations())) {
if (!isPropertyOp(op))
continue;

bool isField = false;
Operation *newOp = &op;

bool needsMove = true;

// Clone instances and register their results to the value mapping.
if (isa<InstanceOp>(op)) {
newOp = op.clone();
for (auto [oldResult, newResult] :
llvm::zip(op.getResults(), newOp->getResults()))
valueMapping[oldResult] = newResult;
classBody->getOperations().insert(classBody->end(), newOp);
needsMove = false;
}

// Remap operands using the value mapping.
for (size_t i = 0; i < newOp->getNumOperands(); ++i) {
auto operand = newOp->getOperand(i);
auto it = valueMapping.find(operand);
if (it != valueMapping.end())
newOp->setOperand(i, it->second);
}

// Move the op into the OM class body.
if (needsMove)
newOp->moveBefore(classBody, classBody->end());
}
rwy7 marked this conversation as resolved.
Show resolved Hide resolved

llvm::SmallVector<mlir::Location> fieldLocs;
llvm::SmallVector<mlir::Value> fieldValues;
for (Operation &op :
llvm::make_early_inc_range(classOp.getBodyBlock()->getOperations())) {
assert(isPropertyOp(op));

if (auto propAssign = dyn_cast<PropAssignOp>(op)) {
if (isa<BlockArgument>(propAssign.getDest())) {
if (auto blockArg = dyn_cast<BlockArgument>(propAssign.getDest())) {
// Store any output property assignments into fields op inputs.
fieldLocs.push_back(op.getLoc());
fieldValues.push_back(mapping.lookup(propAssign.getSrc()));
isField = true;
fieldValues.push_back(propAssign.getSrc());
propAssign.erase();
}
}

if (!isField)
// Clone the op over to the OM Class.
builder.clone(op, mapping);

// In case this is a Module, remember to erase this op, unless it is an
// instance. Instances are handled later in updateInstances.
if (!isa<InstanceOp>(op))
opsToErase.push_back(&op);
}

// If there is a 'containingModule', add an argument for 'ports', and a field.
Expand All @@ -1201,14 +1232,21 @@ void LowerClassesPass::lowerClass(om::ClassOp classOp, FModuleLike moduleLike,
fieldValues.push_back(argumentValue);
}

OpBuilder builder = OpBuilder::atBlockEnd(classOp.getBodyBlock());
classOp.addNewFieldsOp(builder, fieldLocs, fieldValues);

// If the module-like is a Class, it will be completely erased later.
// Otherwise, erase just the property ports and ops.
if (!isa<firrtl::ClassLike>(moduleLike.getOperation())) {
// Erase ops in use before def order, thanks to FIRRTL's SSA regions.
for (auto *op : llvm::reverse(opsToErase))
op->erase();
for (auto &op :
llvm::make_early_inc_range(llvm::reverse(moduleLike.getOperation()
->getRegion(0)
.getBlocks()
.front()
.getOperations())))
if (isPropertyOp(op) && !isa<InstanceOp>(op))
op.erase();
Comment on lines +1242 to +1249
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just making sure, is there anything to erase in the original module, since we're moving all the property operations?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I think this is dead code now.


// Erase property typed ports.
moduleLike.erasePorts(portsToErase);
Expand Down
Loading