CIRCT  19.0.0git
DCMaterialization.cpp
Go to the documentation of this file.
1 //===- DCMaterialization.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"
14 #include "circt/Dialect/DC/DCOps.h"
16 
17 using namespace circt;
18 using namespace dc;
19 using namespace mlir;
20 
21 static bool isDCTyped(Value v) {
22  return isa<dc::TokenType, dc::ValueType>(v.getType());
23 }
24 
25 static void replaceFirstUse(Operation *op, Value oldVal, Value newVal) {
26  for (int i = 0, e = op->getNumOperands(); i < e; ++i)
27  if (op->getOperand(i) == oldVal) {
28  op->setOperand(i, newVal);
29  break;
30  }
31 }
32 
33 // Adds a sink to the provided token or value-typed Value `v`.
34 static void insertSink(Value v, OpBuilder &rewriter) {
35  rewriter.setInsertionPointAfterValue(v);
36  if (isa<ValueType>(v.getType())) {
37  // Unpack before sinking
38  v = rewriter.create<UnpackOp>(v.getLoc(), v).getToken();
39  }
40 
41  rewriter.create<SinkOp>(v.getLoc(), v);
42 }
43 
44 // Adds a fork of the provided token or value-typed Value `result`.
45 static void insertFork(Value result, OpBuilder &rewriter) {
46  rewriter.setInsertionPointAfterValue(result);
47  // Get successor operations
48  std::vector<Operation *> opsToProcess;
49  for (auto &u : result.getUses())
50  opsToProcess.push_back(u.getOwner());
51 
52  bool isValue = isa<ValueType>(result.getType());
53  Value token = result;
54  Value value;
55  if (isValue) {
56  auto unpack = rewriter.create<UnpackOp>(result.getLoc(), result);
57  token = unpack.getToken();
58  value = unpack.getOutput();
59  }
60 
61  // Insert fork after op
62  auto forkSize = opsToProcess.size();
63  auto newFork = rewriter.create<ForkOp>(token.getLoc(), token, forkSize);
64 
65  // Modify operands of successor
66  // opsToProcess may have multiple instances of same operand
67  // Replace uses one by one to assign different fork outputs to them
68  for (auto [op, forkOutput] : llvm::zip(opsToProcess, newFork->getResults())) {
69  Value forkRes = forkOutput;
70  if (isValue)
71  forkRes =
72  rewriter.create<PackOp>(forkRes.getLoc(), forkRes, value).getOutput();
73  replaceFirstUse(op, result, forkRes);
74  }
75 }
76 
77 // Insert Fork Operation for every operation and function argument with more
78 // than one successor.
79 static LogicalResult addForkOps(Block &block, OpBuilder &rewriter) {
80  // Materialization adds operations _after_ their definition, so we can't use
81  // llvm::make_early_inc_range. Copy over all of the ops to process.
82  llvm::SmallVector<Operation *> opsToProcess;
83  for (auto &op : block.getOperations())
84  opsToProcess.push_back(&op);
85 
86  for (Operation *op : opsToProcess) {
87  // Ignore terminators.
88  if (!op->hasTrait<OpTrait::IsTerminator>()) {
89  for (auto result : op->getResults()) {
90  if (!isDCTyped(result))
91  continue;
92  // If there is a result, it is used more than once, and it is a DC
93  // type, fork it!
94  if (!result.use_empty() && !result.hasOneUse())
95  insertFork(result, rewriter);
96  }
97  }
98  }
99 
100  for (auto barg : block.getArguments())
101  if (!barg.use_empty() && !barg.hasOneUse())
102  insertFork(barg, rewriter);
103 
104  return success();
105 }
106 
107 // Create sink for every unused result
108 static LogicalResult addSinkOps(Block &block, OpBuilder &rewriter) {
109  for (auto arg : block.getArguments()) {
110  if (isDCTyped(arg) && arg.use_empty())
111  insertSink(arg, rewriter);
112  }
113 
114  // Materialization adds operations _after_ their definition, so we can't use
115  // llvm::make_early_inc_range. Copy over all of the ops to process.
116  llvm::SmallVector<Operation *> opsToProcess;
117  for (auto &op : block.getOperations())
118  opsToProcess.push_back(&op);
119 
120  for (Operation *op : opsToProcess) {
121  if (op->getNumResults() == 0)
122  continue;
123 
124  for (auto result : op->getResults()) {
125  if (isDCTyped(result) && result.use_empty())
126  insertSink(result, rewriter);
127  }
128  }
129 
130  return success();
131 }
132 
133 namespace {
134 struct DCMaterializeForksSinksPass
135  : public DCMaterializeForksSinksBase<DCMaterializeForksSinksPass> {
136  void runOnOperation() override {
137  auto *op = getOperation();
138  OpBuilder builder(op);
139 
140  auto walkRes = op->walk([&](mlir::Block *block) {
141  if (addForkOps(*block, builder).failed() ||
142  addSinkOps(*block, builder).failed())
143  return WalkResult::interrupt();
144 
145  return WalkResult::advance();
146  });
147 
148  if (walkRes.wasInterrupted())
149  return signalPassFailure();
150  };
151 };
152 
153 struct DCDematerializeForksSinksPass
154  : public DCDematerializeForksSinksBase<DCDematerializeForksSinksPass> {
155  void runOnOperation() override {
156  auto *op = getOperation();
157  op->walk([&](dc::SinkOp sinkOp) { sinkOp.erase(); });
158  op->walk([&](dc::ForkOp forkOp) {
159  for (auto res : forkOp->getResults())
160  res.replaceAllUsesWith(forkOp.getOperand());
161  forkOp.erase();
162  });
163  };
164 };
165 
166 } // namespace
167 
168 std::unique_ptr<mlir::Pass> circt::dc::createDCMaterializeForksSinksPass() {
169  return std::make_unique<DCMaterializeForksSinksPass>();
170 }
171 
172 std::unique_ptr<mlir::Pass> circt::dc::createDCDematerializeForksSinksPass() {
173  return std::make_unique<DCDematerializeForksSinksPass>();
174 }
static LogicalResult addSinkOps(Block &block, OpBuilder &rewriter)
static void insertSink(Value v, OpBuilder &rewriter)
static void insertFork(Value result, OpBuilder &rewriter)
static bool isDCTyped(Value v)
static LogicalResult addForkOps(Block &block, OpBuilder &rewriter)
static void replaceFirstUse(Operation *op, Value oldVal, Value newVal)
Builder builder
std::unique_ptr< mlir::Pass > createDCDematerializeForksSinksPass()
std::unique_ptr< mlir::Pass > createDCMaterializeForksSinksPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21