Loading [MathJax]/extensions/tex2jax.js
CIRCT 22.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
RegisterOptimizer.cpp
Go to the documentation of this file.
1//===- RegisterOptimizer.cpp - Register Optimizer ---------------*- C++ -*-===//
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// This pass optimized registers as allowed by historic firrtl register
9// behaviors.
10//
11//===----------------------------------------------------------------------===//
12
15#include "circt/Support/Debug.h"
16#include "mlir/IR/Dominance.h"
17#include "mlir/Pass/Pass.h"
18#include "llvm/Support/Debug.h"
19
20#define DEBUG_TYPE "firrtl-register-optimizer"
21
22namespace circt {
23namespace firrtl {
24#define GEN_PASS_DEF_REGISTEROPTIMIZER
25#include "circt/Dialect/FIRRTL/Passes.h.inc"
26} // namespace firrtl
27} // namespace circt
28
29using namespace circt;
30using namespace firrtl;
31
32// Instantiated for RegOp and RegResetOp
33template <typename T>
34static bool canErase(T op) {
35 return !(hasDontTouch(op.getResult()) || op.isForceable() ||
36 (op.getAnnotationsAttr() && !op.getAnnotationsAttr().empty()));
37}
38
39namespace {
40
41//===----------------------------------------------------------------------===//
42// Pass Infrastructure
43//===----------------------------------------------------------------------===//
44
45struct RegisterOptimizerPass
46 : public circt::firrtl::impl::RegisterOptimizerBase<RegisterOptimizerPass> {
47 void runOnOperation() override;
48 void checkRegReset(mlir::DominanceInfo &dom,
49 SmallVector<Operation *> &toErase, RegResetOp reg);
50 void checkReg(mlir::DominanceInfo &dom, SmallVector<Operation *> &toErase,
51 RegOp reg);
52};
53
54} // namespace
55
56void RegisterOptimizerPass::checkReg(mlir::DominanceInfo &dom,
57 SmallVector<Operation *> &toErase,
58 RegOp reg) {
59 if (!canErase(reg))
60 return;
61 auto con = getSingleConnectUserOf(reg.getResult());
62 if (!con)
63 return;
64
65 // Register is only written by itself, replace with invalid.
66 if (con.getSrc() == reg.getResult()) {
67 auto inv = OpBuilder(reg).create<InvalidValueOp>(reg.getLoc(),
68 reg.getResult().getType());
69 reg.getResult().replaceAllUsesWith(inv.getResult());
70 toErase.push_back(reg);
71 toErase.push_back(con);
72 return;
73 }
74 // Register is only written by a constant
75 if (isConstant(con.getSrc())) {
76 // constant may not dominate the register. But it might be the next
77 // operation, so we can't just move it. Straight constants can be
78 // rematerialized. Derived constants are piped through wires.
79
80 if (auto cst = con.getSrc().getDefiningOp<ConstantOp>()) {
81 // Simple constants we can move safely
82 auto *fmodb = con->getParentOfType<FModuleOp>().getBodyBlock();
83 cst->moveBefore(fmodb, fmodb->begin());
84 reg.getResult().replaceAllUsesWith(cst.getResult());
85 toErase.push_back(con);
86 } else {
87 bool dominatesAll = true;
88 for (auto *use : reg->getUsers()) {
89 if (use == con)
90 continue;
91 if (!dom.dominates(con.getSrc(), use)) {
92 dominatesAll = false;
93 break;
94 }
95 }
96 if (dominatesAll) {
97 // Dominance is fine, just replace the op.
98 reg.getResult().replaceAllUsesWith(con.getSrc());
99 toErase.push_back(con);
100 } else {
101 auto bounce = OpBuilder(reg).create<WireOp>(reg.getLoc(),
102 reg.getResult().getType());
103 reg.replaceAllUsesWith(bounce);
104 }
105 }
106 toErase.push_back(reg);
107 return;
108 }
109}
110
111void RegisterOptimizerPass::checkRegReset(mlir::DominanceInfo &dom,
112 SmallVector<Operation *> &toErase,
113 RegResetOp reg) {
114 if (!canErase(reg))
115 return;
116 auto con = getSingleConnectUserOf(reg.getResult());
117 if (!con)
118 return;
119
120 // Register is only written by itself, and reset with a constant.
121 if (reg.getResetValue().getType() == reg.getResult().getType()) {
122 if (con.getSrc() == reg.getResult() && isConstant(reg.getResetValue())) {
123 // constant obviously dominates the register.
124 reg.getResult().replaceAllUsesWith(reg.getResetValue());
125 toErase.push_back(reg);
126 toErase.push_back(con);
127 return;
128 }
129 // Register is only written by a constant, and reset with the same constant.
130 if (con.getSrc() == reg.getResetValue() &&
131 isConstant(reg.getResetValue())) {
132 // constant obviously dominates the register.
133 reg.getResult().replaceAllUsesWith(reg.getResetValue());
134 toErase.push_back(reg);
135 toErase.push_back(con);
136 return;
137 }
138 }
139}
140
141void RegisterOptimizerPass::runOnOperation() {
142 auto mod = getOperation();
143 LLVM_DEBUG(debugPassHeader(this) << "\n";);
144
145 SmallVector<Operation *> toErase;
146 mlir::DominanceInfo dom(mod);
147
148 for (auto &op : *mod.getBodyBlock()) {
149 if (auto reg = dyn_cast<RegResetOp>(&op))
150 checkRegReset(dom, toErase, reg);
151 else if (auto reg = dyn_cast<RegOp>(&op))
152 checkReg(dom, toErase, reg);
153 }
154 for (auto *op : toErase)
155 op->erase();
156
157 if (!toErase.empty())
158 return markAllAnalysesPreserved();
159}
static Block * getBodyBlock(FModuleLike mod)
static bool canErase(T op)
bool isConstant(Operation *op)
Return true if the specified operation has a constant value.
bool hasDontTouch(Value value)
Check whether a block argument ("port") or the operation defining a value has a DontTouch annotation,...
MatchingConnectOp getSingleConnectUserOf(Value value)
Scan all the uses of the specified value, checking to see if there is exactly one connect that has th...
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::raw_ostream & debugPassHeader(const mlir::Pass *pass, int width=80)
Write a boilerplate header for a pass to the debug stream.
Definition Debug.cpp:31
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition seq.py:21