CIRCT 20.0.0git
Loading...
Searching...
No Matches
FIRRTLReductions.cpp
Go to the documentation of this file.
1//===- FIRRTLReductions.cpp - Reduction patterns for the FIRRTL dialect ---===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
17#include "mlir/IR/ImplicitLocOpBuilder.h"
18#include "llvm/ADT/APSInt.h"
19#include "llvm/ADT/SmallSet.h"
20#include "llvm/Support/Debug.h"
21
22#define DEBUG_TYPE "firrtl-reductions"
23
24using namespace mlir;
25using namespace circt;
26
27//===----------------------------------------------------------------------===//
28// Utilities
29//===----------------------------------------------------------------------===//
30
31namespace detail {
32/// A utility doing lazy construction of `SymbolTable`s and `SymbolUserMap`s,
33/// which is handy for reductions that need to look up a lot of symbols.
35 SymbolTable &getSymbolTable(Operation *op) {
36 return tables.getSymbolTable(op);
37 }
38 SymbolTable &getNearestSymbolTable(Operation *op) {
39 return getSymbolTable(SymbolTable::getNearestSymbolTable(op));
40 }
41
42 SymbolUserMap &getSymbolUserMap(Operation *op) {
43 auto it = userMaps.find(op);
44 if (it != userMaps.end())
45 return it->second;
46 return userMaps.insert({op, SymbolUserMap(tables, op)}).first->second;
47 }
48 SymbolUserMap &getNearestSymbolUserMap(Operation *op) {
49 return getSymbolUserMap(SymbolTable::getNearestSymbolTable(op));
50 }
51
52 void clear() {
53 tables = SymbolTableCollection();
54 userMaps.clear();
55 }
56
57private:
58 SymbolTableCollection tables;
60};
61} // namespace detail
62
63/// Utility to easily get the instantiated firrtl::FModuleOp or an empty
64/// optional in case another type of module is instantiated.
65static std::optional<firrtl::FModuleOp>
66findInstantiatedModule(firrtl::InstanceOp instOp,
67 ::detail::SymbolCache &symbols) {
68 auto *tableOp = SymbolTable::getNearestSymbolTable(instOp);
69 auto moduleOp = dyn_cast<firrtl::FModuleOp>(
70 instOp.getReferencedOperation(symbols.getSymbolTable(tableOp)));
71 return moduleOp ? std::optional(moduleOp) : std::nullopt;
72}
73
74/// Utility to track the transitive size of modules.
76 void clear() { moduleSizes.clear(); }
77
78 uint64_t getModuleSize(Operation *module, ::detail::SymbolCache &symbols) {
79 if (auto it = moduleSizes.find(module); it != moduleSizes.end())
80 return it->second;
81 uint64_t size = 1;
82 module->walk([&](Operation *op) {
83 size += 1;
84 if (auto instOp = dyn_cast<firrtl::InstanceOp>(op))
85 if (auto instModule = findInstantiatedModule(instOp, symbols))
86 size += getModuleSize(*instModule, symbols);
87 });
88 moduleSizes.insert({module, size});
89 return size;
90 }
91
92private:
93 llvm::DenseMap<Operation *, uint64_t> moduleSizes;
94};
95
96/// Check that all connections to a value are invalids.
97static bool onlyInvalidated(Value arg) {
98 return llvm::all_of(arg.getUses(), [](OpOperand &use) {
99 auto *op = use.getOwner();
100 if (!isa<firrtl::ConnectOp, firrtl::MatchingConnectOp>(op))
101 return false;
102 if (use.getOperandNumber() != 0)
103 return false;
104 if (!op->getOperand(1).getDefiningOp<firrtl::InvalidValueOp>())
105 return false;
106 return true;
107 });
108}
109
110/// A tracker for track NLAs affected by a reduction. Performs the necessary
111/// cleanup steps in order to maintain IR validity after the reduction has
112/// applied. For example, removing an instance that forms part of an NLA path
113/// requires that NLA to be removed as well.
115 /// Clear the set of marked NLAs. Call this before attempting a reduction.
116 void clear() { nlasToRemove.clear(); }
117
118 /// Remove all marked annotations. Call this after applying a reduction in
119 /// order to validate the IR.
120 void remove(mlir::ModuleOp module) {
121 unsigned numRemoved = 0;
122 (void)numRemoved;
123 for (Operation &rootOp : *module.getBody()) {
124 if (!isa<firrtl::CircuitOp>(&rootOp))
125 continue;
126 SymbolTable symbolTable(&rootOp);
127 for (auto sym : nlasToRemove) {
128 if (auto *op = symbolTable.lookup(sym)) {
129 ++numRemoved;
130 op->erase();
131 }
132 }
133 }
134 LLVM_DEBUG({
135 unsigned numLost = nlasToRemove.size() - numRemoved;
136 if (numRemoved > 0 || numLost > 0) {
137 llvm::dbgs() << "Removed " << numRemoved << " NLAs";
138 if (numLost > 0)
139 llvm::dbgs() << " (" << numLost << " no longer there)";
140 llvm::dbgs() << "\n";
141 }
142 });
143 }
144
145 /// Mark all NLAs referenced in the given annotation as to be removed. This
146 /// can be an entire array or dictionary of annotations, and the function will
147 /// descend into child annotations appropriately.
148 void markNLAsInAnnotation(Attribute anno) {
149 if (auto dict = dyn_cast<DictionaryAttr>(anno)) {
150 if (auto field = dict.getAs<FlatSymbolRefAttr>("circt.nonlocal"))
151 nlasToRemove.insert(field.getAttr());
152 for (auto namedAttr : dict)
153 markNLAsInAnnotation(namedAttr.getValue());
154 } else if (auto array = dyn_cast<ArrayAttr>(anno)) {
155 for (auto attr : array)
156 markNLAsInAnnotation(attr);
157 }
158 }
159
160 /// Mark all NLAs referenced in an operation. Also traverses all nested
161 /// operations. Call this before removing an operation, to mark any associated
162 /// NLAs as to be removed as well.
163 void markNLAsInOperation(Operation *op) {
164 op->walk([&](Operation *op) {
165 if (auto annos = op->getAttrOfType<ArrayAttr>("annotations"))
166 markNLAsInAnnotation(annos);
167 });
168 }
169
170 /// The set of NLAs to remove, identified by their symbol.
171 llvm::DenseSet<StringAttr> nlasToRemove;
172};
173
174//===----------------------------------------------------------------------===//
175// Reduction patterns
176//===----------------------------------------------------------------------===//
177
178/// A sample reduction pattern that maps `firrtl.module` to `firrtl.extmodule`.
179struct FIRRTLModuleExternalizer : public OpReduction<firrtl::FModuleOp> {
180 void beforeReduction(mlir::ModuleOp op) override {
182 symbols.clear();
184 }
185 void afterReduction(mlir::ModuleOp op) override { nlaRemover.remove(op); }
186
187 uint64_t match(firrtl::FModuleOp module) override {
188 return moduleSizes.getModuleSize(module, symbols);
189 }
190
191 LogicalResult rewrite(firrtl::FModuleOp module) override {
193 OpBuilder builder(module);
194 builder.create<firrtl::FExtModuleOp>(
195 module->getLoc(),
196 module->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()),
197 module.getConventionAttr(), module.getPorts(), StringRef(),
198 module.getAnnotationsAttr());
199 module->erase();
200 return success();
201 }
202
203 std::string getName() const override { return "firrtl-module-externalizer"; }
204
208};
209
210/// Invalidate all the leaf fields of a value with a given flippedness by
211/// connecting an invalid value to them. This is useful for ensuring that all
212/// output ports of an instance or memory (including those nested in bundles)
213/// are properly invalidated.
214static void invalidateOutputs(ImplicitLocOpBuilder &builder, Value value,
215 SmallDenseMap<Type, Value, 8> &invalidCache,
216 bool flip = false) {
217 auto type = dyn_cast<firrtl::FIRRTLType>(value.getType());
218 if (!type)
219 return;
220
221 // Descend into bundles by creating subfield ops.
222 if (auto bundleType = dyn_cast<firrtl::BundleType>(type)) {
223 for (auto element : llvm::enumerate(bundleType.getElements())) {
224 auto subfield =
225 builder.createOrFold<firrtl::SubfieldOp>(value, element.index());
226 invalidateOutputs(builder, subfield, invalidCache,
227 flip ^ element.value().isFlip);
228 if (subfield.use_empty())
229 subfield.getDefiningOp()->erase();
230 }
231 return;
232 }
233
234 // Descend into vectors by creating subindex ops.
235 if (auto vectorType = dyn_cast<firrtl::FVectorType>(type)) {
236 for (unsigned i = 0, e = vectorType.getNumElements(); i != e; ++i) {
237 auto subindex = builder.createOrFold<firrtl::SubindexOp>(value, i);
238 invalidateOutputs(builder, subindex, invalidCache, flip);
239 if (subindex.use_empty())
240 subindex.getDefiningOp()->erase();
241 }
242 return;
243 }
244
245 // Only drive outputs.
246 if (flip)
247 return;
248 Value invalid = invalidCache.lookup(type);
249 if (!invalid) {
250 invalid = builder.create<firrtl::InvalidValueOp>(type);
251 invalidCache.insert({type, invalid});
252 }
253 builder.create<firrtl::ConnectOp>(value, invalid);
254}
255
256/// Connect a value to every leave of a destination value.
257static void connectToLeafs(ImplicitLocOpBuilder &builder, Value dest,
258 Value value) {
259 auto type = dyn_cast<firrtl::FIRRTLBaseType>(dest.getType());
260 if (!type)
261 return;
262 if (auto bundleType = dyn_cast<firrtl::BundleType>(type)) {
263 for (auto element : llvm::enumerate(bundleType.getElements()))
264 connectToLeafs(builder,
265 builder.create<firrtl::SubfieldOp>(dest, element.index()),
266 value);
267 return;
268 }
269 if (auto vectorType = dyn_cast<firrtl::FVectorType>(type)) {
270 for (unsigned i = 0, e = vectorType.getNumElements(); i != e; ++i)
271 connectToLeafs(builder, builder.create<firrtl::SubindexOp>(dest, i),
272 value);
273 return;
274 }
275 auto valueType = dyn_cast<firrtl::FIRRTLBaseType>(value.getType());
276 if (!valueType)
277 return;
278 auto destWidth = type.getBitWidthOrSentinel();
279 auto valueWidth = valueType ? valueType.getBitWidthOrSentinel() : -1;
280 if (destWidth >= 0 && valueWidth >= 0 && destWidth < valueWidth)
281 value = builder.create<firrtl::HeadPrimOp>(value, destWidth);
282 if (!isa<firrtl::UIntType>(type)) {
283 if (isa<firrtl::SIntType>(type))
284 value = builder.create<firrtl::AsSIntPrimOp>(value);
285 else
286 return;
287 }
288 builder.create<firrtl::ConnectOp>(dest, value);
289}
290
291/// Reduce all leaf fields of a value through an XOR tree.
292static void reduceXor(ImplicitLocOpBuilder &builder, Value &into, Value value) {
293 auto type = dyn_cast<firrtl::FIRRTLType>(value.getType());
294 if (!type)
295 return;
296 if (auto bundleType = dyn_cast<firrtl::BundleType>(type)) {
297 for (auto element : llvm::enumerate(bundleType.getElements()))
298 reduceXor(
299 builder, into,
300 builder.createOrFold<firrtl::SubfieldOp>(value, element.index()));
301 return;
302 }
303 if (auto vectorType = dyn_cast<firrtl::FVectorType>(type)) {
304 for (unsigned i = 0, e = vectorType.getNumElements(); i != e; ++i)
305 reduceXor(builder, into,
306 builder.createOrFold<firrtl::SubindexOp>(value, i));
307 return;
308 }
309 if (!isa<firrtl::UIntType>(type)) {
310 if (isa<firrtl::SIntType>(type))
311 value = builder.create<firrtl::AsUIntPrimOp>(value);
312 else
313 return;
314 }
315 into = into ? builder.createOrFold<firrtl::XorPrimOp>(into, value) : value;
316}
317
318/// A sample reduction pattern that maps `firrtl.instance` to a set of
319/// invalidated wires. This often shortcuts a long iterative process of connect
320/// invalidation, module externalization, and wire stripping
321struct InstanceStubber : public OpReduction<firrtl::InstanceOp> {
322 void beforeReduction(mlir::ModuleOp op) override {
323 erasedInsts.clear();
324 erasedModules.clear();
325 symbols.clear();
328 }
329 void afterReduction(mlir::ModuleOp op) override {
330 // Look into deleted modules to find additional instances that are no longer
331 // instantiated anywhere.
332 SmallVector<Operation *> worklist;
333 auto deadInsts = erasedInsts;
334 for (auto *op : erasedModules)
335 worklist.push_back(op);
336 while (!worklist.empty()) {
337 auto *op = worklist.pop_back_val();
338 auto *tableOp = SymbolTable::getNearestSymbolTable(op);
339 op->walk([&](firrtl::InstanceOp instOp) {
340 auto moduleOp = cast<firrtl::FModuleLike>(
341 instOp.getReferencedOperation(symbols.getSymbolTable(tableOp)));
342 deadInsts.insert(instOp);
343 if (llvm::all_of(
344 symbols.getSymbolUserMap(tableOp).getUsers(moduleOp),
345 [&](Operation *user) { return deadInsts.contains(user); })) {
346 LLVM_DEBUG(llvm::dbgs() << "- Removing transitively unused module `"
347 << moduleOp.getModuleName() << "`\n");
348 erasedModules.insert(moduleOp);
349 worklist.push_back(moduleOp);
350 }
351 });
352 }
353
354 for (auto *op : erasedInsts)
355 op->erase();
356 for (auto *op : erasedModules)
357 op->erase();
358 nlaRemover.remove(op);
359 }
360
361 uint64_t match(firrtl::InstanceOp instOp) override {
362 if (auto fmoduleOp = findInstantiatedModule(instOp, symbols))
363 return moduleSizes.getModuleSize(*fmoduleOp, symbols);
364 return 0;
365 }
366
367 LogicalResult rewrite(firrtl::InstanceOp instOp) override {
368 LLVM_DEBUG(llvm::dbgs()
369 << "Stubbing instance `" << instOp.getName() << "`\n");
370 ImplicitLocOpBuilder builder(instOp.getLoc(), instOp);
372 for (unsigned i = 0, e = instOp.getNumResults(); i != e; ++i) {
373 auto result = instOp.getResult(i);
374 auto name = builder.getStringAttr(Twine(instOp.getName()) + "_" +
375 instOp.getPortNameStr(i));
376 auto wire =
377 builder
378 .create<firrtl::WireOp>(result.getType(), name,
379 firrtl::NameKindEnum::DroppableName,
380 instOp.getPortAnnotation(i), StringAttr{})
381 .getResult();
382 invalidateOutputs(builder, wire, invalidCache,
383 instOp.getPortDirection(i) == firrtl::Direction::In);
384 result.replaceAllUsesWith(wire);
385 }
386 auto *tableOp = SymbolTable::getNearestSymbolTable(instOp);
387 auto moduleOp = cast<firrtl::FModuleLike>(
388 instOp.getReferencedOperation(symbols.getSymbolTable(tableOp)));
390 erasedInsts.insert(instOp);
391 if (llvm::all_of(
392 symbols.getSymbolUserMap(tableOp).getUsers(moduleOp),
393 [&](Operation *user) { return erasedInsts.contains(user); })) {
394 LLVM_DEBUG(llvm::dbgs() << "- Removing now unused module `"
395 << moduleOp.getModuleName() << "`\n");
396 erasedModules.insert(moduleOp);
397 }
398 return success();
399 }
400
401 std::string getName() const override { return "instance-stubber"; }
402 bool acceptSizeIncrease() const override { return true; }
403
406 llvm::DenseSet<Operation *> erasedInsts;
407 llvm::DenseSet<Operation *> erasedModules;
409};
410
411/// A sample reduction pattern that maps `firrtl.mem` to a set of invalidated
412/// wires.
413struct MemoryStubber : public OpReduction<firrtl::MemOp> {
414 void beforeReduction(mlir::ModuleOp op) override { nlaRemover.clear(); }
415 void afterReduction(mlir::ModuleOp op) override { nlaRemover.remove(op); }
416 LogicalResult rewrite(firrtl::MemOp memOp) override {
417 LLVM_DEBUG(llvm::dbgs() << "Stubbing memory `" << memOp.getName() << "`\n");
418 ImplicitLocOpBuilder builder(memOp.getLoc(), memOp);
420 Value xorInputs;
421 SmallVector<Value> outputs;
422 for (unsigned i = 0, e = memOp.getNumResults(); i != e; ++i) {
423 auto result = memOp.getResult(i);
424 auto name = builder.getStringAttr(Twine(memOp.getName()) + "_" +
425 memOp.getPortNameStr(i));
426 auto wire =
427 builder
428 .create<firrtl::WireOp>(result.getType(), name,
429 firrtl::NameKindEnum::DroppableName,
430 memOp.getPortAnnotation(i), StringAttr{})
431 .getResult();
432 invalidateOutputs(builder, wire, invalidCache, true);
433 result.replaceAllUsesWith(wire);
434
435 // Isolate the input and output data fields of the port.
436 Value input, output;
437 switch (memOp.getPortKind(i)) {
438 case firrtl::MemOp::PortKind::Read:
439 output = builder.createOrFold<firrtl::SubfieldOp>(wire, 3);
440 break;
441 case firrtl::MemOp::PortKind::Write:
442 input = builder.createOrFold<firrtl::SubfieldOp>(wire, 3);
443 break;
444 case firrtl::MemOp::PortKind::ReadWrite:
445 input = builder.createOrFold<firrtl::SubfieldOp>(wire, 5);
446 output = builder.createOrFold<firrtl::SubfieldOp>(wire, 3);
447 break;
448 case firrtl::MemOp::PortKind::Debug:
449 output = wire;
450 break;
451 }
452
453 if (!isa<firrtl::RefType>(result.getType())) {
454 // Reduce all input ports to a single one through an XOR tree.
455 unsigned numFields =
456 cast<firrtl::BundleType>(wire.getType()).getNumElements();
457 for (unsigned i = 0; i != numFields; ++i) {
458 if (i != 2 && i != 3 && i != 5)
459 reduceXor(builder, xorInputs,
460 builder.createOrFold<firrtl::SubfieldOp>(wire, i));
461 }
462 if (input)
463 reduceXor(builder, xorInputs, input);
464 }
465
466 // Track the output port to hook it up to the XORd input later.
467 if (output)
468 outputs.push_back(output);
469 }
470
471 // Hook up the outputs.
472 for (auto output : outputs)
473 connectToLeafs(builder, output, xorInputs);
474
476 memOp->erase();
477 return success();
478 }
479 std::string getName() const override { return "memory-stubber"; }
480 bool acceptSizeIncrease() const override { return true; }
482};
483
484/// Check whether an operation interacts with flows in any way, which can make
485/// replacement and operand forwarding harder in some cases.
486static bool isFlowSensitiveOp(Operation *op) {
487 return isa<firrtl::WireOp, firrtl::RegOp, firrtl::RegResetOp,
488 firrtl::InstanceOp, firrtl::SubfieldOp, firrtl::SubindexOp,
489 firrtl::SubaccessOp>(op);
490}
491
492/// A sample reduction pattern that replaces all uses of an operation with one
493/// of its operands. This can help pruning large parts of the expression tree
494/// rapidly.
495template <unsigned OpNum>
497 uint64_t match(Operation *op) override {
498 if (op->getNumResults() != 1 || OpNum >= op->getNumOperands())
499 return 0;
500 if (isFlowSensitiveOp(op))
501 return 0;
502 auto resultTy =
503 dyn_cast<firrtl::FIRRTLBaseType>(op->getResult(0).getType());
504 auto opTy =
505 dyn_cast<firrtl::FIRRTLBaseType>(op->getOperand(OpNum).getType());
506 return resultTy && opTy &&
507 resultTy.getWidthlessType() == opTy.getWidthlessType() &&
508 (resultTy.getBitWidthOrSentinel() == -1) ==
509 (opTy.getBitWidthOrSentinel() == -1) &&
510 isa<firrtl::UIntType, firrtl::SIntType>(resultTy);
511 }
512 LogicalResult rewrite(Operation *op) override {
513 assert(match(op));
514 ImplicitLocOpBuilder builder(op->getLoc(), op);
515 auto result = op->getResult(0);
516 auto operand = op->getOperand(OpNum);
517 auto resultTy = cast<firrtl::FIRRTLBaseType>(result.getType());
518 auto operandTy = cast<firrtl::FIRRTLBaseType>(operand.getType());
519 auto resultWidth = resultTy.getBitWidthOrSentinel();
520 auto operandWidth = operandTy.getBitWidthOrSentinel();
521 Value newOp;
522 if (resultWidth < operandWidth)
523 newOp =
524 builder.createOrFold<firrtl::BitsPrimOp>(operand, resultWidth - 1, 0);
525 else if (resultWidth > operandWidth)
526 newOp = builder.createOrFold<firrtl::PadPrimOp>(operand, resultWidth);
527 else
528 newOp = operand;
529 LLVM_DEBUG(llvm::dbgs() << "Forwarding " << newOp << " in " << *op << "\n");
530 result.replaceAllUsesWith(newOp);
531 reduce::pruneUnusedOps(op, *this);
532 return success();
533 }
534 std::string getName() const override {
535 return ("firrtl-operand" + Twine(OpNum) + "-forwarder").str();
536 }
537};
538
539/// A sample reduction pattern that replaces FIRRTL operations with a constant
540/// zero of their type.
542 uint64_t match(Operation *op) override {
543 if (op->getNumResults() != 1 || op->getNumOperands() == 0)
544 return 0;
545 if (isFlowSensitiveOp(op))
546 return 0;
547 auto type = dyn_cast<firrtl::FIRRTLBaseType>(op->getResult(0).getType());
548 return isa_and_nonnull<firrtl::UIntType, firrtl::SIntType>(type);
549 }
550 LogicalResult rewrite(Operation *op) override {
551 assert(match(op));
552 OpBuilder builder(op);
553 auto type = cast<firrtl::FIRRTLBaseType>(op->getResult(0).getType());
554 auto width = type.getBitWidthOrSentinel();
555 if (width == -1)
556 width = 64;
557 auto newOp = builder.create<firrtl::ConstantOp>(
558 op->getLoc(), type, APSInt(width, isa<firrtl::UIntType>(type)));
559 op->replaceAllUsesWith(newOp);
560 reduce::pruneUnusedOps(op, *this);
561 return success();
562 }
563 std::string getName() const override { return "firrtl-constantifier"; }
564};
565
566/// A sample reduction pattern that replaces the right-hand-side of
567/// `firrtl.connect` and `firrtl.matchingconnect` operations with a
568/// `firrtl.invalidvalue`. This removes uses from the fanin cone to these
569/// connects and creates opportunities for reduction in DCE/CSE.
571 uint64_t match(Operation *op) override {
572 if (!isa<firrtl::ConnectOp, firrtl::MatchingConnectOp>(op))
573 return 0;
574 auto type = dyn_cast<firrtl::FIRRTLBaseType>(op->getOperand(1).getType());
575 return type && type.isPassive() &&
576 !op->getOperand(1).getDefiningOp<firrtl::InvalidValueOp>();
577 }
578 LogicalResult rewrite(Operation *op) override {
579 assert(match(op));
580 auto rhs = op->getOperand(1);
581 OpBuilder builder(op);
582 auto invOp =
583 builder.create<firrtl::InvalidValueOp>(rhs.getLoc(), rhs.getType());
584 auto *rhsOp = rhs.getDefiningOp();
585 op->setOperand(1, invOp);
586 if (rhsOp)
587 reduce::pruneUnusedOps(rhsOp, *this);
588 return success();
589 }
590 std::string getName() const override { return "connect-invalidator"; }
591 bool acceptSizeIncrease() const override { return true; }
592};
593
594/// A sample reduction pattern that removes FIRRTL annotations from ports and
595/// operations.
597 void beforeReduction(mlir::ModuleOp op) override { nlaRemover.clear(); }
598 void afterReduction(mlir::ModuleOp op) override { nlaRemover.remove(op); }
599 uint64_t match(Operation *op) override {
600 return op->hasAttr("annotations") || op->hasAttr("portAnnotations");
601 }
602 LogicalResult rewrite(Operation *op) override {
603 auto emptyArray = ArrayAttr::get(op->getContext(), {});
604 if (auto annos = op->getAttr("annotations")) {
606 op->setAttr("annotations", emptyArray);
607 }
608 if (auto annos = op->getAttr("portAnnotations")) {
610 auto attr = emptyArray;
611 if (isa<firrtl::InstanceOp>(op))
612 attr = ArrayAttr::get(
613 op->getContext(),
614 SmallVector<Attribute>(op->getNumResults(), emptyArray));
615 op->setAttr("portAnnotations", attr);
616 }
617 return success();
618 }
619 std::string getName() const override { return "annotation-remover"; }
621};
622
623/// A sample reduction pattern that removes ports from the root `firrtl.module`
624/// if the port is not used or just invalidated.
625struct RootPortPruner : public OpReduction<firrtl::FModuleOp> {
626 uint64_t match(firrtl::FModuleOp module) override {
627 auto circuit = module->getParentOfType<firrtl::CircuitOp>();
628 if (!circuit)
629 return 0;
630 return circuit.getNameAttr() == module.getNameAttr();
631 }
632 LogicalResult rewrite(firrtl::FModuleOp module) override {
633 assert(match(module));
634 size_t numPorts = module.getNumPorts();
635 llvm::BitVector dropPorts(numPorts);
636 for (unsigned i = 0; i != numPorts; ++i) {
637 if (onlyInvalidated(module.getArgument(i))) {
638 dropPorts.set(i);
639 for (auto *user :
640 llvm::make_early_inc_range(module.getArgument(i).getUsers()))
641 user->erase();
642 }
643 }
644 module.erasePorts(dropPorts);
645 return success();
646 }
647 std::string getName() const override { return "root-port-pruner"; }
648};
649
650/// A sample reduction pattern that replaces instances of `firrtl.extmodule`
651/// with wires.
652struct ExtmoduleInstanceRemover : public OpReduction<firrtl::InstanceOp> {
653 void beforeReduction(mlir::ModuleOp op) override {
654 symbols.clear();
656 }
657 void afterReduction(mlir::ModuleOp op) override { nlaRemover.remove(op); }
658
659 uint64_t match(firrtl::InstanceOp instOp) override {
660 return isa<firrtl::FExtModuleOp>(
661 instOp.getReferencedOperation(symbols.getNearestSymbolTable(instOp)));
662 }
663 LogicalResult rewrite(firrtl::InstanceOp instOp) override {
664 auto portInfo =
665 cast<firrtl::FModuleLike>(instOp.getReferencedOperation(
667 .getPorts();
668 ImplicitLocOpBuilder builder(instOp.getLoc(), instOp);
669 SmallVector<Value> replacementWires;
670 for (firrtl::PortInfo info : portInfo) {
671 auto wire =
672 builder
673 .create<firrtl::WireOp>(
674 info.type,
675 (Twine(instOp.getName()) + "_" + info.getName()).str())
676 .getResult();
677 if (info.isOutput()) {
678 auto inv = builder.create<firrtl::InvalidValueOp>(info.type);
679 builder.create<firrtl::ConnectOp>(wire, inv);
680 }
681 replacementWires.push_back(wire);
682 }
684 instOp.replaceAllUsesWith(std::move(replacementWires));
685 instOp->erase();
686 return success();
687 }
688 std::string getName() const override { return "extmodule-instance-remover"; }
689 bool acceptSizeIncrease() const override { return true; }
690
693};
694
695/// A sample reduction pattern that pushes connected values through wires.
697 void beforeReduction(mlir::ModuleOp op) override { opsToErase.clear(); }
698 void afterReduction(mlir::ModuleOp op) override {
699 for (auto *op : opsToErase)
700 op->dropAllReferences();
701 for (auto *op : opsToErase)
702 op->erase();
703 }
704
705 uint64_t match(Operation *op) override {
706 if (!isa<firrtl::FConnectLike>(op))
707 return 0;
708 auto dest = op->getOperand(0);
709 auto src = op->getOperand(1);
710 auto *destOp = dest.getDefiningOp();
711 auto *srcOp = src.getDefiningOp();
712 if (dest == src)
713 return 0;
714
715 // Ensure that the destination is something we should be able to forward
716 // through.
717 if (!isa_and_nonnull<firrtl::WireOp>(destOp))
718 return 0;
719
720 // Ensure that the destination is connected to only once, and all uses of
721 // the connection occur after the definition of the source.
722 unsigned numConnects = 0;
723 for (auto &use : dest.getUses()) {
724 auto *op = use.getOwner();
725 if (use.getOperandNumber() == 0 && isa<firrtl::FConnectLike>(op)) {
726 if (++numConnects > 1)
727 return 0;
728 continue;
729 }
730 if (srcOp && !srcOp->isBeforeInBlock(op))
731 return 0;
732 }
733
734 return 1;
735 }
736
737 LogicalResult rewrite(Operation *op) override {
738 auto dest = op->getOperand(0);
739 dest.replaceAllUsesWith(op->getOperand(1));
740 opsToErase.insert(dest.getDefiningOp());
741 opsToErase.insert(op);
742 return success();
743 }
744
745 std::string getName() const override { return "connect-forwarder"; }
746
747 llvm::DenseSet<Operation *> opsToErase;
748};
749
750/// A sample reduction pattern that replaces a single-use wire and register with
751/// an operand of the source value of the connection.
752template <unsigned OpNum>
754 uint64_t match(Operation *op) override {
755 if (!isa<firrtl::ConnectOp, firrtl::MatchingConnectOp>(op))
756 return 0;
757 auto dest = op->getOperand(0);
758 auto *destOp = dest.getDefiningOp();
759
760 // Ensure that the destination is used only once.
761 if (!destOp || !destOp->hasOneUse() ||
762 !isa<firrtl::WireOp, firrtl::RegOp, firrtl::RegResetOp>(destOp))
763 return 0;
764
765 auto *srcOp = op->getOperand(1).getDefiningOp();
766 if (!srcOp || OpNum >= srcOp->getNumOperands())
767 return 0;
768
769 auto resultTy = dyn_cast<firrtl::FIRRTLBaseType>(dest.getType());
770 auto opTy =
771 dyn_cast<firrtl::FIRRTLBaseType>(srcOp->getOperand(OpNum).getType());
772
773 return resultTy && opTy &&
774 resultTy.getWidthlessType() == opTy.getWidthlessType() &&
775 ((resultTy.getBitWidthOrSentinel() == -1) ==
776 (opTy.getBitWidthOrSentinel() == -1)) &&
777 isa<firrtl::UIntType, firrtl::SIntType>(resultTy);
778 }
779
780 LogicalResult rewrite(Operation *op) override {
781 auto *destOp = op->getOperand(0).getDefiningOp();
782 auto *srcOp = op->getOperand(1).getDefiningOp();
783 auto forwardedOperand = srcOp->getOperand(OpNum);
784 ImplicitLocOpBuilder builder(destOp->getLoc(), destOp);
785 Value newDest;
786 if (auto wire = dyn_cast<firrtl::WireOp>(destOp))
787 newDest = builder
788 .create<firrtl::WireOp>(forwardedOperand.getType(),
789 wire.getName())
790 .getResult();
791 else {
792 auto regName = destOp->getAttrOfType<StringAttr>("name");
793 // We can promote the register into a wire but we wouldn't do here because
794 // the error might be caused by the register.
795 auto clock = destOp->getOperand(0);
796 newDest = builder
797 .create<firrtl::RegOp>(forwardedOperand.getType(), clock,
798 regName ? regName.str() : "")
799 .getResult();
800 }
801
802 // Create new connection between a new wire and the forwarded operand.
803 builder.setInsertionPointAfter(op);
804 if (isa<firrtl::ConnectOp>(op))
805 builder.create<firrtl::ConnectOp>(newDest, forwardedOperand);
806 else
807 builder.create<firrtl::MatchingConnectOp>(newDest, forwardedOperand);
808
809 // Remove the old connection and destination. We don't have to replace them
810 // because destination has only one use.
811 op->erase();
812 destOp->erase();
813 reduce::pruneUnusedOps(srcOp, *this);
814
815 return success();
816 }
817 std::string getName() const override {
818 return ("connect-source-operand-" + Twine(OpNum) + "-forwarder").str();
819 }
820};
821
822/// A sample reduction pattern that tries to remove aggregate wires by replacing
823/// all subaccesses with new independent wires. This can disentangle large
824/// unused wires that are otherwise difficult to collect due to the subaccesses.
826 void beforeReduction(mlir::ModuleOp op) override { opsToErase.clear(); }
827 void afterReduction(mlir::ModuleOp op) override {
828 for (auto *op : opsToErase)
829 op->dropAllReferences();
830 for (auto *op : opsToErase)
831 op->erase();
832 }
833 uint64_t match(Operation *op) override {
834 // Only applies to wires and registers that are purely used in subaccess
835 // operations.
836 return isa<firrtl::WireOp, firrtl::RegOp, firrtl::RegResetOp>(op) &&
837 llvm::all_of(op->getUses(), [](auto &use) {
838 return use.getOperandNumber() == 0 &&
839 isa<firrtl::SubfieldOp, firrtl::SubindexOp,
840 firrtl::SubaccessOp>(use.getOwner());
841 });
842 }
843 LogicalResult rewrite(Operation *op) override {
844 assert(match(op));
845 OpBuilder builder(op);
846 bool isWire = isa<firrtl::WireOp>(op);
847 Value invalidClock;
848 if (!isWire)
849 invalidClock = builder.create<firrtl::InvalidValueOp>(
850 op->getLoc(), firrtl::ClockType::get(op->getContext()));
851 for (Operation *user : llvm::make_early_inc_range(op->getUsers())) {
852 builder.setInsertionPoint(user);
853 auto type = user->getResult(0).getType();
854 Operation *replOp;
855 if (isWire)
856 replOp = builder.create<firrtl::WireOp>(user->getLoc(), type);
857 else
858 replOp =
859 builder.create<firrtl::RegOp>(user->getLoc(), type, invalidClock);
860 user->replaceAllUsesWith(replOp);
861 opsToErase.insert(user);
862 }
863 opsToErase.insert(op);
864 return success();
865 }
866 std::string getName() const override { return "detach-subaccesses"; }
867 llvm::DenseSet<Operation *> opsToErase;
868};
869
870/// This reduction removes symbols on node ops. Name preservation creates a lot
871/// of nodes ops with symbols to keep name information but it also prevents
872/// normal canonicalizations.
873struct NodeSymbolRemover : public OpReduction<firrtl::NodeOp> {
874
875 uint64_t match(firrtl::NodeOp nodeOp) override {
876 return nodeOp.getInnerSym() &&
877 !nodeOp.getInnerSym()->getSymName().getValue().empty();
878 }
879
880 LogicalResult rewrite(firrtl::NodeOp nodeOp) override {
881 nodeOp.removeInnerSymAttr();
882 return success();
883 }
884
885 std::string getName() const override { return "node-symbol-remover"; }
886};
887
888/// A sample reduction pattern that eagerly inlines instances.
889struct EagerInliner : public OpReduction<firrtl::InstanceOp> {
890 void beforeReduction(mlir::ModuleOp op) override {
891 symbols.clear();
893 }
894 void afterReduction(mlir::ModuleOp op) override { nlaRemover.remove(op); }
895
896 uint64_t match(firrtl::InstanceOp instOp) override {
897 auto *tableOp = SymbolTable::getNearestSymbolTable(instOp);
898 auto *moduleOp =
899 instOp.getReferencedOperation(symbols.getSymbolTable(tableOp));
900 if (!isa<firrtl::FModuleOp>(moduleOp))
901 return 0;
902 return symbols.getSymbolUserMap(tableOp).getUsers(moduleOp).size() == 1;
903 }
904
905 LogicalResult rewrite(firrtl::InstanceOp instOp) override {
906 LLVM_DEBUG(llvm::dbgs()
907 << "Inlining instance `" << instOp.getName() << "`\n");
908 SmallVector<Value> argReplacements;
909 ImplicitLocOpBuilder builder(instOp.getLoc(), instOp);
910 for (unsigned i = 0, e = instOp.getNumResults(); i != e; ++i) {
911 auto result = instOp.getResult(i);
912 auto name = builder.getStringAttr(Twine(instOp.getName()) + "_" +
913 instOp.getPortNameStr(i));
914 auto wire =
915 builder
916 .create<firrtl::WireOp>(result.getType(), name,
917 firrtl::NameKindEnum::DroppableName,
918 instOp.getPortAnnotation(i), StringAttr{})
919 .getResult();
920 result.replaceAllUsesWith(wire);
921 argReplacements.push_back(wire);
922 }
923 auto *tableOp = SymbolTable::getNearestSymbolTable(instOp);
924 auto moduleOp = cast<firrtl::FModuleOp>(
925 instOp.getReferencedOperation(symbols.getSymbolTable(tableOp)));
926 for (auto &op : llvm::make_early_inc_range(*moduleOp.getBodyBlock())) {
927 op.remove();
928 builder.insert(&op);
929 for (auto &operand : op.getOpOperands())
930 if (auto blockArg = dyn_cast<BlockArgument>(operand.get()))
931 operand.set(argReplacements[blockArg.getArgNumber()]);
932 }
934 instOp->erase();
935 moduleOp->erase();
936 return success();
937 }
938
939 std::string getName() const override { return "eager-inliner"; }
940 bool acceptSizeIncrease() const override { return true; }
941
944};
945
946/// Psuedo-reduction that sanitizes the names of things inside modules. This is
947/// not an actual reduction, but often removes extraneous information that has
948/// no bearing on the actual reduction (and would likely be removed before
949/// sharing the reduction). This makes the following changes:
950///
951/// - All wires are renamed to "wire"
952/// - All registers are renamed to "reg"
953/// - All nodes are renamed to "node"
954/// - All memories are renamed to "mem"
955/// - All verification messages and labels are dropped
956///
958 uint64_t match(Operation *op) override {
959 // Only match operations with names.
960 return isa<firrtl::WireOp, firrtl::RegOp, firrtl::RegResetOp,
961 firrtl::NodeOp, firrtl::MemOp, chirrtl::CombMemOp,
962 chirrtl::SeqMemOp, firrtl::AssertOp, firrtl::AssumeOp,
963 firrtl::CoverOp>(op);
964 }
965 LogicalResult rewrite(Operation *op) override {
966 TypeSwitch<Operation *, void>(op)
967 .Case<firrtl::WireOp>([](auto op) { op.setName("wire"); })
968 .Case<firrtl::RegOp, firrtl::RegResetOp>(
969 [](auto op) { op.setName("reg"); })
970 .Case<firrtl::NodeOp>([](auto op) { op.setName("node"); })
971 .Case<firrtl::MemOp, chirrtl::CombMemOp, chirrtl::SeqMemOp>(
972 [](auto op) { op.setName("mem"); })
973 .Case<firrtl::AssertOp, firrtl::AssumeOp, firrtl::CoverOp>([](auto op) {
974 op->setAttr("message", StringAttr::get(op.getContext(), ""));
975 op->setAttr("name", StringAttr::get(op.getContext(), ""));
976 });
977 return success();
978 }
979
980 std::string getName() const override {
981 return "module-internal-name-sanitizer";
982 }
983
984 bool acceptSizeIncrease() const override { return true; }
985
986 bool isOneShot() const override { return true; }
987};
988
989/// Psuedo-reduction that sanitizes module, instance, and port names. This
990/// makes the following changes:
991///
992/// - All modules are given metasyntactic names ("Foo", "Bar", etc.)
993/// - All instances are renamed to match the new module name
994/// - All module ports are renamed in the following way:
995/// - All clocks are reanemd to "clk"
996/// - All resets are renamed to "rst"
997/// - All references are renamed to "ref"
998/// - Anything else is renamed to "port"
999///
1000struct ModuleNameSanitizer : OpReduction<firrtl::CircuitOp> {
1001
1002 const char *names[48] = {
1003 "Foo", "Bar", "Baz", "Qux", "Quux", "Quuux", "Quuuux",
1004 "Quz", "Corge", "Grault", "Bazola", "Ztesch", "Thud", "Grunt",
1005 "Bletch", "Fum", "Fred", "Jim", "Sheila", "Barney", "Flarp",
1006 "Zxc", "Spqr", "Wombat", "Shme", "Bongo", "Spam", "Eggs",
1007 "Snork", "Zot", "Blarg", "Wibble", "Toto", "Titi", "Tata",
1008 "Tutu", "Pippo", "Pluto", "Paperino", "Aap", "Noot", "Mies",
1009 "Oogle", "Foogle", "Boogle", "Zork", "Gork", "Bork"};
1010
1011 size_t nameIndex = 0;
1012
1013 const char *getName() {
1014 if (nameIndex >= 48)
1015 nameIndex = 0;
1016 return names[nameIndex++];
1017 };
1018
1019 size_t portNameIndex = 0;
1020
1022 if (portNameIndex >= 26)
1023 portNameIndex = 0;
1024 return 'a' + portNameIndex++;
1025 }
1026
1027 void beforeReduction(mlir::ModuleOp op) override { nameIndex = 0; }
1028
1029 LogicalResult rewrite(firrtl::CircuitOp circuitOp) override {
1030
1031 firrtl::InstanceGraph iGraph(circuitOp);
1032
1033 auto *circuitName = getName();
1034 iGraph.getTopLevelModule().setName(circuitName);
1035 circuitOp.setName(circuitName);
1036
1037 for (auto *node : iGraph) {
1038 auto module = node->getModule<firrtl::FModuleLike>();
1039
1040 bool shouldReplacePorts = false;
1041 SmallVector<Attribute> newNames;
1042 if (auto fmodule = dyn_cast<firrtl::FModuleOp>(*module)) {
1043 portNameIndex = 0;
1044 // TODO: The namespace should be unnecessary. However, some FIRRTL
1045 // passes expect that port names are unique.
1047 auto oldPorts = fmodule.getPorts();
1048 shouldReplacePorts = !oldPorts.empty();
1049 for (unsigned i = 0, e = fmodule.getNumPorts(); i != e; ++i) {
1050 auto port = oldPorts[i];
1051 auto newName = firrtl::FIRRTLTypeSwitch<Type, StringRef>(port.type)
1052 .Case<firrtl::ClockType>(
1053 [&](auto a) { return ns.newName("clk"); })
1054 .Case<firrtl::ResetType, firrtl::AsyncResetType>(
1055 [&](auto a) { return ns.newName("rst"); })
1056 .Case<firrtl::RefType>(
1057 [&](auto a) { return ns.newName("ref"); })
1058 .Default([&](auto a) {
1059 return ns.newName(Twine(getPortName()));
1060 });
1061 newNames.push_back(StringAttr::get(circuitOp.getContext(), newName));
1062 }
1063 fmodule->setAttr("portNames",
1064 ArrayAttr::get(fmodule.getContext(), newNames));
1065 }
1066
1067 if (module == iGraph.getTopLevelModule())
1068 continue;
1069 auto newName = StringAttr::get(circuitOp.getContext(), getName());
1070 module.setName(newName);
1071 for (auto *use : node->uses()) {
1072 auto instanceOp = dyn_cast<firrtl::InstanceOp>(*use->getInstance());
1073 instanceOp.setModuleName(newName);
1074 instanceOp.setName(newName);
1075 if (shouldReplacePorts)
1076 instanceOp.setPortNamesAttr(
1077 ArrayAttr::get(circuitOp.getContext(), newNames));
1078 }
1079 }
1080
1081 circuitOp->dump();
1082
1083 return success();
1084 }
1085
1086 std::string getName() const override { return "module-name-sanitizer"; }
1087
1088 bool acceptSizeIncrease() const override { return true; }
1089
1090 bool isOneShot() const override { return true; }
1091};
1092
1093//===----------------------------------------------------------------------===//
1094// Reduction Registration
1095//===----------------------------------------------------------------------===//
1096
1099 // Gather a list of reduction patterns that we should try. Ideally these are
1100 // assigned reasonable benefit indicators (higher benefit patterns are
1101 // prioritized). For example, things that can knock out entire modules while
1102 // being cheap should be tried first (and thus have higher benefit), before
1103 // trying to tweak operands of individual arithmetic ops.
1104 patterns.add<PassReduction, 30>(
1106 true);
1107 patterns.add<PassReduction, 29>(getContext(),
1108 firrtl::createLowerCHIRRTLPass(), true, true);
1109 patterns.add<PassReduction, 28>(getContext(), firrtl::createInferWidthsPass(),
1110 true, true);
1111 patterns.add<PassReduction, 27>(getContext(), firrtl::createInferResetsPass(),
1112 true, true);
1114 patterns.add<InstanceStubber, 25>();
1115 patterns.add<MemoryStubber, 24>();
1116 patterns.add<EagerInliner, 23>();
1117 patterns.add<PassReduction, 22>(
1118 getContext(), firrtl::createLowerFIRRTLTypesPass(), true, true);
1119 patterns.add<PassReduction, 21>(getContext(), firrtl::createExpandWhensPass(),
1120 true, true);
1121 patterns.add<PassReduction, 20>(getContext(), firrtl::createInlinerPass());
1122 patterns.add<PassReduction, 18>(getContext(),
1124 patterns.add<PassReduction, 17>(
1125 getContext(),
1126 firrtl::createRemoveUnusedPortsPass(/*ignoreDontTouch=*/true));
1127 patterns.add<NodeSymbolRemover, 15>();
1128 patterns.add<ConnectForwarder, 14>();
1129 patterns.add<ConnectInvalidator, 13>();
1130 patterns.add<FIRRTLConstantifier, 12>();
1134 patterns.add<DetachSubaccesses, 7>();
1135 patterns.add<AnnotationRemover, 6>();
1136 patterns.add<RootPortPruner, 5>();
1142 patterns.add<ModuleNameSanitizer, 0>();
1143}
1144
1146 mlir::DialectRegistry &registry) {
1147 registry.addExtension(+[](MLIRContext *ctx, FIRRTLDialect *dialect) {
1148 dialect->addInterfaces<FIRRTLReducePatternDialectInterface>();
1149 });
1150}
assert(baseType &&"element must be base type")
static bool isFlowSensitiveOp(Operation *op)
Check whether an operation interacts with flows in any way, which can make replacement and operand fo...
static void connectToLeafs(ImplicitLocOpBuilder &builder, Value dest, Value value)
Connect a value to every leave of a destination value.
static void invalidateOutputs(ImplicitLocOpBuilder &builder, Value value, SmallDenseMap< Type, Value, 8 > &invalidCache, bool flip=false)
Invalidate all the leaf fields of a value with a given flippedness by connecting an invalid value to ...
static bool onlyInvalidated(Value arg)
Check that all connections to a value are invalids.
static void reduceXor(ImplicitLocOpBuilder &builder, Value &into, Value value)
Reduce all leaf fields of a value through an XOR tree.
static std::optional< firrtl::FModuleOp > findInstantiatedModule(firrtl::InstanceOp instOp, ::detail::SymbolCache &symbols)
Utility to easily get the instantiated firrtl::FModuleOp or an empty optional in case another type of...
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition Namespace.h:30
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
Definition Namespace.h:85
This class implements the same functionality as TypeSwitch except that it uses firrtl::type_dyn_cast ...
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
This graph tracks modules and where they are instantiated.
FModuleLike getTopLevelModule()
Get the module corresponding to the top-level module of a circuit.
@ None
Don't explicitly preserve any named values.
Definition Passes.h:54
std::unique_ptr< mlir::Pass > createLowerFIRRTLTypesPass(PreserveAggregate::PreserveMode mode=PreserveAggregate::None, PreserveAggregate::PreserveMode memoryMode=PreserveAggregate::None)
This is the pass constructor.
std::unique_ptr< mlir::Pass > createIMConstPropPass()
std::unique_ptr< mlir::Pass > createRemoveUnusedPortsPass(bool ignoreDontTouch=false)
std::unique_ptr< mlir::Pass > createInlinerPass()
void registerReducePatternDialectInterface(mlir::DialectRegistry &registry)
Register the FIRRTL Reduction pattern dialect interface to the given registry.
std::unique_ptr< mlir::Pass > createDropNamesPass(PreserveValues::PreserveMode mode=PreserveValues::None)
Definition DropName.cpp:101
std::unique_ptr< mlir::Pass > createLowerCHIRRTLPass()
std::unique_ptr< mlir::Pass > createExpandWhensPass()
std::unique_ptr< mlir::Pass > createInferWidthsPass()
std::unique_ptr< mlir::Pass > createInferResetsPass()
ModulePort::Direction flip(ModulePort::Direction direction)
Flip a port direction.
Definition HWOps.cpp:36
void pruneUnusedOps(Operation *initialOp, Reduction &reduction)
Starting at the given op, traverse through it and its operands and erase operations that have no more...
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
A sample reduction pattern that removes FIRRTL annotations from ports and operations.
void afterReduction(mlir::ModuleOp op) override
Called after the reduction has been applied to a subset of operations.
uint64_t match(Operation *op) override
Check if the reduction can apply to a specific operation.
LogicalResult rewrite(Operation *op) override
Apply the reduction to a specific operation.
void beforeReduction(mlir::ModuleOp op) override
Called before the reduction is applied to a new subset of operations.
std::string getName() const override
Return a human-readable name for this reduction pattern.
A sample reduction pattern that pushes connected values through wires.
uint64_t match(Operation *op) override
Check if the reduction can apply to a specific operation.
void beforeReduction(mlir::ModuleOp op) override
Called before the reduction is applied to a new subset of operations.
void afterReduction(mlir::ModuleOp op) override
Called after the reduction has been applied to a subset of operations.
LogicalResult rewrite(Operation *op) override
Apply the reduction to a specific operation.
llvm::DenseSet< Operation * > opsToErase
std::string getName() const override
Return a human-readable name for this reduction pattern.
A sample reduction pattern that replaces the right-hand-side of firrtl.connect and firrtl....
LogicalResult rewrite(Operation *op) override
Apply the reduction to a specific operation.
bool acceptSizeIncrease() const override
Return true if the tool should accept the transformation this reduction performs on the module even i...
std::string getName() const override
Return a human-readable name for this reduction pattern.
uint64_t match(Operation *op) override
Check if the reduction can apply to a specific operation.
A sample reduction pattern that replaces a single-use wire and register with an operand of the source...
uint64_t match(Operation *op) override
Check if the reduction can apply to a specific operation.
LogicalResult rewrite(Operation *op) override
Apply the reduction to a specific operation.
std::string getName() const override
Return a human-readable name for this reduction pattern.
A sample reduction pattern that tries to remove aggregate wires by replacing all subaccesses with new...
void afterReduction(mlir::ModuleOp op) override
Called after the reduction has been applied to a subset of operations.
std::string getName() const override
Return a human-readable name for this reduction pattern.
uint64_t match(Operation *op) override
Check if the reduction can apply to a specific operation.
LogicalResult rewrite(Operation *op) override
Apply the reduction to a specific operation.
void beforeReduction(mlir::ModuleOp op) override
Called before the reduction is applied to a new subset of operations.
llvm::DenseSet< Operation * > opsToErase
A sample reduction pattern that eagerly inlines instances.
uint64_t match(firrtl::InstanceOp instOp) override
::detail::SymbolCache symbols
LogicalResult rewrite(firrtl::InstanceOp instOp) override
NLARemover nlaRemover
std::string getName() const override
Return a human-readable name for this reduction pattern.
bool acceptSizeIncrease() const override
Return true if the tool should accept the transformation this reduction performs on the module even i...
void beforeReduction(mlir::ModuleOp op) override
Called before the reduction is applied to a new subset of operations.
void afterReduction(mlir::ModuleOp op) override
Called after the reduction has been applied to a subset of operations.
A sample reduction pattern that replaces instances of firrtl.extmodule with wires.
::detail::SymbolCache symbols
LogicalResult rewrite(firrtl::InstanceOp instOp) override
std::string getName() const override
Return a human-readable name for this reduction pattern.
bool acceptSizeIncrease() const override
Return true if the tool should accept the transformation this reduction performs on the module even i...
void beforeReduction(mlir::ModuleOp op) override
Called before the reduction is applied to a new subset of operations.
uint64_t match(firrtl::InstanceOp instOp) override
void afterReduction(mlir::ModuleOp op) override
Called after the reduction has been applied to a subset of operations.
A sample reduction pattern that replaces FIRRTL operations with a constant zero of their type.
LogicalResult rewrite(Operation *op) override
Apply the reduction to a specific operation.
uint64_t match(Operation *op) override
Check if the reduction can apply to a specific operation.
std::string getName() const override
Return a human-readable name for this reduction pattern.
A sample reduction pattern that maps firrtl.module to firrtl.extmodule.
std::string getName() const override
Return a human-readable name for this reduction pattern.
LogicalResult rewrite(firrtl::FModuleOp module) override
::detail::SymbolCache symbols
void beforeReduction(mlir::ModuleOp op) override
Called before the reduction is applied to a new subset of operations.
void afterReduction(mlir::ModuleOp op) override
Called after the reduction has been applied to a subset of operations.
uint64_t match(firrtl::FModuleOp module) override
A sample reduction pattern that replaces all uses of an operation with one of its operands.
std::string getName() const override
Return a human-readable name for this reduction pattern.
uint64_t match(Operation *op) override
Check if the reduction can apply to a specific operation.
LogicalResult rewrite(Operation *op) override
Apply the reduction to a specific operation.
A sample reduction pattern that maps firrtl.instance to a set of invalidated wires.
void afterReduction(mlir::ModuleOp op) override
Called after the reduction has been applied to a subset of operations.
void beforeReduction(mlir::ModuleOp op) override
Called before the reduction is applied to a new subset of operations.
llvm::DenseSet< Operation * > erasedModules
LogicalResult rewrite(firrtl::InstanceOp instOp) override
llvm::DenseSet< Operation * > erasedInsts
uint64_t match(firrtl::InstanceOp instOp) override
ModuleSizeCache moduleSizes
bool acceptSizeIncrease() const override
Return true if the tool should accept the transformation this reduction performs on the module even i...
std::string getName() const override
Return a human-readable name for this reduction pattern.
::detail::SymbolCache symbols
A sample reduction pattern that maps firrtl.mem to a set of invalidated wires.
std::string getName() const override
Return a human-readable name for this reduction pattern.
bool acceptSizeIncrease() const override
Return true if the tool should accept the transformation this reduction performs on the module even i...
void beforeReduction(mlir::ModuleOp op) override
Called before the reduction is applied to a new subset of operations.
LogicalResult rewrite(firrtl::MemOp memOp) override
void afterReduction(mlir::ModuleOp op) override
Called after the reduction has been applied to a subset of operations.
Psuedo-reduction that sanitizes the names of things inside modules.
bool acceptSizeIncrease() const override
Return true if the tool should accept the transformation this reduction performs on the module even i...
uint64_t match(Operation *op) override
Check if the reduction can apply to a specific operation.
LogicalResult rewrite(Operation *op) override
Apply the reduction to a specific operation.
std::string getName() const override
Return a human-readable name for this reduction pattern.
bool isOneShot() const override
Return true if the tool should not try to reapply this reduction after it has been successful.
Psuedo-reduction that sanitizes module, instance, and port names.
std::string getName() const override
Return a human-readable name for this reduction pattern.
void beforeReduction(mlir::ModuleOp op) override
Called before the reduction is applied to a new subset of operations.
LogicalResult rewrite(firrtl::CircuitOp circuitOp) override
bool isOneShot() const override
Return true if the tool should not try to reapply this reduction after it has been successful.
bool acceptSizeIncrease() const override
Return true if the tool should accept the transformation this reduction performs on the module even i...
Utility to track the transitive size of modules.
llvm::DenseMap< Operation *, uint64_t > moduleSizes
uint64_t getModuleSize(Operation *module, ::detail::SymbolCache &symbols)
A tracker for track NLAs affected by a reduction.
void remove(mlir::ModuleOp module)
Remove all marked annotations.
void clear()
Clear the set of marked NLAs. Call this before attempting a reduction.
llvm::DenseSet< StringAttr > nlasToRemove
The set of NLAs to remove, identified by their symbol.
void markNLAsInAnnotation(Attribute anno)
Mark all NLAs referenced in the given annotation as to be removed.
void markNLAsInOperation(Operation *op)
Mark all NLAs referenced in an operation.
This reduction removes symbols on node ops.
std::string getName() const override
Return a human-readable name for this reduction pattern.
uint64_t match(firrtl::NodeOp nodeOp) override
LogicalResult rewrite(firrtl::NodeOp nodeOp) override
A sample reduction pattern that removes ports from the root firrtl.module if the port is not used or ...
std::string getName() const override
Return a human-readable name for this reduction pattern.
LogicalResult rewrite(firrtl::FModuleOp module) override
uint64_t match(firrtl::FModuleOp module) override
A reduction pattern that applies an mlir::Pass.
Definition Reduction.h:99
An abstract reduction pattern.
Definition Reduction.h:24
A dialect interface to provide reduction patterns to a reducer tool.
void populateReducePatterns(circt::ReducePatternSet &patterns) const override
This holds the name and type that describes the module's ports.
A utility doing lazy construction of SymbolTables and SymbolUserMaps, which is handy for reductions t...
SymbolUserMap & getSymbolUserMap(Operation *op)
SymbolUserMap & getNearestSymbolUserMap(Operation *op)
SymbolTableCollection tables
SymbolTable & getNearestSymbolTable(Operation *op)
SmallDenseMap< Operation *, SymbolUserMap, 2 > userMaps
SymbolTable & getSymbolTable(Operation *op)