Loading [MathJax]/extensions/tex2jax.js
CIRCT 22.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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 SymbolCache() : tables(std::make_unique<SymbolTableCollection>()) {}
36
37 SymbolTable &getSymbolTable(Operation *op) {
38 return tables->getSymbolTable(op);
39 }
40 SymbolTable &getNearestSymbolTable(Operation *op) {
41 return getSymbolTable(SymbolTable::getNearestSymbolTable(op));
42 }
43
44 SymbolUserMap &getSymbolUserMap(Operation *op) {
45 auto it = userMaps.find(op);
46 if (it != userMaps.end())
47 return it->second;
48 return userMaps.insert({op, SymbolUserMap(*tables, op)}).first->second;
49 }
50 SymbolUserMap &getNearestSymbolUserMap(Operation *op) {
51 return getSymbolUserMap(SymbolTable::getNearestSymbolTable(op));
52 }
53
54 void clear() {
55 tables = std::make_unique<SymbolTableCollection>();
56 userMaps.clear();
57 }
58
59private:
60 std::unique_ptr<SymbolTableCollection> tables;
62};
63} // namespace detail
64
65/// Utility to easily get the instantiated firrtl::FModuleOp or an empty
66/// optional in case another type of module is instantiated.
67static std::optional<firrtl::FModuleOp>
68findInstantiatedModule(firrtl::InstanceOp instOp,
69 ::detail::SymbolCache &symbols) {
70 auto *tableOp = SymbolTable::getNearestSymbolTable(instOp);
71 auto moduleOp = dyn_cast<firrtl::FModuleOp>(
72 instOp.getReferencedOperation(symbols.getSymbolTable(tableOp)));
73 return moduleOp ? std::optional(moduleOp) : std::nullopt;
74}
75
76/// Utility to track the transitive size of modules.
78 void clear() { moduleSizes.clear(); }
79
80 uint64_t getModuleSize(Operation *module, ::detail::SymbolCache &symbols) {
81 if (auto it = moduleSizes.find(module); it != moduleSizes.end())
82 return it->second;
83 uint64_t size = 1;
84 module->walk([&](Operation *op) {
85 size += 1;
86 if (auto instOp = dyn_cast<firrtl::InstanceOp>(op))
87 if (auto instModule = findInstantiatedModule(instOp, symbols))
88 size += getModuleSize(*instModule, symbols);
89 });
90 moduleSizes.insert({module, size});
91 return size;
92 }
93
94private:
95 llvm::DenseMap<Operation *, uint64_t> moduleSizes;
96};
97
98/// Check that all connections to a value are invalids.
99static bool onlyInvalidated(Value arg) {
100 return llvm::all_of(arg.getUses(), [](OpOperand &use) {
101 auto *op = use.getOwner();
102 if (!isa<firrtl::ConnectOp, firrtl::MatchingConnectOp>(op))
103 return false;
104 if (use.getOperandNumber() != 0)
105 return false;
106 if (!op->getOperand(1).getDefiningOp<firrtl::InvalidValueOp>())
107 return false;
108 return true;
109 });
110}
111
112/// A tracker for track NLAs affected by a reduction. Performs the necessary
113/// cleanup steps in order to maintain IR validity after the reduction has
114/// applied. For example, removing an instance that forms part of an NLA path
115/// requires that NLA to be removed as well.
117 /// Clear the set of marked NLAs. Call this before attempting a reduction.
118 void clear() { nlasToRemove.clear(); }
119
120 /// Remove all marked annotations. Call this after applying a reduction in
121 /// order to validate the IR.
122 void remove(mlir::ModuleOp module) {
123 unsigned numRemoved = 0;
124 (void)numRemoved;
125 for (Operation &rootOp : *module.getBody()) {
126 if (!isa<firrtl::CircuitOp>(&rootOp))
127 continue;
128 SymbolTable symbolTable(&rootOp);
129 for (auto sym : nlasToRemove) {
130 if (auto *op = symbolTable.lookup(sym)) {
131 ++numRemoved;
132 op->erase();
133 }
134 }
135 }
136 LLVM_DEBUG({
137 unsigned numLost = nlasToRemove.size() - numRemoved;
138 if (numRemoved > 0 || numLost > 0) {
139 llvm::dbgs() << "Removed " << numRemoved << " NLAs";
140 if (numLost > 0)
141 llvm::dbgs() << " (" << numLost << " no longer there)";
142 llvm::dbgs() << "\n";
143 }
144 });
145 }
146
147 /// Mark all NLAs referenced in the given annotation as to be removed. This
148 /// can be an entire array or dictionary of annotations, and the function will
149 /// descend into child annotations appropriately.
150 void markNLAsInAnnotation(Attribute anno) {
151 if (auto dict = dyn_cast<DictionaryAttr>(anno)) {
152 if (auto field = dict.getAs<FlatSymbolRefAttr>("circt.nonlocal"))
153 nlasToRemove.insert(field.getAttr());
154 for (auto namedAttr : dict)
155 markNLAsInAnnotation(namedAttr.getValue());
156 } else if (auto array = dyn_cast<ArrayAttr>(anno)) {
157 for (auto attr : array)
158 markNLAsInAnnotation(attr);
159 }
160 }
161
162 /// Mark all NLAs referenced in an operation. Also traverses all nested
163 /// operations. Call this before removing an operation, to mark any associated
164 /// NLAs as to be removed as well.
165 void markNLAsInOperation(Operation *op) {
166 op->walk([&](Operation *op) {
167 if (auto annos = op->getAttrOfType<ArrayAttr>("annotations"))
168 markNLAsInAnnotation(annos);
169 });
170 }
171
172 /// The set of NLAs to remove, identified by their symbol.
173 llvm::DenseSet<StringAttr> nlasToRemove;
174};
175
176//===----------------------------------------------------------------------===//
177// Reduction patterns
178//===----------------------------------------------------------------------===//
179
180/// A sample reduction pattern that maps `firrtl.module` to `firrtl.extmodule`.
181struct FIRRTLModuleExternalizer : public OpReduction<firrtl::FModuleOp> {
182 void beforeReduction(mlir::ModuleOp op) override {
184 symbols.clear();
186 }
187 void afterReduction(mlir::ModuleOp op) override { nlaRemover.remove(op); }
188
189 uint64_t match(firrtl::FModuleOp module) override {
190 return moduleSizes.getModuleSize(module, symbols);
191 }
192
193 LogicalResult rewrite(firrtl::FModuleOp module) override {
195 OpBuilder builder(module);
196 firrtl::FExtModuleOp::create(
197 builder, module->getLoc(),
198 module->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()),
199 module.getConventionAttr(), module.getPorts(), ArrayAttr(), StringRef(),
200 module.getAnnotationsAttr());
201 module->erase();
202 return success();
203 }
204
205 std::string getName() const override { return "firrtl-module-externalizer"; }
206
210};
211
212/// Invalidate all the leaf fields of a value with a given flippedness by
213/// connecting an invalid value to them. This is useful for ensuring that all
214/// output ports of an instance or memory (including those nested in bundles)
215/// are properly invalidated.
216static void invalidateOutputs(ImplicitLocOpBuilder &builder, Value value,
217 SmallDenseMap<Type, Value, 8> &invalidCache,
218 bool flip = false) {
219 auto type = dyn_cast<firrtl::FIRRTLType>(value.getType());
220 if (!type)
221 return;
222
223 // Descend into bundles by creating subfield ops.
224 if (auto bundleType = dyn_cast<firrtl::BundleType>(type)) {
225 for (auto element : llvm::enumerate(bundleType.getElements())) {
226 auto subfield =
227 builder.createOrFold<firrtl::SubfieldOp>(value, element.index());
228 invalidateOutputs(builder, subfield, invalidCache,
229 flip ^ element.value().isFlip);
230 if (subfield.use_empty())
231 subfield.getDefiningOp()->erase();
232 }
233 return;
234 }
235
236 // Descend into vectors by creating subindex ops.
237 if (auto vectorType = dyn_cast<firrtl::FVectorType>(type)) {
238 for (unsigned i = 0, e = vectorType.getNumElements(); i != e; ++i) {
239 auto subindex = builder.createOrFold<firrtl::SubindexOp>(value, i);
240 invalidateOutputs(builder, subindex, invalidCache, flip);
241 if (subindex.use_empty())
242 subindex.getDefiningOp()->erase();
243 }
244 return;
245 }
246
247 // Only drive outputs.
248 if (flip)
249 return;
250 Value invalid = invalidCache.lookup(type);
251 if (!invalid) {
252 invalid = firrtl::InvalidValueOp::create(builder, type);
253 invalidCache.insert({type, invalid});
254 }
255 firrtl::ConnectOp::create(builder, value, invalid);
256}
257
258/// Connect a value to every leave of a destination value.
259static void connectToLeafs(ImplicitLocOpBuilder &builder, Value dest,
260 Value value) {
261 auto type = dyn_cast<firrtl::FIRRTLBaseType>(dest.getType());
262 if (!type)
263 return;
264 if (auto bundleType = dyn_cast<firrtl::BundleType>(type)) {
265 for (auto element : llvm::enumerate(bundleType.getElements()))
266 connectToLeafs(builder,
267 firrtl::SubfieldOp::create(builder, dest, element.index()),
268 value);
269 return;
270 }
271 if (auto vectorType = dyn_cast<firrtl::FVectorType>(type)) {
272 for (unsigned i = 0, e = vectorType.getNumElements(); i != e; ++i)
273 connectToLeafs(builder, firrtl::SubindexOp::create(builder, dest, i),
274 value);
275 return;
276 }
277 auto valueType = dyn_cast<firrtl::FIRRTLBaseType>(value.getType());
278 if (!valueType)
279 return;
280 auto destWidth = type.getBitWidthOrSentinel();
281 auto valueWidth = valueType ? valueType.getBitWidthOrSentinel() : -1;
282 if (destWidth >= 0 && valueWidth >= 0 && destWidth < valueWidth)
283 value = firrtl::HeadPrimOp::create(builder, value, destWidth);
284 if (!isa<firrtl::UIntType>(type)) {
285 if (isa<firrtl::SIntType>(type))
286 value = firrtl::AsSIntPrimOp::create(builder, value);
287 else
288 return;
289 }
290 firrtl::ConnectOp::create(builder, dest, value);
291}
292
293/// Reduce all leaf fields of a value through an XOR tree.
294static void reduceXor(ImplicitLocOpBuilder &builder, Value &into, Value value) {
295 auto type = dyn_cast<firrtl::FIRRTLType>(value.getType());
296 if (!type)
297 return;
298 if (auto bundleType = dyn_cast<firrtl::BundleType>(type)) {
299 for (auto element : llvm::enumerate(bundleType.getElements()))
300 reduceXor(
301 builder, into,
302 builder.createOrFold<firrtl::SubfieldOp>(value, element.index()));
303 return;
304 }
305 if (auto vectorType = dyn_cast<firrtl::FVectorType>(type)) {
306 for (unsigned i = 0, e = vectorType.getNumElements(); i != e; ++i)
307 reduceXor(builder, into,
308 builder.createOrFold<firrtl::SubindexOp>(value, i));
309 return;
310 }
311 if (!isa<firrtl::UIntType>(type)) {
312 if (isa<firrtl::SIntType>(type))
313 value = firrtl::AsUIntPrimOp::create(builder, value);
314 else
315 return;
316 }
317 into = into ? builder.createOrFold<firrtl::XorPrimOp>(into, value) : value;
318}
319
320/// A sample reduction pattern that maps `firrtl.instance` to a set of
321/// invalidated wires. This often shortcuts a long iterative process of connect
322/// invalidation, module externalization, and wire stripping
323struct InstanceStubber : public OpReduction<firrtl::InstanceOp> {
324 void beforeReduction(mlir::ModuleOp op) override {
325 erasedInsts.clear();
326 erasedModules.clear();
327 symbols.clear();
330 }
331 void afterReduction(mlir::ModuleOp op) override {
332 // Look into deleted modules to find additional instances that are no longer
333 // instantiated anywhere.
334 SmallVector<Operation *> worklist;
335 auto deadInsts = erasedInsts;
336 for (auto *op : erasedModules)
337 worklist.push_back(op);
338 while (!worklist.empty()) {
339 auto *op = worklist.pop_back_val();
340 auto *tableOp = SymbolTable::getNearestSymbolTable(op);
341 op->walk([&](firrtl::InstanceOp instOp) {
342 auto moduleOp = cast<firrtl::FModuleLike>(
343 instOp.getReferencedOperation(symbols.getSymbolTable(tableOp)));
344 deadInsts.insert(instOp);
345 if (llvm::all_of(
346 symbols.getSymbolUserMap(tableOp).getUsers(moduleOp),
347 [&](Operation *user) { return deadInsts.contains(user); })) {
348 LLVM_DEBUG(llvm::dbgs() << "- Removing transitively unused module `"
349 << moduleOp.getModuleName() << "`\n");
350 erasedModules.insert(moduleOp);
351 worklist.push_back(moduleOp);
352 }
353 });
354 }
355
356 for (auto *op : erasedInsts)
357 op->erase();
358 for (auto *op : erasedModules)
359 op->erase();
360 nlaRemover.remove(op);
361 }
362
363 uint64_t match(firrtl::InstanceOp instOp) override {
364 if (auto fmoduleOp = findInstantiatedModule(instOp, symbols))
365 return moduleSizes.getModuleSize(*fmoduleOp, symbols);
366 return 0;
367 }
368
369 LogicalResult rewrite(firrtl::InstanceOp instOp) override {
370 LLVM_DEBUG(llvm::dbgs()
371 << "Stubbing instance `" << instOp.getName() << "`\n");
372 ImplicitLocOpBuilder builder(instOp.getLoc(), instOp);
374 for (unsigned i = 0, e = instOp.getNumResults(); i != e; ++i) {
375 auto result = instOp.getResult(i);
376 auto name = builder.getStringAttr(Twine(instOp.getName()) + "_" +
377 instOp.getPortNameStr(i));
378 auto wire =
379 firrtl::WireOp::create(builder, result.getType(), name,
380 firrtl::NameKindEnum::DroppableName,
381 instOp.getPortAnnotation(i), StringAttr{})
382 .getResult();
383 invalidateOutputs(builder, wire, invalidCache,
384 instOp.getPortDirection(i) == firrtl::Direction::In);
385 result.replaceAllUsesWith(wire);
386 }
387 auto *tableOp = SymbolTable::getNearestSymbolTable(instOp);
388 auto moduleOp = cast<firrtl::FModuleLike>(
389 instOp.getReferencedOperation(symbols.getSymbolTable(tableOp)));
391 erasedInsts.insert(instOp);
392 if (llvm::all_of(
393 symbols.getSymbolUserMap(tableOp).getUsers(moduleOp),
394 [&](Operation *user) { return erasedInsts.contains(user); })) {
395 LLVM_DEBUG(llvm::dbgs() << "- Removing now unused module `"
396 << moduleOp.getModuleName() << "`\n");
397 erasedModules.insert(moduleOp);
398 }
399 return success();
400 }
401
402 std::string getName() const override { return "instance-stubber"; }
403 bool acceptSizeIncrease() const override { return true; }
404
407 llvm::DenseSet<Operation *> erasedInsts;
408 llvm::DenseSet<Operation *> erasedModules;
410};
411
412/// A sample reduction pattern that maps `firrtl.mem` to a set of invalidated
413/// wires.
414struct MemoryStubber : public OpReduction<firrtl::MemOp> {
415 void beforeReduction(mlir::ModuleOp op) override { nlaRemover.clear(); }
416 void afterReduction(mlir::ModuleOp op) override { nlaRemover.remove(op); }
417 LogicalResult rewrite(firrtl::MemOp memOp) override {
418 LLVM_DEBUG(llvm::dbgs() << "Stubbing memory `" << memOp.getName() << "`\n");
419 ImplicitLocOpBuilder builder(memOp.getLoc(), memOp);
421 Value xorInputs;
422 SmallVector<Value> outputs;
423 for (unsigned i = 0, e = memOp.getNumResults(); i != e; ++i) {
424 auto result = memOp.getResult(i);
425 auto name = builder.getStringAttr(Twine(memOp.getName()) + "_" +
426 memOp.getPortNameStr(i));
427 auto wire =
428 firrtl::WireOp::create(builder, 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 =
558 firrtl::ConstantOp::create(builder, op->getLoc(), type,
559 APSInt(width, isa<firrtl::UIntType>(type)));
560 op->replaceAllUsesWith(newOp);
561 reduce::pruneUnusedOps(op, *this);
562 return success();
563 }
564 std::string getName() const override { return "firrtl-constantifier"; }
565};
566
567/// A sample reduction pattern that replaces the right-hand-side of
568/// `firrtl.connect` and `firrtl.matchingconnect` operations with a
569/// `firrtl.invalidvalue`. This removes uses from the fanin cone to these
570/// connects and creates opportunities for reduction in DCE/CSE.
572 uint64_t match(Operation *op) override {
573 if (!isa<firrtl::ConnectOp, firrtl::MatchingConnectOp>(op))
574 return 0;
575 auto type = dyn_cast<firrtl::FIRRTLBaseType>(op->getOperand(1).getType());
576 return type && type.isPassive() &&
577 !op->getOperand(1).getDefiningOp<firrtl::InvalidValueOp>();
578 }
579 LogicalResult rewrite(Operation *op) override {
580 assert(match(op));
581 auto rhs = op->getOperand(1);
582 OpBuilder builder(op);
583 auto invOp =
584 firrtl::InvalidValueOp::create(builder, rhs.getLoc(), rhs.getType());
585 auto *rhsOp = rhs.getDefiningOp();
586 op->setOperand(1, invOp);
587 if (rhsOp)
588 reduce::pruneUnusedOps(rhsOp, *this);
589 return success();
590 }
591 std::string getName() const override { return "connect-invalidator"; }
592 bool acceptSizeIncrease() const override { return true; }
593};
594
595/// A sample reduction pattern that removes FIRRTL annotations from ports and
596/// operations.
598 void beforeReduction(mlir::ModuleOp op) override { nlaRemover.clear(); }
599 void afterReduction(mlir::ModuleOp op) override { nlaRemover.remove(op); }
600 uint64_t match(Operation *op) override {
601 return op->hasAttr("annotations") || op->hasAttr("portAnnotations");
602 }
603 LogicalResult rewrite(Operation *op) override {
604 auto emptyArray = ArrayAttr::get(op->getContext(), {});
605 if (auto annos = op->getAttr("annotations")) {
607 op->setAttr("annotations", emptyArray);
608 }
609 if (auto annos = op->getAttr("portAnnotations")) {
611 auto attr = emptyArray;
612 if (isa<firrtl::InstanceOp>(op))
613 attr = ArrayAttr::get(
614 op->getContext(),
615 SmallVector<Attribute>(op->getNumResults(), emptyArray));
616 op->setAttr("portAnnotations", attr);
617 }
618 return success();
619 }
620 std::string getName() const override { return "annotation-remover"; }
622};
623
624/// A sample reduction pattern that removes ports from the root `firrtl.module`
625/// if the port is not used or just invalidated.
626struct RootPortPruner : public OpReduction<firrtl::FModuleOp> {
627 uint64_t match(firrtl::FModuleOp module) override {
628 auto circuit = module->getParentOfType<firrtl::CircuitOp>();
629 if (!circuit)
630 return 0;
631 return circuit.getNameAttr() == module.getNameAttr();
632 }
633 LogicalResult rewrite(firrtl::FModuleOp module) override {
634 assert(match(module));
635 size_t numPorts = module.getNumPorts();
636 llvm::BitVector dropPorts(numPorts);
637 for (unsigned i = 0; i != numPorts; ++i) {
638 if (onlyInvalidated(module.getArgument(i))) {
639 dropPorts.set(i);
640 for (auto *user :
641 llvm::make_early_inc_range(module.getArgument(i).getUsers()))
642 user->erase();
643 }
644 }
645 module.erasePorts(dropPorts);
646 return success();
647 }
648 std::string getName() const override { return "root-port-pruner"; }
649};
650
651/// A sample reduction pattern that replaces instances of `firrtl.extmodule`
652/// with wires.
653struct ExtmoduleInstanceRemover : public OpReduction<firrtl::InstanceOp> {
654 void beforeReduction(mlir::ModuleOp op) override {
655 symbols.clear();
657 }
658 void afterReduction(mlir::ModuleOp op) override { nlaRemover.remove(op); }
659
660 uint64_t match(firrtl::InstanceOp instOp) override {
661 return isa<firrtl::FExtModuleOp>(
662 instOp.getReferencedOperation(symbols.getNearestSymbolTable(instOp)));
663 }
664 LogicalResult rewrite(firrtl::InstanceOp instOp) override {
665 auto portInfo =
666 cast<firrtl::FModuleLike>(instOp.getReferencedOperation(
668 .getPorts();
669 ImplicitLocOpBuilder builder(instOp.getLoc(), instOp);
670 SmallVector<Value> replacementWires;
671 for (firrtl::PortInfo info : portInfo) {
672 auto wire = firrtl::WireOp::create(
673 builder, info.type,
674 (Twine(instOp.getName()) + "_" + info.getName()).str())
675 .getResult();
676 if (info.isOutput()) {
677 auto inv = firrtl::InvalidValueOp::create(builder, info.type);
678 firrtl::ConnectOp::create(builder, wire, inv);
679 }
680 replacementWires.push_back(wire);
681 }
683 instOp.replaceAllUsesWith(std::move(replacementWires));
684 instOp->erase();
685 return success();
686 }
687 std::string getName() const override { return "extmodule-instance-remover"; }
688 bool acceptSizeIncrease() const override { return true; }
689
692};
693
694/// A sample reduction pattern that pushes connected values through wires.
696 void beforeReduction(mlir::ModuleOp op) override { opsToErase.clear(); }
697 void afterReduction(mlir::ModuleOp op) override {
698 for (auto *op : opsToErase)
699 op->dropAllReferences();
700 for (auto *op : opsToErase)
701 op->erase();
702 }
703
704 uint64_t match(Operation *op) override {
705 if (!isa<firrtl::FConnectLike>(op))
706 return 0;
707 auto dest = op->getOperand(0);
708 auto src = op->getOperand(1);
709 auto *destOp = dest.getDefiningOp();
710 auto *srcOp = src.getDefiningOp();
711 if (dest == src)
712 return 0;
713
714 // Ensure that the destination is something we should be able to forward
715 // through.
716 if (!isa_and_nonnull<firrtl::WireOp>(destOp))
717 return 0;
718
719 // Ensure that the destination is connected to only once, and all uses of
720 // the connection occur after the definition of the source.
721 unsigned numConnects = 0;
722 for (auto &use : dest.getUses()) {
723 auto *op = use.getOwner();
724 if (use.getOperandNumber() == 0 && isa<firrtl::FConnectLike>(op)) {
725 if (++numConnects > 1)
726 return 0;
727 continue;
728 }
729 if (srcOp && !srcOp->isBeforeInBlock(op))
730 return 0;
731 }
732
733 return 1;
734 }
735
736 LogicalResult rewrite(Operation *op) override {
737 auto dest = op->getOperand(0);
738 dest.replaceAllUsesWith(op->getOperand(1));
739 opsToErase.insert(dest.getDefiningOp());
740 opsToErase.insert(op);
741 return success();
742 }
743
744 std::string getName() const override { return "connect-forwarder"; }
745
746 llvm::DenseSet<Operation *> opsToErase;
747};
748
749/// A sample reduction pattern that replaces a single-use wire and register with
750/// an operand of the source value of the connection.
751template <unsigned OpNum>
753 uint64_t match(Operation *op) override {
754 if (!isa<firrtl::ConnectOp, firrtl::MatchingConnectOp>(op))
755 return 0;
756 auto dest = op->getOperand(0);
757 auto *destOp = dest.getDefiningOp();
758
759 // Ensure that the destination is used only once.
760 if (!destOp || !destOp->hasOneUse() ||
761 !isa<firrtl::WireOp, firrtl::RegOp, firrtl::RegResetOp>(destOp))
762 return 0;
763
764 auto *srcOp = op->getOperand(1).getDefiningOp();
765 if (!srcOp || OpNum >= srcOp->getNumOperands())
766 return 0;
767
768 auto resultTy = dyn_cast<firrtl::FIRRTLBaseType>(dest.getType());
769 auto opTy =
770 dyn_cast<firrtl::FIRRTLBaseType>(srcOp->getOperand(OpNum).getType());
771
772 return resultTy && opTy &&
773 resultTy.getWidthlessType() == opTy.getWidthlessType() &&
774 ((resultTy.getBitWidthOrSentinel() == -1) ==
775 (opTy.getBitWidthOrSentinel() == -1)) &&
776 isa<firrtl::UIntType, firrtl::SIntType>(resultTy);
777 }
778
779 LogicalResult rewrite(Operation *op) override {
780 auto *destOp = op->getOperand(0).getDefiningOp();
781 auto *srcOp = op->getOperand(1).getDefiningOp();
782 auto forwardedOperand = srcOp->getOperand(OpNum);
783 ImplicitLocOpBuilder builder(destOp->getLoc(), destOp);
784 Value newDest;
785 if (auto wire = dyn_cast<firrtl::WireOp>(destOp))
786 newDest = firrtl::WireOp::create(builder, forwardedOperand.getType(),
787 wire.getName())
788 .getResult();
789 else {
790 auto regName = destOp->getAttrOfType<StringAttr>("name");
791 // We can promote the register into a wire but we wouldn't do here because
792 // the error might be caused by the register.
793 auto clock = destOp->getOperand(0);
794 newDest = firrtl::RegOp::create(builder, forwardedOperand.getType(),
795 clock, regName ? regName.str() : "")
796 .getResult();
797 }
798
799 // Create new connection between a new wire and the forwarded operand.
800 builder.setInsertionPointAfter(op);
801 if (isa<firrtl::ConnectOp>(op))
802 firrtl::ConnectOp::create(builder, newDest, forwardedOperand);
803 else
804 firrtl::MatchingConnectOp::create(builder, newDest, forwardedOperand);
805
806 // Remove the old connection and destination. We don't have to replace them
807 // because destination has only one use.
808 op->erase();
809 destOp->erase();
810 reduce::pruneUnusedOps(srcOp, *this);
811
812 return success();
813 }
814 std::string getName() const override {
815 return ("connect-source-operand-" + Twine(OpNum) + "-forwarder").str();
816 }
817};
818
819/// A sample reduction pattern that tries to remove aggregate wires by replacing
820/// all subaccesses with new independent wires. This can disentangle large
821/// unused wires that are otherwise difficult to collect due to the subaccesses.
823 void beforeReduction(mlir::ModuleOp op) override { opsToErase.clear(); }
824 void afterReduction(mlir::ModuleOp op) override {
825 for (auto *op : opsToErase)
826 op->dropAllReferences();
827 for (auto *op : opsToErase)
828 op->erase();
829 }
830 uint64_t match(Operation *op) override {
831 // Only applies to wires and registers that are purely used in subaccess
832 // operations.
833 return isa<firrtl::WireOp, firrtl::RegOp, firrtl::RegResetOp>(op) &&
834 llvm::all_of(op->getUses(), [](auto &use) {
835 return use.getOperandNumber() == 0 &&
836 isa<firrtl::SubfieldOp, firrtl::SubindexOp,
837 firrtl::SubaccessOp>(use.getOwner());
838 });
839 }
840 LogicalResult rewrite(Operation *op) override {
841 assert(match(op));
842 OpBuilder builder(op);
843 bool isWire = isa<firrtl::WireOp>(op);
844 Value invalidClock;
845 if (!isWire)
846 invalidClock = firrtl::InvalidValueOp::create(
847 builder, op->getLoc(), firrtl::ClockType::get(op->getContext()));
848 for (Operation *user : llvm::make_early_inc_range(op->getUsers())) {
849 builder.setInsertionPoint(user);
850 auto type = user->getResult(0).getType();
851 Operation *replOp;
852 if (isWire)
853 replOp = firrtl::WireOp::create(builder, user->getLoc(), type);
854 else
855 replOp =
856 firrtl::RegOp::create(builder, user->getLoc(), type, invalidClock);
857 user->replaceAllUsesWith(replOp);
858 opsToErase.insert(user);
859 }
860 opsToErase.insert(op);
861 return success();
862 }
863 std::string getName() const override { return "detach-subaccesses"; }
864 llvm::DenseSet<Operation *> opsToErase;
865};
866
867/// This reduction removes symbols on node ops. Name preservation creates a lot
868/// of nodes ops with symbols to keep name information but it also prevents
869/// normal canonicalizations.
870struct NodeSymbolRemover : public OpReduction<firrtl::NodeOp> {
871
872 uint64_t match(firrtl::NodeOp nodeOp) override {
873 return nodeOp.getInnerSym() &&
874 !nodeOp.getInnerSym()->getSymName().getValue().empty();
875 }
876
877 LogicalResult rewrite(firrtl::NodeOp nodeOp) override {
878 nodeOp.removeInnerSymAttr();
879 return success();
880 }
881
882 std::string getName() const override { return "node-symbol-remover"; }
883};
884
885/// A sample reduction pattern that eagerly inlines instances.
886struct EagerInliner : public OpReduction<firrtl::InstanceOp> {
887 void beforeReduction(mlir::ModuleOp op) override {
888 symbols.clear();
890 }
891 void afterReduction(mlir::ModuleOp op) override { nlaRemover.remove(op); }
892
893 uint64_t match(firrtl::InstanceOp instOp) override {
894 auto *tableOp = SymbolTable::getNearestSymbolTable(instOp);
895 auto *moduleOp =
896 instOp.getReferencedOperation(symbols.getSymbolTable(tableOp));
897 if (!isa<firrtl::FModuleOp>(moduleOp))
898 return 0;
899 return symbols.getSymbolUserMap(tableOp).getUsers(moduleOp).size() == 1;
900 }
901
902 LogicalResult rewrite(firrtl::InstanceOp instOp) override {
903 LLVM_DEBUG(llvm::dbgs()
904 << "Inlining instance `" << instOp.getName() << "`\n");
905 SmallVector<Value> argReplacements;
906 ImplicitLocOpBuilder builder(instOp.getLoc(), instOp);
907 for (unsigned i = 0, e = instOp.getNumResults(); i != e; ++i) {
908 auto result = instOp.getResult(i);
909 auto name = builder.getStringAttr(Twine(instOp.getName()) + "_" +
910 instOp.getPortNameStr(i));
911 auto wire =
912 firrtl::WireOp::create(builder, result.getType(), name,
913 firrtl::NameKindEnum::DroppableName,
914 instOp.getPortAnnotation(i), StringAttr{})
915 .getResult();
916 result.replaceAllUsesWith(wire);
917 argReplacements.push_back(wire);
918 }
919 auto *tableOp = SymbolTable::getNearestSymbolTable(instOp);
920 auto moduleOp = cast<firrtl::FModuleOp>(
921 instOp.getReferencedOperation(symbols.getSymbolTable(tableOp)));
922 for (auto &op : llvm::make_early_inc_range(*moduleOp.getBodyBlock())) {
923 op.remove();
924 builder.insert(&op);
925 for (auto &operand : op.getOpOperands())
926 if (auto blockArg = dyn_cast<BlockArgument>(operand.get()))
927 operand.set(argReplacements[blockArg.getArgNumber()]);
928 }
930 instOp->erase();
931 moduleOp->erase();
932 return success();
933 }
934
935 std::string getName() const override { return "eager-inliner"; }
936 bool acceptSizeIncrease() const override { return true; }
937
940};
941
942/// Psuedo-reduction that sanitizes the names of things inside modules. This is
943/// not an actual reduction, but often removes extraneous information that has
944/// no bearing on the actual reduction (and would likely be removed before
945/// sharing the reduction). This makes the following changes:
946///
947/// - All wires are renamed to "wire"
948/// - All registers are renamed to "reg"
949/// - All nodes are renamed to "node"
950/// - All memories are renamed to "mem"
951/// - All verification messages and labels are dropped
952///
954 uint64_t match(Operation *op) override {
955 // Only match operations with names.
956 return isa<firrtl::WireOp, firrtl::RegOp, firrtl::RegResetOp,
957 firrtl::NodeOp, firrtl::MemOp, chirrtl::CombMemOp,
958 chirrtl::SeqMemOp, firrtl::AssertOp, firrtl::AssumeOp,
959 firrtl::CoverOp>(op);
960 }
961 LogicalResult rewrite(Operation *op) override {
962 TypeSwitch<Operation *, void>(op)
963 .Case<firrtl::WireOp>([](auto op) { op.setName("wire"); })
964 .Case<firrtl::RegOp, firrtl::RegResetOp>(
965 [](auto op) { op.setName("reg"); })
966 .Case<firrtl::NodeOp>([](auto op) { op.setName("node"); })
967 .Case<firrtl::MemOp, chirrtl::CombMemOp, chirrtl::SeqMemOp>(
968 [](auto op) { op.setName("mem"); })
969 .Case<firrtl::AssertOp, firrtl::AssumeOp, firrtl::CoverOp>([](auto op) {
970 op->setAttr("message", StringAttr::get(op.getContext(), ""));
971 op->setAttr("name", StringAttr::get(op.getContext(), ""));
972 });
973 return success();
974 }
975
976 std::string getName() const override {
977 return "module-internal-name-sanitizer";
978 }
979
980 bool acceptSizeIncrease() const override { return true; }
981
982 bool isOneShot() const override { return true; }
983};
984
985/// Psuedo-reduction that sanitizes module, instance, and port names. This
986/// makes the following changes:
987///
988/// - All modules are given metasyntactic names ("Foo", "Bar", etc.)
989/// - All instances are renamed to match the new module name
990/// - All module ports are renamed in the following way:
991/// - All clocks are reanemd to "clk"
992/// - All resets are renamed to "rst"
993/// - All references are renamed to "ref"
994/// - Anything else is renamed to "port"
995///
996struct ModuleNameSanitizer : OpReduction<firrtl::CircuitOp> {
997
998 const char *names[48] = {
999 "Foo", "Bar", "Baz", "Qux", "Quux", "Quuux", "Quuuux",
1000 "Quz", "Corge", "Grault", "Bazola", "Ztesch", "Thud", "Grunt",
1001 "Bletch", "Fum", "Fred", "Jim", "Sheila", "Barney", "Flarp",
1002 "Zxc", "Spqr", "Wombat", "Shme", "Bongo", "Spam", "Eggs",
1003 "Snork", "Zot", "Blarg", "Wibble", "Toto", "Titi", "Tata",
1004 "Tutu", "Pippo", "Pluto", "Paperino", "Aap", "Noot", "Mies",
1005 "Oogle", "Foogle", "Boogle", "Zork", "Gork", "Bork"};
1006
1007 size_t nameIndex = 0;
1008
1009 const char *getName() {
1010 if (nameIndex >= 48)
1011 nameIndex = 0;
1012 return names[nameIndex++];
1013 };
1014
1015 size_t portNameIndex = 0;
1016
1018 if (portNameIndex >= 26)
1019 portNameIndex = 0;
1020 return 'a' + portNameIndex++;
1021 }
1022
1023 void beforeReduction(mlir::ModuleOp op) override { nameIndex = 0; }
1024
1025 LogicalResult rewrite(firrtl::CircuitOp circuitOp) override {
1026
1027 firrtl::InstanceGraph iGraph(circuitOp);
1028
1029 auto *circuitName = getName();
1030 iGraph.getTopLevelModule().setName(circuitName);
1031 circuitOp.setName(circuitName);
1032
1033 for (auto *node : iGraph) {
1034 auto module = node->getModule<firrtl::FModuleLike>();
1035
1036 bool shouldReplacePorts = false;
1037 SmallVector<Attribute> newNames;
1038 if (auto fmodule = dyn_cast<firrtl::FModuleOp>(*module)) {
1039 portNameIndex = 0;
1040 // TODO: The namespace should be unnecessary. However, some FIRRTL
1041 // passes expect that port names are unique.
1043 auto oldPorts = fmodule.getPorts();
1044 shouldReplacePorts = !oldPorts.empty();
1045 for (unsigned i = 0, e = fmodule.getNumPorts(); i != e; ++i) {
1046 auto port = oldPorts[i];
1047 auto newName = firrtl::FIRRTLTypeSwitch<Type, StringRef>(port.type)
1048 .Case<firrtl::ClockType>(
1049 [&](auto a) { return ns.newName("clk"); })
1050 .Case<firrtl::ResetType, firrtl::AsyncResetType>(
1051 [&](auto a) { return ns.newName("rst"); })
1052 .Case<firrtl::RefType>(
1053 [&](auto a) { return ns.newName("ref"); })
1054 .Default([&](auto a) {
1055 return ns.newName(Twine(getPortName()));
1056 });
1057 newNames.push_back(StringAttr::get(circuitOp.getContext(), newName));
1058 }
1059 fmodule->setAttr("portNames",
1060 ArrayAttr::get(fmodule.getContext(), newNames));
1061 }
1062
1063 if (module == iGraph.getTopLevelModule())
1064 continue;
1065 auto newName = StringAttr::get(circuitOp.getContext(), getName());
1066 module.setName(newName);
1067 for (auto *use : node->uses()) {
1068 auto instanceOp = dyn_cast<firrtl::InstanceOp>(*use->getInstance());
1069 instanceOp.setModuleName(newName);
1070 instanceOp.setName(newName);
1071 if (shouldReplacePorts)
1072 instanceOp.setPortNamesAttr(
1073 ArrayAttr::get(circuitOp.getContext(), newNames));
1074 }
1075 }
1076
1077 circuitOp->dump();
1078
1079 return success();
1080 }
1081
1082 std::string getName() const override { return "module-name-sanitizer"; }
1083
1084 bool acceptSizeIncrease() const override { return true; }
1085
1086 bool isOneShot() const override { return true; }
1087};
1088
1089//===----------------------------------------------------------------------===//
1090// Reduction Registration
1091//===----------------------------------------------------------------------===//
1092
1095 // Gather a list of reduction patterns that we should try. Ideally these are
1096 // assigned reasonable benefit indicators (higher benefit patterns are
1097 // prioritized). For example, things that can knock out entire modules while
1098 // being cheap should be tried first (and thus have higher benefit), before
1099 // trying to tweak operands of individual arithmetic ops.
1100 patterns.add<PassReduction, 30>(
1101 getContext(),
1102 firrtl::createDropName({/*preserveMode=*/PreserveValues::None}), false,
1103 true);
1104 patterns.add<PassReduction, 29>(getContext(),
1105 firrtl::createLowerCHIRRTLPass(), true, true);
1106 patterns.add<PassReduction, 28>(getContext(), firrtl::createInferWidths(),
1107 true, true);
1108 patterns.add<PassReduction, 27>(getContext(), firrtl::createInferResets(),
1109 true, true);
1111 patterns.add<InstanceStubber, 25>();
1112 patterns.add<MemoryStubber, 24>();
1113 patterns.add<EagerInliner, 23>();
1114 patterns.add<PassReduction, 22>(getContext(),
1115 firrtl::createLowerFIRRTLTypes(), true, true);
1116 patterns.add<PassReduction, 21>(getContext(), firrtl::createExpandWhens(),
1117 true, true);
1118 patterns.add<PassReduction, 20>(getContext(), firrtl::createInliner());
1119 patterns.add<PassReduction, 18>(getContext(), firrtl::createIMConstProp());
1120 patterns.add<PassReduction, 17>(
1121 getContext(),
1122 firrtl::createRemoveUnusedPorts({/*ignoreDontTouch=*/true}));
1123 patterns.add<NodeSymbolRemover, 15>();
1124 patterns.add<ConnectForwarder, 14>();
1125 patterns.add<ConnectInvalidator, 13>();
1126 patterns.add<FIRRTLConstantifier, 12>();
1130 patterns.add<DetachSubaccesses, 7>();
1131 patterns.add<AnnotationRemover, 6>();
1132 patterns.add<RootPortPruner, 5>();
1138 patterns.add<ModuleNameSanitizer, 0>();
1139}
1140
1142 mlir::DialectRegistry &registry) {
1143 registry.addExtension(+[](MLIRContext *ctx, FIRRTLDialect *dialect) {
1144 dialect->addInterfaces<FIRRTLReducePatternDialectInterface>();
1145 });
1146}
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:87
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:52
void registerReducePatternDialectInterface(mlir::DialectRegistry &registry)
Register the FIRRTL Reduction pattern dialect interface to the given registry.
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:123
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...
std::unique_ptr< SymbolTableCollection > tables
SymbolUserMap & getSymbolUserMap(Operation *op)
SymbolUserMap & getNearestSymbolUserMap(Operation *op)
SymbolTable & getNearestSymbolTable(Operation *op)
SmallDenseMap< Operation *, SymbolUserMap, 2 > userMaps
SymbolTable & getSymbolTable(Operation *op)