CIRCT  19.0.0git
CFToHandshake.h
Go to the documentation of this file.
1 //===- CFToHandshake.h ------------------------------------------*- 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 // This file declares passes which together will lower the Standard dialect to
10 // Handshake dialect.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef CIRCT_CONVERSION_CFTOHANDSHAKE_H_
15 #define CIRCT_CONVERSION_CFTOHANDSHAKE_H_
16 
20 #include "mlir/Transforms/DialectConversion.h"
21 
22 #include <memory>
23 
24 namespace mlir {
25 class ModuleOp;
26 template <typename T>
28 } // namespace mlir
29 
30 namespace circt {
31 
32 #define GEN_PASS_DECL_CFTOHANDSHAKE
33 #define GEN_PASS_DECL_HANDSHAKEREMOVEBLOCK
34 #include "circt/Conversion/Passes.h.inc"
35 
36 namespace handshake {
37 
38 // ============================================================================
39 // Partial lowering infrastructure
40 // ============================================================================
41 
43  llvm::function_ref<LogicalResult(Region &, ConversionPatternRewriter &)>;
44 // Convenience function for executing a PartialLowerRegion with a provided
45 // partial lowering function.
46 LogicalResult partiallyLowerRegion(const RegionLoweringFunc &loweringFunc,
47  MLIRContext *ctx, Region &r);
48 
49 // Holds the shared state of the different transformations required to transform
50 // Standard to Handshake operations. The transformations are expressed as member
51 // functions to simplify access to the state.
52 //
53 // This class' member functions expect a rewriter that matched on the parent
54 // operation of the encapsulated region.
56 public:
57  struct MergeOpInfo {
58  Operation *op;
59  Value val;
60  SmallVector<Backedge> dataEdges;
61  std::optional<Backedge> indexEdge{};
62  };
63 
64  using BlockValues = DenseMap<Block *, std::vector<Value>>;
65  using BlockOps = DenseMap<Block *, std::vector<MergeOpInfo>>;
66  using ValueMap = DenseMap<Value, Value>;
68  llvm::MapVector<Value, std::vector<Operation *>>;
69 
70  explicit HandshakeLowering(Region &r) : r(r) {}
71 
72  LogicalResult addMergeOps(ConversionPatternRewriter &rewriter);
73  LogicalResult addBranchOps(ConversionPatternRewriter &rewriter);
74  LogicalResult replaceCallOps(ConversionPatternRewriter &rewriter);
75 
76  template <typename TSrcTerm, typename TDstTerm>
77  LogicalResult setControlOnlyPath(ConversionPatternRewriter &rewriter,
78  Value entryCtrl) {
79  assert(isa<NoneType>(entryCtrl.getType()) &&
80  "Expected NoneType for entry control value");
81  // Creates start and end points of the control-only path
82  Block *entryBlock = &r.front();
83  setBlockEntryControl(entryBlock, entryCtrl);
84 
85  // Replace original return ops with new returns with additional control
86  // input
87  for (auto retOp : llvm::make_early_inc_range(r.getOps<TSrcTerm>())) {
88  rewriter.setInsertionPoint(retOp);
89  SmallVector<Value, 8> operands(retOp->getOperands());
90  operands.push_back(entryCtrl);
91  rewriter.replaceOpWithNewOp<TDstTerm>(retOp, operands);
92  }
93 
94  // Store the number of block arguments in each block
95  DenseMap<Block *, unsigned> numArgsPerBlock;
96  for (auto &block : r.getBlocks())
97  numArgsPerBlock[&block] = block.getNumArguments();
98 
99  // Apply SSA maximization on the newly added entry block argument to
100  // propagate it explicitly between the start-point of the control-only
101  // network and the function's terminators
102  if (failed(runSSAMaximization(rewriter, entryCtrl)))
103  return failure();
104 
105  // Identify all block arguments belonging to the control-only network
106  // (needed to later insert control merges after those values). These are the
107  // last arguments to blocks that got a new argument during SSA maximization
108  for (auto &[block, numArgs] : numArgsPerBlock)
109  if (block->getNumArguments() != numArgs)
110  setBlockEntryControl(block, block->getArguments().back());
111 
112  return success();
113  }
114 
115  LogicalResult connectConstantsToControl(ConversionPatternRewriter &rewriter,
116  bool sourceConstants);
117 
118  LogicalResult feedForwardRewriting(ConversionPatternRewriter &rewriter);
119  LogicalResult loopNetworkRewriting(ConversionPatternRewriter &rewriter);
120 
121  BlockOps insertMergeOps(ValueMap &mergePairs, BackedgeBuilder &edgeBuilder,
122  ConversionPatternRewriter &rewriter);
123 
124  // Insert appropriate type of Merge CMerge for control-only path,
125  // Merge for single-successor blocks, Mux otherwise
126  MergeOpInfo insertMerge(Block *block, Value val, BackedgeBuilder &edgeBuilder,
127  ConversionPatternRewriter &rewriter);
128 
129  // Replaces standard memory ops with their handshake version (i.e.,
130  // ops which connect to memory/LSQ). Returns a map with an ordered
131  // list of new ops corresponding to each memref. Later, we instantiate
132  // a memory node for each memref and connect it to its load/store ops
133  LogicalResult replaceMemoryOps(ConversionPatternRewriter &rewriter,
134  MemRefToMemoryAccessOp &memRefOps);
135 
136  LogicalResult connectToMemory(ConversionPatternRewriter &rewriter,
137  MemRefToMemoryAccessOp memRefOps, bool lsq);
138  void setMemOpControlInputs(ConversionPatternRewriter &rewriter,
139  ArrayRef<Operation *> memOps, Operation *memOp,
140  int offset, ArrayRef<int> cntrlInd);
141 
142  // Returns the entry control value for operations contained within this
143  // block.
144  Value getBlockEntryControl(Block *block) const;
145 
146  void setBlockEntryControl(Block *block, Value v);
147 
148  Region &getRegion() { return r; }
149  MLIRContext *getContext() { return r.getContext(); }
150 
151 protected:
152  // SSA maximization dispatch, to avoid an inclusion of
153  // "circt/Transforms/Passes.h" in this header file (which itself is in the
154  // Converisons library). Instead, move this dependency to CFToHandshake.cpp.
155  LogicalResult runSSAMaximization(ConversionPatternRewriter &rewriter,
156  Value entryCtrl);
157 
158  Region &r;
159 
160  /// Start point of the control-only network
161  BlockArgument startCtrl;
162 
163 private:
164  DenseMap<Block *, Value> blockEntryControlMap;
165 };
166 
167 // Driver for the HandshakeLowering class.
168 // Note: using two different vararg template names due to potantial references
169 // that would cause a type mismatch
170 template <typename T, typename... TArgs, typename... TArgs2>
171 LogicalResult runPartialLowering(
172  T &instance,
173  LogicalResult (T::*memberFunc)(ConversionPatternRewriter &, TArgs2...),
174  TArgs &...args) {
175  return partiallyLowerRegion(
176  [&](Region &, ConversionPatternRewriter &rewriter) -> LogicalResult {
177  return (instance.*memberFunc)(rewriter, args...);
178  },
179  instance.getContext(), instance.getRegion());
180 }
181 
182 /// Remove basic blocks inside the given region. This allows the result to be
183 /// a valid graph region, since multi-basic block regions are not allowed to
184 /// be graph regions currently.
185 void removeBasicBlocks(Region &r);
186 
187 // Helper to check the validity of the dataflow conversion
188 // Driver that applies the partial lowerings expressed in HandshakeLowering to
189 // the region encapsulated in it. The region is assumed to have a terminator of
190 // type TSrcTerm, and will replace it with TDstTerm. See HandshakeLowering for
191 // the different lowering steps.
192 template <typename TSrcTerm, typename TDstTerm>
193 LogicalResult lowerRegion(HandshakeLowering &hl, bool sourceConstants,
194  bool disableTaskPipelining, Value entryCtrl) {
195  // Perform initial dataflow conversion. This process allows for the use of
196  // non-deterministic merge-like operations.
198 
199  if (failed(
201  return failure();
202  if (failed(runPartialLowering(
203  hl, &HandshakeLowering::setControlOnlyPath<TSrcTerm, TDstTerm>,
204  entryCtrl)))
205  return failure();
207  return failure();
209  return failure();
211  return failure();
212 
213  // The following passes modifies the dataflow graph to being safe for task
214  // pipelining. In doing so, non-deterministic merge structures are replaced
215  // for deterministic structures.
216  if (!disableTaskPipelining) {
217  if (failed(
219  return failure();
220  if (failed(
222  return failure();
223  }
224 
225  if (failed(runPartialLowering(
226  hl, &HandshakeLowering::connectConstantsToControl, sourceConstants)))
227  return failure();
228 
229  bool lsq = false;
231  lsq)))
232  return failure();
233 
234  // Legalize the resulting regions, removing basic blocks and performing
235  // any simple conversions.
237 
238  return success();
239 }
240 
241 /// Lowers the mlir operations into handshake that are not part of the dataflow
242 /// conversion.
243 LogicalResult postDataflowConvert(Operation *op);
244 
245 } // namespace handshake
246 
247 std::unique_ptr<mlir::OperationPass<mlir::ModuleOp>>
249 
250 std::unique_ptr<mlir::OperationPass<mlir::ModuleOp>>
251 createCFToHandshakePass(bool sourceConstants = false,
252  bool disableTaskPipelining = false);
253 
254 std::unique_ptr<mlir::OperationPass<handshake::FuncOp>>
256 
257 std::unique_ptr<mlir::OperationPass<handshake::FuncOp>>
259 
260 /// Insert additional blocks that serve as counterparts to the blocks that
261 /// diverged the control flow.
262 /// The resulting merge block tree is guaranteed to be a binary tree.
263 ///
264 /// This transformation does treat loops like a single block and thus does not
265 /// affect them.
266 mlir::LogicalResult
267 insertMergeBlocks(mlir::Region &r, mlir::ConversionPatternRewriter &rewriter);
268 
269 std::unique_ptr<mlir::Pass> createInsertMergeBlocksPass();
270 
271 } // namespace circt
272 
273 #endif // CIRCT_CONVERSION_CFTOHANDSHAKE_H_
assert(baseType &&"element must be base type")
llvm::ScopedHashTable< mlir::Value, std::string > ValueMap
Instantiate one of these and use it to build typed backedges.
BlockOps insertMergeOps(ValueMap &mergePairs, BackedgeBuilder &edgeBuilder, ConversionPatternRewriter &rewriter)
MergeOpInfo insertMerge(Block *block, Value val, BackedgeBuilder &edgeBuilder, ConversionPatternRewriter &rewriter)
LogicalResult loopNetworkRewriting(ConversionPatternRewriter &rewriter)
BlockArgument startCtrl
Start point of the control-only network.
DenseMap< Block *, std::vector< MergeOpInfo > > BlockOps
Definition: CFToHandshake.h:65
LogicalResult feedForwardRewriting(ConversionPatternRewriter &rewriter)
LogicalResult replaceCallOps(ConversionPatternRewriter &rewriter)
void setMemOpControlInputs(ConversionPatternRewriter &rewriter, ArrayRef< Operation * > memOps, Operation *memOp, int offset, ArrayRef< int > cntrlInd)
LogicalResult addMergeOps(ConversionPatternRewriter &rewriter)
LogicalResult replaceMemoryOps(ConversionPatternRewriter &rewriter, MemRefToMemoryAccessOp &memRefOps)
DenseMap< Block *, std::vector< Value > > BlockValues
Definition: CFToHandshake.h:64
LogicalResult connectConstantsToControl(ConversionPatternRewriter &rewriter, bool sourceConstants)
llvm::MapVector< Value, std::vector< Operation * > > MemRefToMemoryAccessOp
Definition: CFToHandshake.h:68
LogicalResult runSSAMaximization(ConversionPatternRewriter &rewriter, Value entryCtrl)
DenseMap< Block *, Value > blockEntryControlMap
LogicalResult setControlOnlyPath(ConversionPatternRewriter &rewriter, Value entryCtrl)
Definition: CFToHandshake.h:77
DenseMap< Value, Value > ValueMap
Definition: CFToHandshake.h:66
Value getBlockEntryControl(Block *block) const
void setBlockEntryControl(Block *block, Value v)
LogicalResult connectToMemory(ConversionPatternRewriter &rewriter, MemRefToMemoryAccessOp memRefOps, bool lsq)
LogicalResult addBranchOps(ConversionPatternRewriter &rewriter)
LogicalResult runPartialLowering(T &instance, LogicalResult(T::*memberFunc)(ConversionPatternRewriter &, TArgs2...), TArgs &...args)
LogicalResult postDataflowConvert(Operation *op)
Lowers the mlir operations into handshake that are not part of the dataflow conversion.
LogicalResult lowerRegion(HandshakeLowering &hl, bool sourceConstants, bool disableTaskPipelining, Value entryCtrl)
llvm::function_ref< LogicalResult(Region &, ConversionPatternRewriter &)> RegionLoweringFunc
Definition: CFToHandshake.h:43
void removeBasicBlocks(Region &r)
Remove basic blocks inside the given region.
LogicalResult partiallyLowerRegion(const RegionLoweringFunc &loweringFunc, MLIRContext *ctx, Region &r)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
std::unique_ptr< mlir::Pass > createInsertMergeBlocksPass()
mlir::LogicalResult insertMergeBlocks(mlir::Region &r, mlir::ConversionPatternRewriter &rewriter)
Insert additional blocks that serve as counterparts to the blocks that diverged the control flow.
std::unique_ptr< mlir::OperationPass< mlir::ModuleOp > > createCFToHandshakePass(bool sourceConstants=false, bool disableTaskPipelining=false)
std::unique_ptr< mlir::OperationPass< mlir::ModuleOp > > createHandshakeAnalysisPass()
std::unique_ptr< mlir::OperationPass< handshake::FuncOp > > createHandshakeRemoveBlockPass()
std::unique_ptr< mlir::OperationPass< handshake::FuncOp > > createHandshakeCanonicalizePass()