16 #include "mlir/IR/ImplicitLocOpBuilder.h"
17 #include "llvm/ADT/APSInt.h"
18 #include "llvm/ADT/BitVector.h"
19 #include "llvm/ADT/PostOrderIterator.h"
20 #include "llvm/Support/Debug.h"
22 #define DEBUG_TYPE "firrtl-remove-unused-ports"
24 using namespace circt;
25 using namespace firrtl;
28 struct RemoveUnusedPortsPass
29 :
public RemoveUnusedPortsBase<RemoveUnusedPortsPass> {
30 void runOnOperation()
override;
31 void removeUnusedModulePorts(FModuleOp module,
38 bool ignoreDontTouch =
false;
42 void RemoveUnusedPortsPass::runOnOperation() {
43 auto &instanceGraph = getAnalysis<InstanceGraph>();
47 for (
auto *node : llvm::post_order(&instanceGraph))
48 if (
auto module = dyn_cast<FModuleOp>(*node->getModule()))
50 if (!module.isPublic())
51 removeUnusedModulePorts(module, node);
54 void RemoveUnusedPortsPass::removeUnusedModulePorts(
56 LLVM_DEBUG(llvm::dbgs() <<
"Prune ports of module: " << module.getName()
60 SmallVector<std::optional<APSInt>> outputPortConstants;
61 auto ports = module.getPorts();
63 llvm::BitVector removalPortIndexes(ports.size());
65 for (
const auto &e : llvm::enumerate(ports)) {
66 unsigned index = e.index();
67 auto port = e.value();
68 auto arg = module.getArgument(index);
72 if ((
hasDontTouch(arg) || !port.annotations.canBeDeleted()) &&
82 if (port.isInput() && !arg.use_empty())
86 auto port = a->getInstance()->getResult(arg.getArgNumber());
87 return port.getUses().empty();
91 if (port.isOutput()) {
92 if (arg.use_empty()) {
95 outputPortConstants.push_back(std::nullopt);
96 }
else if (llvm::all_of(instanceGraphNode->
uses(), portIsUnused)) {
98 auto builder = ImplicitLocOpBuilder::atBlockBegin(
99 arg.getLoc(), module.getBodyBlock());
100 auto wire =
builder.create<WireOp>(arg.getType());
101 arg.replaceAllUsesWith(wire.getResult());
102 outputPortConstants.push_back(std::nullopt);
103 }
else if (arg.hasOneUse()) {
106 Operation *op = arg.use_begin().getUser();
107 auto connectLike = dyn_cast<FConnectLike>(op);
110 auto *srcOp = connectLike.getSrc().getDefiningOp();
111 if (!isa_and_nonnull<InvalidValueOp, ConstantOp>(srcOp))
114 if (
auto constant = dyn_cast<ConstantOp>(srcOp))
115 outputPortConstants.push_back(constant.getValue());
117 assert(isa<InvalidValueOp>(srcOp) &&
"only expect invalid");
118 outputPortConstants.push_back(std::nullopt);
124 if (srcOp->use_empty())
132 removalPortIndexes.set(index);
136 if (removalPortIndexes.none())
140 module.erasePorts(removalPortIndexes);
141 LLVM_DEBUG(llvm::for_each(removalPortIndexes.set_bits(), [&](
unsigned index) {
142 llvm::dbgs() <<
"Delete port: " << ports[index].name <<
"\n";
146 for (
auto *use : instanceGraphNode->
uses()) {
147 auto instance = ::cast<InstanceOp>(*use->getInstance());
148 ImplicitLocOpBuilder
builder(instance.getLoc(), instance);
149 unsigned outputPortIndex = 0;
150 for (
auto index : removalPortIndexes.set_bits()) {
151 auto result = instance.getResult(index);
152 assert(!ports[index].isInOut() &&
"don't expect inout ports");
156 if (ports[index].isInput()) {
157 WireOp wire =
builder.create<WireOp>(result.getType());
161 bool onlyWritten = llvm::all_of(result.getUsers(), [&](Operation *op) {
162 if (auto connect = dyn_cast<FConnectLike>(op))
163 return connect.getDest() == result;
167 result.replaceUsesWithIf(wire.getResult(), [&](OpOperand &op) ->
bool {
169 if (onlyWritten && isa<FConnectLike>(op.getOwner())) {
170 op.getOwner()->erase();
177 if (wire.use_empty())
185 auto portConstant = outputPortConstants[outputPortIndex++];
188 value =
builder.create<ConstantOp>(*portConstant);
190 value =
builder.create<InvalidValueOp>(result.getType());
192 result.replaceAllUsesWith(value);
196 instance.erasePorts(
builder, removalPortIndexes);
201 numRemovedPorts += removalPortIndexes.count();
204 std::unique_ptr<mlir::Pass>
206 auto pass = std::make_unique<RemoveUnusedPortsPass>();
207 pass->ignoreDontTouch = ignoreDontTouch;
assert(baseType &&"element must be base type")
This is a Node in the InstanceGraph.
llvm::iterator_range< UseIterator > uses()
This is an edge in the InstanceGraph.
std::unique_ptr< mlir::Pass > createRemoveUnusedPortsPass(bool ignoreDontTouch=false)
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.
llvm::raw_ostream & debugPassHeader(const mlir::Pass *pass, int width=80)
Write a boilerplate header for a pass to the debug stream.