CIRCT  19.0.0git
HandshakeToDC.cpp
Go to the documentation of this file.
1 //===- HandshakeToDC.cpp - Translate Handshake into DC --------------------===//
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 is the main Handshake to DC Conversion Pass Implementation.
10 //
11 //===----------------------------------------------------------------------===//
12 
14 #include "../PassDetail.h"
17 #include "circt/Dialect/DC/DCOps.h"
19 #include "circt/Dialect/HW/HWOps.h"
24 #include "mlir/Dialect/Arith/IR/Arith.h"
25 #include "mlir/Pass/PassManager.h"
26 #include "mlir/Transforms/DialectConversion.h"
27 #include "llvm/Support/MathExtras.h"
28 #include <optional>
29 
30 using namespace mlir;
31 using namespace circt;
32 using namespace handshake;
33 using namespace dc;
34 using namespace hw;
35 using namespace handshaketodc;
36 
37 namespace {
38 
39 struct DCTuple {
40  DCTuple() = default;
41  DCTuple(Value token, Value data) : token(token), data(data) {}
42  DCTuple(dc::UnpackOp unpack)
43  : token(unpack.getToken()), data(unpack.getOutput()) {}
44  Value token;
45  Value data;
46 };
47 
48 // Unpack a !dc.value<...> into a DCTuple.
49 static DCTuple unpack(OpBuilder &b, Value v) {
50  if (v.getType().isa<dc::ValueType>())
51  return DCTuple(b.create<dc::UnpackOp>(v.getLoc(), v));
52  assert(v.getType().isa<dc::TokenType>() && "Expected a dc::TokenType");
53  return DCTuple(v, {});
54 }
55 
56 static Value pack(OpBuilder &b, Value token, Value data = {}) {
57  if (!data)
58  return token;
59  return b.create<dc::PackOp>(token.getLoc(), token, data);
60 }
61 
62 class DCTypeConverter : public TypeConverter {
63 public:
64  DCTypeConverter() {
65  addConversion([](Type type) -> Type {
66  if (type.isa<NoneType>())
67  return dc::TokenType::get(type.getContext());
68  return dc::ValueType::get(type.getContext(), type);
69  });
70  addConversion([](ValueType type) { return type; });
71  addConversion([](TokenType type) { return type; });
72 
73  addTargetMaterialization(
74  [](mlir::OpBuilder &builder, mlir::Type resultType,
75  mlir::ValueRange inputs,
76  mlir::Location loc) -> std::optional<mlir::Value> {
77  if (inputs.size() != 1)
78  return std::nullopt;
79 
80  // Materialize !dc.value<> -> !dc.token
81  if (resultType.isa<dc::TokenType>() &&
82  inputs.front().getType().isa<dc::ValueType>())
83  return unpack(builder, inputs.front()).token;
84 
85  // Materialize !dc.token -> !dc.value<>
86  auto vt = resultType.dyn_cast<dc::ValueType>();
87  if (vt && !vt.getInnerType())
88  return pack(builder, inputs.front());
89 
90  return inputs[0];
91  });
92 
93  addSourceMaterialization(
94  [](mlir::OpBuilder &builder, mlir::Type resultType,
95  mlir::ValueRange inputs,
96  mlir::Location loc) -> std::optional<mlir::Value> {
97  if (inputs.size() != 1)
98  return std::nullopt;
99 
100  // Materialize !dc.value<> -> !dc.token
101  if (resultType.isa<dc::TokenType>() &&
102  inputs.front().getType().isa<dc::ValueType>())
103  return unpack(builder, inputs.front()).token;
104 
105  // Materialize !dc.token -> !dc.value<>
106  auto vt = resultType.dyn_cast<dc::ValueType>();
107  if (vt && !vt.getInnerType())
108  return pack(builder, inputs.front());
109 
110  return inputs[0];
111  });
112  }
113 };
114 
115 template <typename OpTy>
116 class DCOpConversionPattern : public OpConversionPattern<OpTy> {
117 public:
119  using OpAdaptor = typename OpTy::Adaptor;
120 
121  DCOpConversionPattern(MLIRContext *context, TypeConverter &typeConverter,
122  ConvertedOps *convertedOps)
123  : OpConversionPattern<OpTy>(typeConverter, context),
124  convertedOps(convertedOps) {}
125  mutable ConvertedOps *convertedOps;
126 };
127 
128 class CondBranchConversionPattern
129  : public DCOpConversionPattern<handshake::ConditionalBranchOp> {
130 public:
131  using DCOpConversionPattern<
132  handshake::ConditionalBranchOp>::DCOpConversionPattern;
133  using OpAdaptor = typename handshake::ConditionalBranchOp::Adaptor;
134 
135  LogicalResult
136  matchAndRewrite(handshake::ConditionalBranchOp op, OpAdaptor adaptor,
137  ConversionPatternRewriter &rewriter) const override {
138  auto condition = unpack(rewriter, adaptor.getConditionOperand());
139  auto data = unpack(rewriter, adaptor.getDataOperand());
140 
141  // Join the token of the condition and the input.
142  auto join = rewriter.create<dc::JoinOp>(
143  op.getLoc(), ValueRange{condition.token, data.token});
144 
145  // Pack that together with the condition data.
146  auto packedCondition = pack(rewriter, join, condition.data);
147 
148  // Branch on the input data and the joined control input.
149  auto branch = rewriter.create<dc::BranchOp>(op.getLoc(), packedCondition);
150 
151  // Pack the branch output tokens with the input data, and replace the uses.
152  llvm::SmallVector<Value, 4> packed;
153  packed.push_back(pack(rewriter, branch.getTrueToken(), data.data));
154  packed.push_back(pack(rewriter, branch.getFalseToken(), data.data));
155 
156  rewriter.replaceOp(op, packed);
157  return success();
158  }
159 };
160 
161 class ForkOpConversionPattern
162  : public DCOpConversionPattern<handshake::ForkOp> {
163 public:
164  using DCOpConversionPattern<handshake::ForkOp>::DCOpConversionPattern;
165  using OpAdaptor = typename handshake::ForkOp::Adaptor;
166 
167  LogicalResult
168  matchAndRewrite(handshake::ForkOp op, OpAdaptor adaptor,
169  ConversionPatternRewriter &rewriter) const override {
170  auto input = unpack(rewriter, adaptor.getOperand());
171  auto forkOut = rewriter.create<dc::ForkOp>(op.getLoc(), input.token,
172  op.getNumResults());
173 
174  // Pack the fork result tokens with the input data, and replace the uses.
175  llvm::SmallVector<Value, 4> packed;
176  for (auto res : forkOut.getResults())
177  packed.push_back(pack(rewriter, res, input.data));
178 
179  rewriter.replaceOp(op, packed);
180  return success();
181  }
182 };
183 
184 class JoinOpConversion : public DCOpConversionPattern<handshake::JoinOp> {
185 public:
186  using DCOpConversionPattern<handshake::JoinOp>::DCOpConversionPattern;
187  using OpAdaptor = typename handshake::JoinOp::Adaptor;
188 
189  LogicalResult
190  matchAndRewrite(handshake::JoinOp op, OpAdaptor adaptor,
191  ConversionPatternRewriter &rewriter) const override {
192  llvm::SmallVector<Value, 4> inputTokens;
193  for (auto input : adaptor.getData())
194  inputTokens.push_back(unpack(rewriter, input).token);
195 
196  rewriter.replaceOpWithNewOp<dc::JoinOp>(op, inputTokens);
197  return success();
198  }
199 };
200 
201 class ControlMergeOpConversion
202  : public DCOpConversionPattern<handshake::ControlMergeOp> {
203 public:
204  using DCOpConversionPattern<handshake::ControlMergeOp>::DCOpConversionPattern;
205 
206  using OpAdaptor = typename handshake::ControlMergeOp::Adaptor;
207 
208  LogicalResult
209  matchAndRewrite(handshake::ControlMergeOp op, OpAdaptor adaptor,
210  ConversionPatternRewriter &rewriter) const override {
211  if (op.getDataOperands().size() != 2)
212  return op.emitOpError("expected two data operands");
213 
214  llvm::SmallVector<Value> tokens, data;
215  for (auto input : adaptor.getDataOperands()) {
216  auto up = unpack(rewriter, input);
217  tokens.push_back(up.token);
218  if (up.data)
219  data.push_back(up.data);
220  }
221 
222  // control-side
223  Value selectedIndex = rewriter.create<dc::MergeOp>(op.getLoc(), tokens);
224  auto mergeOpUnpacked = unpack(rewriter, selectedIndex);
225  auto selValue = mergeOpUnpacked.data;
226 
227  Value dataSide = selectedIndex;
228  if (!data.empty()) {
229  // Data side mux using the selected input.
230  auto dataMux = rewriter.create<arith::SelectOp>(op.getLoc(), selValue,
231  data[0], data[1]);
232  convertedOps->insert(dataMux);
233  // Pack the data mux with the control token.
234  auto packed = pack(rewriter, mergeOpUnpacked.token, dataMux);
235 
236  dataSide = packed;
237  }
238 
239  // if the original op used `index` as the select operand type, we need to
240  // index-cast the unpacked select operand
241  if (op.getIndex().getType().isa<IndexType>()) {
242  selValue = rewriter.create<arith::IndexCastOp>(
243  op.getLoc(), rewriter.getIndexType(), selValue);
244  convertedOps->insert(selValue.getDefiningOp());
245  selectedIndex = pack(rewriter, mergeOpUnpacked.token, selValue);
246  }
247 
248  rewriter.replaceOp(op, {dataSide, selectedIndex});
249  return success();
250  }
251 };
252 
253 class SyncOpConversion : public DCOpConversionPattern<handshake::SyncOp> {
254 public:
255  using DCOpConversionPattern<handshake::SyncOp>::DCOpConversionPattern;
256  using OpAdaptor = typename handshake::SyncOp::Adaptor;
257 
258  LogicalResult
259  matchAndRewrite(handshake::SyncOp op, OpAdaptor adaptor,
260  ConversionPatternRewriter &rewriter) const override {
261  llvm::SmallVector<Value, 4> inputTokens;
262  for (auto input : adaptor.getOperands())
263  inputTokens.push_back(unpack(rewriter, input).token);
264 
265  auto syncToken = rewriter.create<dc::JoinOp>(op.getLoc(), inputTokens);
266 
267  // Wrap all outputs with the synchronization token
268  llvm::SmallVector<Value, 4> wrappedInputs;
269  for (auto input : adaptor.getOperands())
270  wrappedInputs.push_back(pack(rewriter, syncToken, input));
271 
272  rewriter.replaceOp(op, wrappedInputs);
273 
274  return success();
275  }
276 };
277 
278 class ConstantOpConversion
279  : public DCOpConversionPattern<handshake::ConstantOp> {
280 public:
281  using DCOpConversionPattern<handshake::ConstantOp>::DCOpConversionPattern;
282  using OpAdaptor = typename handshake::ConstantOp::Adaptor;
283 
284  LogicalResult
285  matchAndRewrite(handshake::ConstantOp op, OpAdaptor adaptor,
286  ConversionPatternRewriter &rewriter) const override {
287  // Wrap the constant with a token.
288  auto token = rewriter.create<dc::SourceOp>(op.getLoc());
289  auto cst =
290  rewriter.create<arith::ConstantOp>(op.getLoc(), adaptor.getValue());
291  convertedOps->insert(cst);
292  rewriter.replaceOp(op, pack(rewriter, token, cst));
293  return success();
294  }
295 };
296 
297 struct UnitRateConversionPattern : public ConversionPattern {
298 public:
299  UnitRateConversionPattern(MLIRContext *context, TypeConverter &converter,
300  ConvertedOps *joinedOps)
301  : ConversionPattern(converter, MatchAnyOpTypeTag(), 1, context),
302  joinedOps(joinedOps) {}
303  using ConversionPattern::ConversionPattern;
304 
305  // Generic pattern which replaces an operation by one of the same type, but
306  // with the in- and outputs synchronized through join semantics.
307  LogicalResult
308  matchAndRewrite(Operation *op, ArrayRef<Value> operands,
309  ConversionPatternRewriter &rewriter) const override {
310  llvm::SmallVector<Value> inputData;
311 
312  Value outToken;
313  if (operands.empty()) {
314  if (!op->hasTrait<OpTrait::ConstantLike>())
315  return op->emitOpError(
316  "no-operand operation which isn't constant-like. Too dangerous "
317  "to assume semantics - won't convert");
318 
319  // Constant-like operation; assume the token can be represented as a
320  // constant `dc.source`.
321  outToken = rewriter.create<dc::SourceOp>(op->getLoc());
322  } else {
323  llvm::SmallVector<Value> inputTokens;
324  for (auto input : operands) {
325  auto dct = unpack(rewriter, input);
326  inputData.push_back(dct.data);
327  inputTokens.push_back(dct.token);
328  }
329  // Join the tokens of the inputs.
330  assert(!inputTokens.empty() && "Expected at least one input token");
331  outToken = rewriter.create<dc::JoinOp>(op->getLoc(), inputTokens);
332  }
333 
334  // Patchwork to fix bad IR design in Handshake.
335  auto opName = op->getName();
336  if (opName.getStringRef() == "handshake.select") {
337  opName = OperationName("arith.select", getContext());
338  } else if (opName.getStringRef() == "handshake.constant") {
339  opName = OperationName("arith.constant", getContext());
340  }
341 
342  // Re-create the operation using the unpacked input data.
343  OperationState state(op->getLoc(), opName, inputData, op->getResultTypes(),
344  op->getAttrs(), op->getSuccessors());
345 
346  Operation *newOp = rewriter.create(state);
347  joinedOps->insert(newOp);
348 
349  // Pack the result token with the output data, and replace the uses.
350  llvm::SmallVector<Value> results;
351  for (auto result : newOp->getResults())
352  results.push_back(pack(rewriter, outToken, result));
353 
354  rewriter.replaceOp(op, results);
355 
356  return success();
357  }
358 
359  mutable ConvertedOps *joinedOps;
360 };
361 
362 class SinkOpConversionPattern
363  : public DCOpConversionPattern<handshake::SinkOp> {
364 public:
365  using DCOpConversionPattern<handshake::SinkOp>::DCOpConversionPattern;
366  using OpAdaptor = typename handshake::SinkOp::Adaptor;
367 
368  LogicalResult
369  matchAndRewrite(handshake::SinkOp op, OpAdaptor adaptor,
370  ConversionPatternRewriter &rewriter) const override {
371  auto input = unpack(rewriter, adaptor.getOperand());
372  rewriter.replaceOpWithNewOp<dc::SinkOp>(op, input.token);
373  return success();
374  }
375 };
376 
377 class SourceOpConversionPattern
378  : public DCOpConversionPattern<handshake::SourceOp> {
379 public:
380  using DCOpConversionPattern<handshake::SourceOp>::DCOpConversionPattern;
381  using OpAdaptor = typename handshake::SourceOp::Adaptor;
382 
383  LogicalResult
384  matchAndRewrite(handshake::SourceOp op, OpAdaptor adaptor,
385  ConversionPatternRewriter &rewriter) const override {
386  rewriter.replaceOpWithNewOp<dc::SourceOp>(op);
387  return success();
388  }
389 };
390 
391 class BufferOpConversion : public DCOpConversionPattern<handshake::BufferOp> {
392 public:
393  using DCOpConversionPattern<handshake::BufferOp>::DCOpConversionPattern;
394  using OpAdaptor = typename handshake::BufferOp::Adaptor;
395 
396  LogicalResult
397  matchAndRewrite(handshake::BufferOp op, OpAdaptor adaptor,
398  ConversionPatternRewriter &rewriter) const override {
399  rewriter.getI32IntegerAttr(1);
400  rewriter.replaceOpWithNewOp<dc::BufferOp>(
401  op, adaptor.getOperand(), static_cast<size_t>(op.getNumSlots()));
402  return success();
403  }
404 };
405 
406 class ReturnOpConversion : public OpConversionPattern<handshake::ReturnOp> {
407 public:
409  using OpAdaptor = typename handshake::ReturnOp::Adaptor;
410 
411  LogicalResult
412  matchAndRewrite(handshake::ReturnOp op, OpAdaptor adaptor,
413  ConversionPatternRewriter &rewriter) const override {
414  // Locate existing output op, Append operands to output op, and move to
415  // the end of the block.
416  auto hwModule = op->getParentOfType<hw::HWModuleOp>();
417  auto outputOp = *hwModule.getBodyBlock()->getOps<hw::OutputOp>().begin();
418  outputOp->setOperands(adaptor.getOperands());
419  outputOp->moveAfter(&hwModule.getBodyBlock()->back());
420  rewriter.eraseOp(op);
421  return success();
422  }
423 };
424 
425 class MuxOpConversionPattern : public DCOpConversionPattern<handshake::MuxOp> {
426 public:
427  using DCOpConversionPattern<handshake::MuxOp>::DCOpConversionPattern;
428  using OpAdaptor = typename handshake::MuxOp::Adaptor;
429 
430  LogicalResult
431  matchAndRewrite(handshake::MuxOp op, OpAdaptor adaptor,
432  ConversionPatternRewriter &rewriter) const override {
433  auto select = unpack(rewriter, adaptor.getSelectOperand());
434  auto selectData = select.data;
435  auto selectToken = select.token;
436  bool isIndexType = selectData.getType().isa<IndexType>();
437 
438  bool withData = !op.getResult().getType().isa<NoneType>();
439 
440  llvm::SmallVector<DCTuple> inputs;
441  for (auto input : adaptor.getDataOperands())
442  inputs.push_back(unpack(rewriter, input));
443 
444  Value dataMux;
445  Value controlMux = inputs.front().token;
446  // Convert the data-side mux to a sequence of arith.select operations.
447  // The data and control muxes are assumed one-hot and the base-case is set
448  // as the first input.
449  if (withData)
450  dataMux = inputs[0].data;
451 
452  llvm::SmallVector<Value> controlMuxInputs = {inputs.front().token};
453  for (auto [i, input] :
454  llvm::enumerate(llvm::make_range(inputs.begin() + 1, inputs.end()))) {
455  if (!withData)
456  continue;
457 
458  Value cmpIndex;
459  Value inputData = input.data;
460  Value inputControl = input.token;
461  if (isIndexType) {
462  cmpIndex = rewriter.create<arith::ConstantIndexOp>(op.getLoc(), i);
463  } else {
464  size_t width = selectData.getType().cast<IntegerType>().getWidth();
465  cmpIndex = rewriter.create<arith::ConstantIntOp>(op.getLoc(), i, width);
466  }
467  auto inputSelected = rewriter.create<arith::CmpIOp>(
468  op.getLoc(), arith::CmpIPredicate::eq, selectData, cmpIndex);
469  dataMux = rewriter.create<arith::SelectOp>(op.getLoc(), inputSelected,
470  inputData, dataMux);
471 
472  // Legalize the newly created operations.
473  convertedOps->insert(cmpIndex.getDefiningOp());
474  convertedOps->insert(dataMux.getDefiningOp());
475  convertedOps->insert(inputSelected);
476 
477  // And similarly for the control mux, by muxing the input token with a
478  // select value that has it's control from the original select token +
479  // the inputSelected value.
480  auto inputSelectedControl = pack(rewriter, selectToken, inputSelected);
481  controlMux = rewriter.create<dc::SelectOp>(
482  op.getLoc(), inputSelectedControl, inputControl, controlMux);
483  convertedOps->insert(controlMux.getDefiningOp());
484  }
485 
486  // finally, pack the control and data side muxes into the output value.
487  rewriter.replaceOp(
488  op, pack(rewriter, controlMux, withData ? dataMux : Value{}));
489  return success();
490  }
491 };
492 
493 static hw::ModulePortInfo getModulePortInfoHS(const TypeConverter &tc,
494  handshake::FuncOp funcOp) {
495  SmallVector<hw::PortInfo> inputs, outputs;
496  auto *ctx = funcOp->getContext();
497  auto ft = funcOp.getFunctionType();
498 
499  // Add all inputs of funcOp.
500  for (auto [index, type] : llvm::enumerate(ft.getInputs())) {
501  inputs.push_back({{StringAttr::get(ctx, "in" + std::to_string(index)),
502  tc.convertType(type), hw::ModulePort::Direction::Input},
503  index,
504  {}});
505  }
506 
507  // Add all outputs of funcOp.
508  for (auto [index, type] : llvm::enumerate(ft.getResults())) {
509  outputs.push_back(
510  {{StringAttr::get(ctx, "out" + std::to_string(index)),
511  tc.convertType(type), hw::ModulePort::Direction::Output},
512  index,
513  {}});
514  }
515 
516  return hw::ModulePortInfo{inputs, outputs};
517 }
518 
519 class FuncOpConversion : public OpConversionPattern<handshake::FuncOp> {
520 public:
522  using OpAdaptor = typename handshake::FuncOp::Adaptor;
523 
524  // Replaces a handshake.func with a hw.module, converting the argument and
525  // result types using the provided type converter.
526  // @mortbopet: Not a fan of converting to hw here seeing as we don't
527  // necessarily have hardware semantics here. But, DC doesn't define a function
528  // operation, and there is no "func.graph_func" or any other generic function
529  // operation which is a graph region...
530  LogicalResult
531  matchAndRewrite(handshake::FuncOp op, OpAdaptor adaptor,
532  ConversionPatternRewriter &rewriter) const override {
533  ModulePortInfo ports = getModulePortInfoHS(*getTypeConverter(), op);
534 
535  if (op.isExternal()) {
536  rewriter.create<hw::HWModuleExternOp>(
537  op.getLoc(), rewriter.getStringAttr(op.getName()), ports);
538  } else {
539  auto hwModule = rewriter.create<hw::HWModuleOp>(
540  op.getLoc(), rewriter.getStringAttr(op.getName()), ports);
541 
542  auto &region = op->getRegions().front();
543 
544  Region &moduleRegion = hwModule->getRegions().front();
545  rewriter.mergeBlocks(&region.getBlocks().front(), hwModule.getBodyBlock(),
546  hwModule.getBodyBlock()->getArguments());
547  TypeConverter::SignatureConversion result(moduleRegion.getNumArguments());
548  (void)getTypeConverter()->convertSignatureArgs(
549  TypeRange(moduleRegion.getArgumentTypes()), result);
550  rewriter.applySignatureConversion(&moduleRegion, result);
551  }
552 
553  rewriter.eraseOp(op);
554  return success();
555  }
556 };
557 
558 class HandshakeToDCPass : public HandshakeToDCBase<HandshakeToDCPass> {
559 public:
560  void runOnOperation() override {
561  mlir::ModuleOp mod = getOperation();
562  auto targetModifier = [](mlir::ConversionTarget &target) {
563  target.addLegalDialect<hw::HWDialect, func::FuncDialect>();
564  };
565 
566  auto patternBuilder = [&](TypeConverter &typeConverter,
567  handshaketodc::ConvertedOps &convertedOps,
568  RewritePatternSet &patterns) {
569  patterns.add<FuncOpConversion, ReturnOpConversion>(typeConverter,
570  mod.getContext());
571  };
572 
573  LogicalResult res = runHandshakeToDC(mod, patternBuilder, targetModifier);
574  if (failed(res))
575  signalPassFailure();
576  }
577 };
578 } // namespace
579 
580 std::unique_ptr<mlir::Pass> circt::createHandshakeToDCPass() {
581  return std::make_unique<HandshakeToDCPass>();
582 }
583 
585  mlir::Operation *op,
586  llvm::function_ref<void(TypeConverter &typeConverter,
587  handshaketodc::ConvertedOps &convertedOps,
588  RewritePatternSet &patterns)>
589  patternBuilder,
590  llvm::function_ref<void(mlir::ConversionTarget &)> configureTarget) {
591  // Maintain the set of operations which has been converted either through
592  // unit rate conversion, or as part of other conversions.
593  // Rationale:
594  // This is needed for all of the arith ops that get created as part of the
595  // handshake ops (e.g. arith.select for handshake.mux). There's a bit of a
596  // dilemma here seeing as all operations need to be converted/touched in a
597  // handshake.func - which is done so by UnitRateConversionPattern (when no
598  // other pattern applies). However, we obviously don't want to run said
599  // pattern on these newly created ops since they do not have handshake
600  // semantics.
601  handshaketodc::ConvertedOps convertedOps;
602  mlir::MLIRContext *ctx = op->getContext();
603  ConversionTarget target(*ctx);
604  target.addIllegalDialect<handshake::HandshakeDialect>();
605  target.addLegalDialect<dc::DCDialect>();
606  target.addLegalOp<mlir::ModuleOp>();
607 
608  // And any user-specified target adjustments
609  if (configureTarget)
610  configureTarget(target);
611 
612  // The various patterns will insert new operations into the module to
613  // facilitate the conversion - however, these operations must be
614  // distinguishable from already converted operations (which may be of the
615  // same type as the newly inserted operations). To do this, we mark all
616  // operations which have been converted as legal, and all other operations
617  // as illegal.
618  target.markUnknownOpDynamicallyLegal(
619  [&](Operation *op) { return convertedOps.contains(op); });
620 
621  DCTypeConverter typeConverter;
622  RewritePatternSet patterns(ctx);
623 
624  // Add handshake conversion patterns.
625  // Note: merge/control merge are not supported - these are non-deterministic
626  // operators and we do not care for them.
627  patterns
628  .add<BufferOpConversion, CondBranchConversionPattern,
629  SinkOpConversionPattern, SourceOpConversionPattern,
630  MuxOpConversionPattern, ForkOpConversionPattern, JoinOpConversion,
631  ControlMergeOpConversion, ConstantOpConversion, SyncOpConversion>(
632  ctx, typeConverter, &convertedOps);
633 
634  // ALL other single-result operations are converted via the
635  // UnitRateConversionPattern.
636  patterns.add<UnitRateConversionPattern>(ctx, typeConverter, &convertedOps);
637 
638  // Build any user-specified patterns
639  patternBuilder(typeConverter, convertedOps, patterns);
640  return applyPartialConversion(op, target, std::move(patterns));
641 }
assert(baseType &&"element must be base type")
int32_t width
Definition: FIRRTL.cpp:36
@ Input
Definition: HW.h:35
@ Output
Definition: HW.h:35
llvm::SmallVector< StringAttr > inputs
llvm::SmallVector< StringAttr > outputs
Builder builder
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
uint64_t getWidth(Type t)
Definition: ESIPasses.cpp:34
DenseSet< Operation * > ConvertedOps
Definition: HandshakeToDC.h:30
LogicalResult runHandshakeToDC(mlir::Operation *op, llvm::function_ref< void(TypeConverter &typeConverter, ConvertedOps &convertedOps, RewritePatternSet &patterns)> patternBuilder, llvm::function_ref< void(mlir::ConversionTarget &)> configureTarget={})
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
std::unique_ptr< mlir::Pass > createHandshakeToDCPass()
Definition: hw.py:1
This holds a decoded list of input/inout and output ports for a module or instance.