CIRCT 20.0.0git
Loading...
Searching...
No Matches
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
29namespace circt {
30namespace 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
37using namespace circt;
38using namespace handshake;
39using namespace mlir;
40using namespace mlir::affine;
41
42using BlockValues = DenseMap<Block *, std::vector<Value>>;
43
44static 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.
51static 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.
71static 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
99namespace {
100struct 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
115struct 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
137std::unique_ptr<mlir::Pass>
139 return std::make_unique<HandshakeMaterializeForksSinksPass>();
140}
141
142std::unique_ptr<mlir::Pass>
144 return std::make_unique<HandshakeDematerializeForksSinksPass>();
145}
static void insertFork(Value result, OpBuilder &rewriter)
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()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.