14#include "mlir/IR/ImplicitLocOpBuilder.h"
15#include "mlir/Pass/Pass.h"
16#include "llvm/ADT/BitVector.h"
17#include "llvm/ADT/PostOrderIterator.h"
18#include "llvm/Support/Debug.h"
20#define DEBUG_TYPE "firrtl-remove-unused-ports"
24#define GEN_PASS_DEF_REMOVEUNUSEDPORTS
25#include "circt/Dialect/FIRRTL/Passes.h.inc"
30using namespace firrtl;
33struct RemoveUnusedPortsPass
34 :
public circt::firrtl::impl::RemoveUnusedPortsBase<RemoveUnusedPortsPass> {
37 void runOnOperation()
override;
38 void removeUnusedModulePorts(FModuleOp module,
45 bool ignoreDontTouch =
false;
49void RemoveUnusedPortsPass::runOnOperation() {
52 auto &instanceGraph = getAnalysis<InstanceGraph>();
55 for (
auto *node :
llvm::post_order(&instanceGraph))
56 if (auto module = dyn_cast<FModuleOp>(*node->getModule()))
58 if (!module.isPublic())
59 removeUnusedModulePorts(module, node);
62void RemoveUnusedPortsPass::removeUnusedModulePorts(
64 LLVM_DEBUG(llvm::dbgs() <<
"Prune ports of module: " << module.getName()
68 SmallVector<std::optional<APSInt>> outputPortConstants;
69 auto ports =
module.getPorts();
71 llvm::BitVector removalPortIndexes(ports.size());
73 for (
const auto &e :
llvm::enumerate(ports)) {
74 unsigned index = e.index();
75 auto port = e.value();
76 auto arg =
module.getArgument(index);
80 if ((
hasDontTouch(arg) || !port.annotations.empty()) && !ignoreDontTouch)
89 if (port.isInput() && !arg.use_empty())
93 auto port = a->getInstance()->getResult(arg.getArgNumber());
94 return port.getUses().empty();
98 if (port.isOutput()) {
99 if (arg.use_empty()) {
102 outputPortConstants.push_back(std::nullopt);
103 }
else if (llvm::all_of(instanceGraphNode->
uses(), portIsUnused)) {
105 auto builder = ImplicitLocOpBuilder::atBlockBegin(
106 arg.getLoc(), module.getBodyBlock());
107 auto wire = WireOp::create(builder, arg.getType());
108 arg.replaceAllUsesWith(wire.getResult());
109 outputPortConstants.push_back(std::nullopt);
110 }
else if (arg.hasOneUse()) {
113 Operation *op = arg.use_begin().getUser();
114 auto connectLike = dyn_cast<FConnectLike>(op);
117 auto *srcOp = connectLike.getSrc().getDefiningOp();
118 if (!isa_and_nonnull<InvalidValueOp, ConstantOp>(srcOp))
121 if (
auto constant = dyn_cast<ConstantOp>(srcOp))
122 outputPortConstants.push_back(constant.getValue());
124 assert(isa<InvalidValueOp>(srcOp) &&
"only expect invalid");
125 outputPortConstants.push_back(std::nullopt);
131 if (srcOp->use_empty())
139 removalPortIndexes.set(index);
143 if (removalPortIndexes.none())
147 module.erasePorts(removalPortIndexes);
148 LLVM_DEBUG(llvm::for_each(removalPortIndexes.set_bits(), [&](
unsigned index) {
149 llvm::dbgs() <<
"Delete port: " << ports[index].name <<
"\n";
153 for (
auto *use : instanceGraphNode->uses()) {
154 auto instance = ::cast<InstanceOp>(*use->getInstance());
155 ImplicitLocOpBuilder builder(instance.getLoc(), instance);
156 unsigned outputPortIndex = 0;
157 for (
auto index : removalPortIndexes.set_bits()) {
158 auto result = instance.getResult(index);
159 assert(!ports[index].isInOut() &&
"don't expect inout ports");
163 if (ports[index].isInput()) {
164 WireOp wire = WireOp::create(builder, result.getType());
168 bool onlyWritten = llvm::all_of(result.getUsers(), [&](Operation *op) {
169 if (auto connect = dyn_cast<FConnectLike>(op))
170 return connect.getDest() == result;
174 result.replaceUsesWithIf(wire.getResult(), [&](OpOperand &op) ->
bool {
176 if (onlyWritten && isa<FConnectLike>(op.getOwner())) {
177 op.getOwner()->erase();
184 if (wire.use_empty())
192 auto portConstant = outputPortConstants[outputPortIndex++];
195 value = ConstantOp::create(builder, *portConstant);
197 value = InvalidValueOp::create(builder, result.getType());
199 result.replaceAllUsesWith(value);
203 instance.erasePorts(builder, removalPortIndexes);
208 numRemovedPorts += removalPortIndexes.count();
assert(baseType &&"element must be base type")
#define CIRCT_DEBUG_SCOPED_PASS_LOGGER(PASS)
This is a Node in the InstanceGraph.
llvm::iterator_range< UseIterator > uses()
This is an edge in the InstanceGraph.
bool hasDontTouch(Value value)
Check whether a block argument ("port") or the operation defining a value has a DontTouch annotation,...
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.