[IMDCE] Forward constant output ports to caller sides (#3688)

This PR makes IMDCE propagate constant output ports to caller sides before
actually performing dataflow analysis so that we can eliminate constant output ports.
This commit is contained in:
Hideto Ueno 2022-08-10 14:10:09 +09:00 committed by GitHub
parent e15b651c88
commit 82cc9db2f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 65 additions and 0 deletions

View File

@ -40,6 +40,7 @@ struct IMDeadCodeElimPass : public IMDeadCodeElimBase<IMDeadCodeElimPass> {
void rewriteModuleSignature(FModuleOp module);
void rewriteModuleBody(FModuleOp module);
void eraseEmptyModule(FModuleOp module);
void forwardConstantOutputPort(FModuleOp module);
void markAlive(Value value) {
// If the value is already in `liveSet`, skip it.
@ -179,11 +180,57 @@ void IMDeadCodeElimPass::markBlockExecutable(Block *block) {
}
}
void IMDeadCodeElimPass::forwardConstantOutputPort(FModuleOp module) {
// This tracks constant values of output ports.
SmallVector<std::pair<unsigned, APSInt>> constantPortIndicesAndValues;
auto ports = module.getPorts();
auto *instanceGraphNode = instanceGraph->lookup(module);
for (const auto &e : llvm::enumerate(ports)) {
unsigned index = e.index();
auto port = e.value();
auto arg = module.getArgument(index);
// If the port has don't touch, don't propagate the constant value.
if (!port.isOutput() || hasDontTouch(arg))
continue;
// Remember the index and constant value connected to an output port.
if (auto connect = getSingleConnectUserOf(arg))
if (auto constant = connect.getSrc().getDefiningOp<ConstantOp>())
constantPortIndicesAndValues.push_back({index, constant.getValue()});
}
// If there is no constant port, abort.
if (constantPortIndicesAndValues.empty())
return;
// Rewrite all uses.
for (auto *use : instanceGraphNode->uses()) {
auto instance = cast<InstanceOp>(*use->getInstance());
ImplicitLocOpBuilder builder(instance.getLoc(), instance);
for (auto [index, constant] : constantPortIndicesAndValues) {
auto result = instance.getResult(index);
assert(ports[index].isOutput() && "must be an output port");
// Replace the port with the constant.
result.replaceAllUsesWith(builder.create<ConstantOp>(constant));
}
}
}
void IMDeadCodeElimPass::runOnOperation() {
LLVM_DEBUG(llvm::dbgs() << "===----- Remove unused ports -----==="
<< "\n");
auto circuit = getOperation();
instanceGraph = &getAnalysis<InstanceGraph>();
// Forward constant output ports to caller sides so that we can eliminate
// constant outputs.
for (auto *node : llvm::post_order(instanceGraph))
if (auto module = dyn_cast_or_null<FModuleOp>(*node->getModule()))
forwardConstantOutputPort(module);
for (auto module : circuit.getBodyBlock()->getOps<FModuleOp>()) {
// Mark the ports of public modules as alive.
if (module.isPublic()) {

View File

@ -162,3 +162,21 @@ firrtl.circuit "DeleteEmptyModule" {
firrtl.instance sub2 @Sub(in a: !firrtl.uint<1>)
}
}
// -----
// CHECK-LABEL: "ForwardConstant"
firrtl.circuit "ForwardConstant" {
// CHECK-NOT: Zero
firrtl.module private @Zero(out %zero: !firrtl.uint<1>) {
%c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
firrtl.strictconnect %zero, %c0_ui1 : !firrtl.uint<1>
}
// CHECK-LABEL: @ForwardConstant
firrtl.module @ForwardConstant(out %zero: !firrtl.uint<1>) {
// CHECK: %c0_ui1 = firrtl.constant 0
%sub_zero = firrtl.instance sub @Zero(out zero: !firrtl.uint<1>)
// CHECK-NEXT: firrtl.strictconnect %zero, %c0_ui1
firrtl.strictconnect %zero, %sub_zero : !firrtl.uint<1>
}
}