CIRCT  18.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 v.getType().isa<dc::TokenType, dc::ValueType>();
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 (v.getType().isa<ValueType>()) {
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 = result.getType().isa<ValueType>();
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 funcOp = getOperation();
138  if (funcOp.isExternal())
139  return;
140  OpBuilder builder(funcOp);
141 
142  auto walkRes = funcOp.walk([&](mlir::Block *block) {
143  if (addForkOps(*block, builder).failed() ||
144  addSinkOps(*block, builder).failed())
145  return WalkResult::interrupt();
146 
147  return WalkResult::advance();
148  });
149 
150  if (walkRes.wasInterrupted())
151  return signalPassFailure();
152  };
153 };
154 
155 struct DCDematerializeForksSinksPass
156  : public DCDematerializeForksSinksBase<DCDematerializeForksSinksPass> {
157  void runOnOperation() override {
158  auto funcOp = getOperation();
159 
160  if (funcOp.isExternal())
161  return;
162  funcOp.walk([&](dc::SinkOp sinkOp) { sinkOp.erase(); });
163  funcOp.walk([&](dc::ForkOp forkOp) {
164  for (auto res : forkOp->getResults())
165  res.replaceAllUsesWith(forkOp.getOperand());
166  forkOp.erase();
167  });
168  };
169 };
170 
171 } // namespace
172 
173 std::unique_ptr<mlir::Pass> circt::dc::createDCMaterializeForksSinksPass() {
174  return std::make_unique<DCMaterializeForksSinksPass>();
175 }
176 
177 std::unique_ptr<mlir::Pass> circt::dc::createDCDematerializeForksSinksPass() {
178  return std::make_unique<DCDematerializeForksSinksPass>();
179 }
lowerAnnotationsNoRefTypePorts FirtoolPreserveValuesMode value
Definition: Firtool.cpp:95
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()
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21