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 
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/Pass/Pass.h"
26 #include "mlir/Support/IndentedOstream.h"
27 #include "llvm/ADT/TypeSwitch.h"
28 
29 namespace circt {
30 namespace handshake {
31 #define GEN_PASS_DEF_HANDSHAKEMATERIALIZEFORKSSINKS
32 #define GEN_PASS_DEF_HANDSHAKEDEMATERIALIZEFORKSSINKS
33 #include "circt/Dialect/Handshake/HandshakePasses.h.inc"
34 } // namespace handshake
35 } // namespace circt
36 
37 using namespace circt;
38 using namespace handshake;
39 using namespace mlir;
40 using namespace mlir::affine;
41 
42 using BlockValues = DenseMap<Block *, std::vector<Value>>;
43 
44 static void insertSink(Value val, OpBuilder &rewriter) {
45  rewriter.setInsertionPointAfterValue(val);
46  rewriter.create<SinkOp>(val.getLoc(), val);
47 }
48 
49 /// Insert Fork Operation for every operation and function argument with more
50 /// than one successor.
51 static LogicalResult addForkOps(Region &r, OpBuilder &rewriter) {
52  for (Operation &op : r.getOps()) {
53  // Ignore terminators, and don't add Forks to Forks.
54  if (op.getNumSuccessors() == 0 && !isa<ForkOp>(op)) {
55  for (auto result : op.getResults()) {
56  // If there is a result and it is used more than once
57  if (!result.use_empty() && !result.hasOneUse())
58  insertFork(result, false, rewriter);
59  }
60  }
61  }
62 
63  for (auto barg : r.front().getArguments())
64  if (!barg.use_empty() && !barg.hasOneUse())
65  insertFork(barg, false, rewriter);
66 
67  return success();
68 }
69 
70 /// Adds sink operations to any unused value in r.
71 static LogicalResult addSinkOps(Region &r, OpBuilder &rewriter) {
72  BlockValues liveOuts;
73 
74  for (Block &block : r) {
75  for (auto arg : block.getArguments()) {
76  if (arg.use_empty())
77  insertSink(arg, rewriter);
78  }
79  for (Operation &op : block) {
80  // Do not add sinks for unused MLIR operations which the rewriter will
81  // later remove We have already replaced these ops with their handshake
82  // equivalents
83  // TODO: should we use other indicator for op that has been erased?
84  if (isa<mlir::cf::CondBranchOp, mlir::cf::BranchOp, memref::LoadOp,
85  AffineReadOpInterface, AffineForOp>(op))
86  continue;
87 
88  if (op.getNumResults() == 0)
89  continue;
90 
91  for (auto result : op.getResults())
92  if (result.use_empty())
93  insertSink(result, rewriter);
94  }
95  }
96  return success();
97 }
98 
99 namespace {
100 struct HandshakeMaterializeForksSinksPass
101  : public circt::handshake::impl::HandshakeMaterializeForksSinksBase<
102  HandshakeMaterializeForksSinksPass> {
103  void runOnOperation() override {
104  handshake::FuncOp op = getOperation();
105  if (op.isExternal())
106  return;
107  OpBuilder builder(op);
108  if (addForkOps(op.getRegion(), builder).failed() ||
109  addSinkOps(op.getRegion(), builder).failed() ||
110  verifyAllValuesHasOneUse(op).failed())
111  return signalPassFailure();
112  };
113 };
114 
115 struct HandshakeDematerializeForksSinksPass
116  : public circt::handshake::impl::HandshakeDematerializeForksSinksBase<
117  HandshakeDematerializeForksSinksPass> {
118  void runOnOperation() override {
119  handshake::FuncOp op = getOperation();
120  if (op.isExternal())
121  return;
122  for (auto sinkOp :
123  llvm::make_early_inc_range(op.getOps<handshake::SinkOp>()))
124  sinkOp.erase();
125 
126  for (auto forkOp :
127  llvm::make_early_inc_range(op.getOps<handshake::ForkOp>())) {
128  for (auto res : forkOp->getResults())
129  res.replaceAllUsesWith(forkOp.getOperand());
130  forkOp.erase();
131  }
132  };
133 };
134 
135 } // namespace
136 
137 std::unique_ptr<mlir::Pass>
139  return std::make_unique<HandshakeMaterializeForksSinksPass>();
140 }
141 
142 std::unique_ptr<mlir::Pass>
144  return std::make_unique<HandshakeDematerializeForksSinksPass>();
145 }
static void insertSink(Value val, OpBuilder &rewriter)
static LogicalResult addSinkOps(Region &r, OpBuilder &rewriter)
Adds sink operations to any unused value in r.
static LogicalResult addForkOps(Region &r, OpBuilder &rewriter)
Insert Fork Operation for every operation and function argument with more than one successor.
DenseMap< Block *, std::vector< Value > > BlockValues
std::unique_ptr< mlir::Pass > createHandshakeMaterializeForksSinksPass()
LogicalResult verifyAllValuesHasOneUse(handshake::FuncOp op)
Checks all block arguments and values within op to ensure that all values have exactly one use.
std::unique_ptr< mlir::Pass > createHandshakeDematerializeForksSinksPass()
void insertFork(Value result, bool isLazy, OpBuilder &rewriter)
Adds fork operations to any value with multiple uses in r.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21