CIRCT 22.0.0git
Loading...
Searching...
No Matches
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 builder = OpBuilder(reg);
68 auto inv = InvalidValueOp::create(builder, reg.getLoc(),
69 reg.getResult().getType());
70 reg.getResult().replaceAllUsesWith(inv.getResult());
71 toErase.push_back(reg);
72 toErase.push_back(con);
73 return;
74 }
75 // Register is only written by a constant
76 if (isConstant(con.getSrc())) {
77 // constant may not dominate the register. But it might be the next
78 // operation, so we can't just move it. Straight constants can be
79 // rematerialized. Derived constants are piped through wires.
80
81 if (auto cst = con.getSrc().getDefiningOp<ConstantOp>()) {
82 // Simple constants we can move safely
83 auto *fmodb = con->getParentOfType<FModuleOp>().getBodyBlock();
84 cst->moveBefore(fmodb, fmodb->begin());
85 reg.getResult().replaceAllUsesWith(cst.getResult());
86 toErase.push_back(con);
87 } else {
88 bool dominatesAll = true;
89 for (auto *use : reg->getUsers()) {
90 if (use == con)
91 continue;
92 if (!dom.dominates(con.getSrc(), use)) {
93 dominatesAll = false;
94 break;
95 }
96 }
97 if (dominatesAll) {
98 // Dominance is fine, just replace the op.
99 reg.getResult().replaceAllUsesWith(con.getSrc());
100 toErase.push_back(con);
101 } else {
102 auto builder = OpBuilder(reg);
103 auto bounce =
104 WireOp::create(builder, reg.getLoc(), reg.getResult().getType());
105 reg.replaceAllUsesWith(bounce);
106 }
107 }
108 toErase.push_back(reg);
109 return;
110 }
111}
112
113void RegisterOptimizerPass::checkRegReset(mlir::DominanceInfo &dom,
114 SmallVector<Operation *> &toErase,
115 RegResetOp reg) {
116 if (!canErase(reg))
117 return;
118 auto con = getSingleConnectUserOf(reg.getResult());
119 if (!con)
120 return;
121
122 // Register is only written by itself, and reset with a constant.
123 if (reg.getResetValue().getType() == reg.getResult().getType()) {
124 if (con.getSrc() == reg.getResult() && isConstant(reg.getResetValue())) {
125 // constant obviously dominates the register.
126 reg.getResult().replaceAllUsesWith(reg.getResetValue());
127 toErase.push_back(reg);
128 toErase.push_back(con);
129 return;
130 }
131 // Register is only written by a constant, and reset with the same constant.
132 if (con.getSrc() == reg.getResetValue() &&
133 isConstant(reg.getResetValue())) {
134 // constant obviously dominates the register.
135 reg.getResult().replaceAllUsesWith(reg.getResetValue());
136 toErase.push_back(reg);
137 toErase.push_back(con);
138 return;
139 }
140 }
141}
142
143void RegisterOptimizerPass::runOnOperation() {
144 auto mod = getOperation();
145 LLVM_DEBUG(debugPassHeader(this) << "\n";);
146
147 SmallVector<Operation *> toErase;
148 mlir::DominanceInfo dom(mod);
149
150 for (auto &op : *mod.getBodyBlock()) {
151 if (auto reg = dyn_cast<RegResetOp>(&op))
152 checkRegReset(dom, toErase, reg);
153 else if (auto reg = dyn_cast<RegOp>(&op))
154 checkReg(dom, toErase, reg);
155 }
156 for (auto *op : toErase)
157 op->erase();
158
159 if (!toErase.empty())
160 return markAllAnalysesPreserved();
161}
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, unsigned 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