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