CIRCT  18.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"
15 #include "mlir/IR/Dominance.h"
16 #include "mlir/IR/ImplicitLocOpBuilder.h"
17 #include "llvm/Support/Debug.h"
18 
19 #define DEBUG_TYPE "firrtl-register-optimizer"
20 
21 using namespace circt;
22 using namespace firrtl;
23 
24 // Instantiated for RegOp and RegResetOp
25 template <typename T>
26 static bool canErase(T op) {
27  return !(hasDontTouch(op.getResult()) || op.isForceable() ||
28  (op.getAnnotationsAttr() && !op.getAnnotationsAttr().empty()));
29 }
30 
31 namespace {
32 
33 //===----------------------------------------------------------------------===//
34 // Pass Infrastructure
35 //===----------------------------------------------------------------------===//
36 
37 struct RegisterOptimizerPass
38  : public RegisterOptimizerBase<RegisterOptimizerPass> {
39  void runOnOperation() override;
40  void checkRegReset(mlir::DominanceInfo &dom,
41  SmallVector<Operation *> &toErase, RegResetOp reg);
42  void checkReg(mlir::DominanceInfo &dom, SmallVector<Operation *> &toErase,
43  RegOp reg);
44 };
45 
46 } // namespace
47 
48 void RegisterOptimizerPass::checkReg(mlir::DominanceInfo &dom,
49  SmallVector<Operation *> &toErase,
50  RegOp reg) {
51  if (!canErase(reg))
52  return;
53  auto con = getSingleConnectUserOf(reg.getResult());
54  if (!con)
55  return;
56 
57  // Register is only written by itself, replace with invalid.
58  if (con.getSrc() == reg.getResult()) {
59  auto inv = OpBuilder(reg).create<InvalidValueOp>(reg.getLoc(),
60  reg.getResult().getType());
61  reg.getResult().replaceAllUsesWith(inv.getResult());
62  toErase.push_back(reg);
63  toErase.push_back(con);
64  return;
65  }
66  // Register is only written by a constant
67  if (isConstant(con.getSrc())) {
68  // constant may not dominate the register. But it might be the next
69  // operation, so we can't just move it. Straight constants can be
70  // rematerialized. Derived constants are piped through wires.
71 
72  if (auto cst = con.getSrc().getDefiningOp<ConstantOp>()) {
73  // Simple constants we can move safely
74  auto *fmodb = con->getParentOfType<FModuleOp>().getBodyBlock();
75  cst->moveBefore(fmodb, fmodb->begin());
76  reg.getResult().replaceAllUsesWith(cst.getResult());
77  toErase.push_back(con);
78  } else {
79  bool dominatesAll = true;
80  for (auto *use : reg->getUsers()) {
81  if (use == con)
82  continue;
83  if (!dom.dominates(con.getSrc(), use)) {
84  dominatesAll = false;
85  break;
86  }
87  }
88  if (dominatesAll) {
89  // Dominance is fine, just replace the op.
90  reg.getResult().replaceAllUsesWith(con.getSrc());
91  toErase.push_back(con);
92  } else {
93  auto bounce = OpBuilder(reg).create<WireOp>(reg.getLoc(),
94  reg.getResult().getType());
95  reg.replaceAllUsesWith(bounce);
96  }
97  }
98  toErase.push_back(reg);
99  return;
100  }
101 }
102 
103 void RegisterOptimizerPass::checkRegReset(mlir::DominanceInfo &dom,
104  SmallVector<Operation *> &toErase,
105  RegResetOp reg) {
106  if (!canErase(reg))
107  return;
108  auto con = getSingleConnectUserOf(reg.getResult());
109  if (!con)
110  return;
111 
112  // Register is only written by itself, and reset with a constant.
113  if (reg.getResetValue().getType() == reg.getResult().getType()) {
114  if (con.getSrc() == reg.getResult() && isConstant(reg.getResetValue())) {
115  // constant obviously dominates the register.
116  reg.getResult().replaceAllUsesWith(reg.getResetValue());
117  toErase.push_back(reg);
118  toErase.push_back(con);
119  return;
120  }
121  // Register is only written by a constant, and reset with the same constant.
122  if (con.getSrc() == reg.getResetValue() &&
123  isConstant(reg.getResetValue())) {
124  // constant obviously dominates the register.
125  reg.getResult().replaceAllUsesWith(reg.getResetValue());
126  toErase.push_back(reg);
127  toErase.push_back(con);
128  return;
129  }
130  }
131 }
132 
133 void RegisterOptimizerPass::runOnOperation() {
134  auto mod = getOperation();
135  LLVM_DEBUG(llvm::dbgs() << "===----- Running RegisterOptimizer "
136  "--------------------------------------===\n"
137  << "Module: '" << mod.getName() << "'\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:4070
bool hasDontTouch(Value value)
Check whether a block argument ("port") or the operation defining a value has a DontTouch annotation,...
Definition: FIRRTLOps.cpp:287
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...
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21
mlir::raw_indented_ostream & dbgs()
Definition: Utility.h:28
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition: seq.py:20