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