CIRCT  19.0.0git
Materialization.cpp
Go to the documentation of this file.
1 //===- Materialization.cpp - Fork/sink materialization pass -----*- 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 //
9 // Contains the definitions of the Fork/sink materialization pass.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "PassDetails.h"
16 #include "circt/Support/LLVM.h"
17 #include "mlir/Dialect/Affine/IR/AffineOps.h"
18 #include "mlir/Dialect/Arith/IR/Arith.h"
19 #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
20 #include "mlir/Dialect/Func/IR/FuncOps.h"
21 #include "mlir/Dialect/MemRef/IR/MemRef.h"
22 #include "mlir/IR/BuiltinTypes.h"
23 #include "mlir/IR/OperationSupport.h"
24 #include "mlir/IR/PatternMatch.h"
25 #include "mlir/Support/IndentedOstream.h"
26 #include "llvm/ADT/TypeSwitch.h"
27 
28 using namespace circt;
29 using namespace handshake;
30 using namespace mlir;
31 using namespace mlir::affine;
32 
33 using BlockValues = DenseMap<Block *, std::vector<Value>>;
34 
35 static void replaceFirstUse(Operation *op, Value oldVal, Value newVal) {
36  for (int i = 0, e = op->getNumOperands(); i < e; ++i)
37  if (op->getOperand(i) == oldVal) {
38  op->setOperand(i, newVal);
39  break;
40  }
41  return;
42 }
43 
44 static void insertSink(Value val, OpBuilder &rewriter) {
45  rewriter.setInsertionPointAfterValue(val);
46  rewriter.create<SinkOp>(val.getLoc(), val);
47 }
48 
49 namespace circt {
50 namespace handshake {
51 
52 void insertFork(Value result, bool isLazy, OpBuilder &rewriter) {
53  // Get successor operations
54  std::vector<Operation *> opsToProcess;
55  for (auto &u : result.getUses())
56  opsToProcess.push_back(u.getOwner());
57 
58  // Insert fork after op
59  rewriter.setInsertionPointAfterValue(result);
60  auto forkSize = opsToProcess.size();
61  Operation *newOp;
62  if (isLazy)
63  newOp = rewriter.create<LazyForkOp>(result.getLoc(), result, forkSize);
64  else
65  newOp = rewriter.create<ForkOp>(result.getLoc(), result, forkSize);
66 
67  // Modify operands of successor
68  // opsToProcess may have multiple instances of same operand
69  // Replace uses one by one to assign different fork outputs to them
70  for (int i = 0, e = forkSize; i < e; ++i)
71  replaceFirstUse(opsToProcess[i], result, newOp->getResult(i));
72 }
73 
74 // Insert Fork Operation for every operation and function argument with more
75 // than one successor.
76 LogicalResult addForkOps(Region &r, OpBuilder &rewriter) {
77  for (Operation &op : r.getOps()) {
78  // Ignore terminators, and don't add Forks to Forks.
79  if (op.getNumSuccessors() == 0 && !isa<ForkOp>(op)) {
80  for (auto result : op.getResults()) {
81  // If there is a result and it is used more than once
82  if (!result.use_empty() && !result.hasOneUse())
83  insertFork(result, false, rewriter);
84  }
85  }
86  }
87 
88  for (auto barg : r.front().getArguments())
89  if (!barg.use_empty() && !barg.hasOneUse())
90  insertFork(barg, false, rewriter);
91 
92  return success();
93 }
94 
95 // Create sink for every unused result
96 LogicalResult addSinkOps(Region &r, OpBuilder &rewriter) {
97  BlockValues liveOuts;
98 
99  for (Block &block : r) {
100  for (auto arg : block.getArguments()) {
101  if (arg.use_empty())
102  insertSink(arg, rewriter);
103  }
104  for (Operation &op : block) {
105  // Do not add sinks for unused MLIR operations which the rewriter will
106  // later remove We have already replaced these ops with their handshake
107  // equivalents
108  // TODO: should we use other indicator for op that has been erased?
109  if (isa<mlir::cf::CondBranchOp, mlir::cf::BranchOp, memref::LoadOp,
110  AffineReadOpInterface, AffineForOp>(op))
111  continue;
112 
113  if (op.getNumResults() == 0)
114  continue;
115 
116  for (auto result : op.getResults())
117  if (result.use_empty())
118  insertSink(result, rewriter);
119  }
120  }
121  return success();
122 }
123 
124 } // namespace handshake
125 } // namespace circt
126 
127 namespace {
128 struct HandshakeMaterializeForksSinksPass
129  : public HandshakeMaterializeForksSinksBase<
130  HandshakeMaterializeForksSinksPass> {
131  void runOnOperation() override {
132  handshake::FuncOp op = getOperation();
133  if (op.isExternal())
134  return;
135  OpBuilder builder(op);
136  if (addForkOps(op.getRegion(), builder).failed() ||
137  addSinkOps(op.getRegion(), builder).failed() ||
138  verifyAllValuesHasOneUse(op).failed())
139  return signalPassFailure();
140  };
141 };
142 
143 struct HandshakeDematerializeForksSinksPass
144  : public HandshakeDematerializeForksSinksBase<
145  HandshakeDematerializeForksSinksPass> {
146  void runOnOperation() override {
147  handshake::FuncOp op = getOperation();
148  if (op.isExternal())
149  return;
150  for (auto sinkOp :
151  llvm::make_early_inc_range(op.getOps<handshake::SinkOp>()))
152  sinkOp.erase();
153 
154  for (auto forkOp :
155  llvm::make_early_inc_range(op.getOps<handshake::ForkOp>())) {
156  for (auto res : forkOp->getResults())
157  res.replaceAllUsesWith(forkOp.getOperand());
158  forkOp.erase();
159  }
160  };
161 };
162 
163 } // namespace
164 
165 std::unique_ptr<mlir::Pass>
167  return std::make_unique<HandshakeMaterializeForksSinksPass>();
168 }
169 
170 std::unique_ptr<mlir::Pass>
172  return std::make_unique<HandshakeDematerializeForksSinksPass>();
173 }
static void insertSink(Value val, OpBuilder &rewriter)
static void replaceFirstUse(Operation *op, Value oldVal, Value newVal)
DenseMap< Block *, std::vector< Value > > BlockValues
Builder builder
std::unique_ptr< mlir::Pass > createHandshakeMaterializeForksSinksPass()
LogicalResult addForkOps(Region &r, OpBuilder &rewriter)
LogicalResult verifyAllValuesHasOneUse(handshake::FuncOp op)
std::unique_ptr< mlir::Pass > createHandshakeDematerializeForksSinksPass()
void insertFork(Value result, bool isLazy, OpBuilder &rewriter)
LogicalResult addSinkOps(Region &r, OpBuilder &rewriter)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21