19 #include "mlir/IR/ImplicitLocOpBuilder.h"
20 #include "mlir/IR/Iterators.h"
21 #include "mlir/IR/Threading.h"
22 #include "llvm/Support/Debug.h"
23 #include "llvm/Support/SaveAndRestore.h"
25 #define DEBUG_TYPE "firrtl-probe-dce"
27 using namespace circt;
28 using namespace firrtl;
35 struct ProbeDCEPass :
public ProbeDCEBase<ProbeDCEPass> {
36 using ProbeDCEBase::ProbeDCEBase;
37 void runOnOperation()
override;
43 process(FModuleLike mod,
44 std::optional<std::reference_wrapper<InstanceGraph>> ig);
48 void ProbeDCEPass::runOnOperation() {
49 LLVM_DEBUG(llvm::dbgs()
50 <<
"===- Running ProbeDCE Pass "
51 "--------------------------------------------------===\n");
53 SmallVector<Operation *, 0> ops(getOperation().getOps<FModuleLike>());
55 auto ig = getCachedAnalysis<InstanceGraph>();
57 std::atomic<bool> anyChanges(
false);
58 auto result = failableParallelForEach(&getContext(), ops, [&](Operation *op) {
59 auto failOrChanged = process(cast<FModuleLike>(op), ig);
60 if (failed(failOrChanged))
62 auto changed = *failOrChanged;
72 markAnalysesPreserved<InstanceGraph>();
74 markAllAnalysesPreserved();
79 return std::make_unique<ProbeDCEPass>();
95 void chase(SmallVectorImpl<Operation *> &worklist, Operation *op) {
96 if (!slice.insert(op).second)
98 worklist.push_back(op);
102 void chaseUsers(SmallVectorImpl<Operation *> &worklist, Value v) {
103 for (
auto *user : v.getUsers())
104 chase(worklist, user);
108 LogicalResult
chaseVal(SmallVectorImpl<Operation *> &worklist, Value v) {
109 if (
auto depArg = dyn_cast<BlockArgument>(v)) {
112 if (depArg != currentSliceSource)
113 return emitError(depArg.getLoc(),
"argument depends on input probe")
114 .attachNote(currentSliceSource.getLoc())
119 auto *op = v.getDefiningOp();
122 chaseUsers(worklist, v);
127 bool contains(Operation *op)
const {
return slice.contains(op); }
128 bool empty()
const {
return slice.empty(); }
129 size_t size()
const {
return slice.size(); }
131 const DenseSet<Operation *> &
get()
const {
return slice; }
135 LogicalResult
add(BlockArgument arg) {
137 llvm::SaveAndRestore<BlockArgument> x(this->currentSliceSource, arg);
139 SmallVector<Operation *> worklist;
142 chaseUsers(worklist, arg);
144 while (!worklist.empty()) {
145 auto *op = worklist.pop_back_val();
150 TypeSwitch<Operation *, LogicalResult>(op)
151 .Case([&](InstanceOp inst) {
156 .Case([&](FConnectLike
connect) {
157 return chaseVal(worklist,
connect.getDest());
159 .Case([&](RefSubOp op) {
160 chaseUsers(worklist, op.getResult());
163 .Case([&](RefCastOp op) {
164 chaseUsers(worklist, op.getResult());
167 .Case([&](WireOp op) {
172 .Default([&](
auto *op) -> LogicalResult {
173 return emitError(op->getLoc(),
"input probes cannot be used")
174 .attachNote(arg.getLoc())
175 <<
"input probe here";
185 ProbeDCEPass::process(FModuleLike mod,
186 std::optional<std::reference_wrapper<InstanceGraph>> ig) {
187 SmallVector<size_t> probePortIndices;
190 for (
auto idx : llvm::seq(mod.getNumPorts())) {
192 type_isa<RefType>(mod.getPortType(idx)))
193 probePortIndices.push_back(idx);
197 if (!probePortIndices.empty() && mod.isPublic()) {
198 auto idx = probePortIndices.front();
199 return mlir::emitError(mod.getPortLocation(idx),
200 "input probe not allowed on public module");
202 auto modOp = dyn_cast<FModuleOp>(mod.getOperation());
204 if (!probePortIndices.empty()) {
205 auto idx = probePortIndices.front();
206 return mlir::emitError(mod.getPortLocation(idx),
207 "input probe not allowed on this module kind");
213 BitVector portsToErase(mod.getNumPorts());
218 for (
auto idx : probePortIndices) {
219 portsToErase.set(idx);
220 if (failed(slice.
add(modOp.getArgument(idx))))
225 bool changes = portsToErase.any();
230 modOp.walk<mlir::WalkOrder::PostOrder, mlir::ReverseIterator>(
232 auto inst = dyn_cast<InstanceOp>(op);
246 ImplicitLocOpBuilder
builder(inst.getLoc(), inst);
247 builder.setInsertionPointAfter(op);
248 BitVector instPortsToErase(inst.getNumResults());
249 for (
auto [idx, result] : llvm::enumerate(inst.getResults())) {
252 if (!type_isa<RefType>(result.getType()))
254 instPortsToErase.set(idx);
258 if (result.use_empty())
261 auto wire =
builder.create<WireOp>(result.getType());
262 result.replaceAllUsesWith(wire.getDataRaw());
264 if (instPortsToErase.none())
267 auto newInst = inst.erasePorts(
builder, instPortsToErase);
271 ig->get().replaceInstance(inst, newInst);
275 numErasedPorts += portsToErase.count();
276 mod.erasePorts(portsToErase);
assert(baseType &&"element must be base type")
def connect(destination, source)
std::unique_ptr< mlir::Pass > createProbeDCEPass()
This is the pass constructor.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.