CIRCT  20.0.0git
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 
22 namespace circt {
23 namespace firrtl {
24 #define GEN_PASS_DEF_REGISTEROPTIMIZER
25 #include "circt/Dialect/FIRRTL/Passes.h.inc"
26 } // namespace firrtl
27 } // namespace circt
28 
29 using namespace circt;
30 using namespace firrtl;
31 
32 // Instantiated for RegOp and RegResetOp
33 template <typename T>
34 static bool canErase(T op) {
35  return !(hasDontTouch(op.getResult()) || op.isForceable() ||
36  (op.getAnnotationsAttr() && !op.getAnnotationsAttr().empty()));
37 }
38 
39 namespace {
40 
41 //===----------------------------------------------------------------------===//
42 // Pass Infrastructure
43 //===----------------------------------------------------------------------===//
44 
45 struct 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 
56 void 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 
111 void 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 
141 void 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 }
160 
161 std::unique_ptr<mlir::Pass> circt::firrtl::createRegisterOptimizerPass() {
162  return std::make_unique<RegisterOptimizerPass>();
163 }
static Block * getBodyBlock(FModuleLike mod)
static bool canErase(T op)
bool isConstant(Operation *op)
Return true if the specified operation has a constant value.
Definition: FIRRTLOps.cpp:4588
bool hasDontTouch(Value value)
Check whether a block argument ("port") or the operation defining a value has a DontTouch annotation,...
Definition: FIRRTLOps.cpp:317
MatchingConnectOp getSingleConnectUserOf(Value value)
Scan all the uses of the specified value, checking to see if there is exactly one connect that has th...
std::unique_ptr< mlir::Pass > createRegisterOptimizerPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
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
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition: seq.py:21