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 "mlir/Pass/Pass.h"
16 
18 #include "circt/Support/Debug.h"
19 #include "mlir/IR/Dominance.h"
20 #include "mlir/IR/ImplicitLocOpBuilder.h"
21 #include "llvm/Support/Debug.h"
22 
23 #define DEBUG_TYPE "firrtl-register-optimizer"
24 
25 namespace circt {
26 namespace firrtl {
27 #define GEN_PASS_DEF_REGISTEROPTIMIZER
28 #include "circt/Dialect/FIRRTL/Passes.h.inc"
29 } // namespace firrtl
30 } // namespace circt
31 
32 using namespace circt;
33 using namespace firrtl;
34 
35 // Instantiated for RegOp and RegResetOp
36 template <typename T>
37 static bool canErase(T op) {
38  return !(hasDontTouch(op.getResult()) || op.isForceable() ||
39  (op.getAnnotationsAttr() && !op.getAnnotationsAttr().empty()));
40 }
41 
42 namespace {
43 
44 //===----------------------------------------------------------------------===//
45 // Pass Infrastructure
46 //===----------------------------------------------------------------------===//
47 
48 struct RegisterOptimizerPass
49  : public circt::firrtl::impl::RegisterOptimizerBase<RegisterOptimizerPass> {
50  void runOnOperation() override;
51  void checkRegReset(mlir::DominanceInfo &dom,
52  SmallVector<Operation *> &toErase, RegResetOp reg);
53  void checkReg(mlir::DominanceInfo &dom, SmallVector<Operation *> &toErase,
54  RegOp reg);
55 };
56 
57 } // namespace
58 
59 void RegisterOptimizerPass::checkReg(mlir::DominanceInfo &dom,
60  SmallVector<Operation *> &toErase,
61  RegOp reg) {
62  if (!canErase(reg))
63  return;
64  auto con = getSingleConnectUserOf(reg.getResult());
65  if (!con)
66  return;
67 
68  // Register is only written by itself, replace with invalid.
69  if (con.getSrc() == reg.getResult()) {
70  auto inv = OpBuilder(reg).create<InvalidValueOp>(reg.getLoc(),
71  reg.getResult().getType());
72  reg.getResult().replaceAllUsesWith(inv.getResult());
73  toErase.push_back(reg);
74  toErase.push_back(con);
75  return;
76  }
77  // Register is only written by a constant
78  if (isConstant(con.getSrc())) {
79  // constant may not dominate the register. But it might be the next
80  // operation, so we can't just move it. Straight constants can be
81  // rematerialized. Derived constants are piped through wires.
82 
83  if (auto cst = con.getSrc().getDefiningOp<ConstantOp>()) {
84  // Simple constants we can move safely
85  auto *fmodb = con->getParentOfType<FModuleOp>().getBodyBlock();
86  cst->moveBefore(fmodb, fmodb->begin());
87  reg.getResult().replaceAllUsesWith(cst.getResult());
88  toErase.push_back(con);
89  } else {
90  bool dominatesAll = true;
91  for (auto *use : reg->getUsers()) {
92  if (use == con)
93  continue;
94  if (!dom.dominates(con.getSrc(), use)) {
95  dominatesAll = false;
96  break;
97  }
98  }
99  if (dominatesAll) {
100  // Dominance is fine, just replace the op.
101  reg.getResult().replaceAllUsesWith(con.getSrc());
102  toErase.push_back(con);
103  } else {
104  auto bounce = OpBuilder(reg).create<WireOp>(reg.getLoc(),
105  reg.getResult().getType());
106  reg.replaceAllUsesWith(bounce);
107  }
108  }
109  toErase.push_back(reg);
110  return;
111  }
112 }
113 
114 void RegisterOptimizerPass::checkRegReset(mlir::DominanceInfo &dom,
115  SmallVector<Operation *> &toErase,
116  RegResetOp reg) {
117  if (!canErase(reg))
118  return;
119  auto con = getSingleConnectUserOf(reg.getResult());
120  if (!con)
121  return;
122 
123  // Register is only written by itself, and reset with a constant.
124  if (reg.getResetValue().getType() == reg.getResult().getType()) {
125  if (con.getSrc() == reg.getResult() && isConstant(reg.getResetValue())) {
126  // constant obviously dominates the register.
127  reg.getResult().replaceAllUsesWith(reg.getResetValue());
128  toErase.push_back(reg);
129  toErase.push_back(con);
130  return;
131  }
132  // Register is only written by a constant, and reset with the same constant.
133  if (con.getSrc() == reg.getResetValue() &&
134  isConstant(reg.getResetValue())) {
135  // constant obviously dominates the register.
136  reg.getResult().replaceAllUsesWith(reg.getResetValue());
137  toErase.push_back(reg);
138  toErase.push_back(con);
139  return;
140  }
141  }
142 }
143 
144 void RegisterOptimizerPass::runOnOperation() {
145  auto mod = getOperation();
146  LLVM_DEBUG(debugPassHeader(this) << "\n";);
147 
148  SmallVector<Operation *> toErase;
149  mlir::DominanceInfo dom(mod);
150 
151  for (auto &op : *mod.getBodyBlock()) {
152  if (auto reg = dyn_cast<RegResetOp>(&op))
153  checkRegReset(dom, toErase, reg);
154  else if (auto reg = dyn_cast<RegOp>(&op))
155  checkReg(dom, toErase, reg);
156  }
157  for (auto *op : toErase)
158  op->erase();
159 
160  if (!toErase.empty())
161  return markAllAnalysesPreserved();
162 }
163 
164 std::unique_ptr<mlir::Pass> circt::firrtl::createRegisterOptimizerPass() {
165  return std::make_unique<RegisterOptimizerPass>();
166 }
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:4556
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