CIRCT  20.0.0git
FIRRTLIntrinsics.cpp
Go to the documentation of this file.
1 //===- FIRRTLIntrinsics.cpp - Lower Intrinsics ------------------*- 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 
12 #include "mlir/Transforms/DialectConversion.h"
13 
14 using namespace circt;
15 using namespace firrtl;
16 
17 //===----------------------------------------------------------------------===//
18 // GenericIntrinsic
19 //===----------------------------------------------------------------------===//
20 
21 // Checks for a number of operands between n and n+c (allows for c optional
22 // inputs)
23 ParseResult GenericIntrinsic::hasNInputs(unsigned n, unsigned c) {
24  auto numOps = op.getNumOperands();
25  unsigned m = n + c;
26  if (numOps < n || numOps > m) {
27  auto err = emitError() << " has " << numOps << " inputs instead of ";
28  if (c == 0)
29  err << n;
30  else
31  err << " between " << n << " and " << m;
32  return failure();
33  }
34  return success();
35 }
36 
37 // Accessor method for the number of inputs
38 unsigned GenericIntrinsic::getNumInputs() { return op.getNumOperands(); }
39 
40 ParseResult GenericIntrinsic::hasNOutputElements(unsigned n) {
41  auto b = getOutputBundle();
42  if (!b)
43  return emitError() << " missing output bundle";
44  if (b.getType().getNumElements() != n)
45  return emitError() << " has " << b.getType().getNumElements()
46  << " output elements instead of " << n;
47  return success();
48 }
49 
50 ParseResult GenericIntrinsic::hasNParam(unsigned n, unsigned c) {
51  unsigned num = 0;
52  if (op.getParameters())
53  num = op.getParameters().size();
54  if (num < n || num > n + c) {
55  auto d = emitError() << " has " << num << " parameters instead of ";
56  if (c == 0)
57  d << n;
58  else
59  d << " between " << n << " and " << (n + c);
60  return failure();
61  }
62  return success();
63 }
64 
65 ParseResult GenericIntrinsic::namedParam(StringRef paramName, bool optional) {
66  for (auto a : op.getParameters()) {
67  auto param = cast<ParamDeclAttr>(a);
68  if (param.getName().getValue() == paramName) {
69  if (isa<StringAttr>(param.getValue()))
70  return success();
71 
72  return emitError() << " has parameter '" << param.getName()
73  << "' which should be a string but is not";
74  }
75  }
76  if (optional)
77  return success();
78  return emitError() << " is missing parameter " << paramName;
79 }
80 
81 ParseResult GenericIntrinsic::namedIntParam(StringRef paramName,
82  bool optional) {
83  for (auto a : op.getParameters()) {
84  auto param = cast<ParamDeclAttr>(a);
85  if (param.getName().getValue() == paramName) {
86  if (isa<IntegerAttr>(param.getValue()))
87  return success();
88 
89  return emitError() << " has parameter '" << param.getName()
90  << "' which should be an integer but is not";
91  }
92  }
93  if (optional)
94  return success();
95  return emitError() << " is missing parameter " << paramName;
96 }
97 
98 //===----------------------------------------------------------------------===//
99 // IntrinsicOpConversion
100 //===----------------------------------------------------------------------===//
101 
102 /// Conversion pattern adaptor dispatching via generic intrinsic name.
103 namespace {
104 class IntrinsicOpConversion final
105  : public OpConversionPattern<GenericIntrinsicOp> {
106 public:
107  using ConversionMapTy = IntrinsicLowerings::ConversionMapTy;
108 
109  IntrinsicOpConversion(TypeConverter &typeConverter, MLIRContext *context,
110  const ConversionMapTy &conversions,
111  size_t &numConversions,
112  bool allowUnknownIntrinsics = false)
113  : OpConversionPattern(typeConverter, context), conversions(conversions),
114  numConversions(numConversions),
115  allowUnknownIntrinsics(allowUnknownIntrinsics) {}
116 
117  LogicalResult
118  matchAndRewrite(GenericIntrinsicOp op, OpAdaptor adaptor,
119  ConversionPatternRewriter &rewriter) const override {
120 
121  auto it = conversions.find(op.getIntrinsicAttr());
122  if (it == conversions.end()) {
123  if (!allowUnknownIntrinsics)
124  return op.emitError("unknown intrinsic ") << op.getIntrinsicAttr();
125  return failure();
126  }
127 
128  auto &conv = *it->second;
129  if (conv.check(GenericIntrinsic(op)))
130  return failure();
131  conv.convert(GenericIntrinsic(op), adaptor, rewriter);
132  ++numConversions;
133  return success();
134  }
135 
136 private:
137  const ConversionMapTy &conversions;
138  size_t &numConversions;
139  const bool allowUnknownIntrinsics;
140 };
141 } // namespace
142 
143 //===----------------------------------------------------------------------===//
144 // IntrinsicLowerings
145 //===----------------------------------------------------------------------===//
146 
147 FailureOr<size_t> IntrinsicLowerings::lower(FModuleOp mod,
148  bool allowUnknownIntrinsics) {
149 
150  ConversionTarget target(*context);
151 
152  target.markUnknownOpDynamicallyLegal([](Operation *op) { return true; });
153  if (allowUnknownIntrinsics)
154  target.addDynamicallyLegalOp<GenericIntrinsicOp>(
155  [this](GenericIntrinsicOp op) {
156  return !conversions.contains(op.getIntrinsicAttr());
157  });
158  else
159  target.addIllegalOp<GenericIntrinsicOp>();
160 
161  // Automatically insert wires + connect for compatible FIRRTL base types.
162  // For now, this is not customizable/extendable.
163  TypeConverter typeConverter;
164  typeConverter.addConversion([](Type type) { return type; });
165  auto firrtlBaseTypeMaterialization =
166  [](OpBuilder &builder, FIRRTLBaseType resultType, ValueRange inputs,
167  Location loc) -> Value {
168  if (inputs.size() != 1)
169  return {};
170  auto inputType = type_dyn_cast<FIRRTLBaseType>(inputs.front().getType());
171  if (!inputType)
172  return {};
173 
174  if (!areTypesEquivalent(resultType, inputType) ||
175  !isTypeLarger(resultType, inputType))
176  return {};
177 
178  auto w = builder.create<WireOp>(loc, resultType).getResult();
179  emitConnect(builder, loc, w, inputs.front());
180  return w;
181  };
182  // New result doesn't match? Add wire + connect.
183  typeConverter.addSourceMaterialization(firrtlBaseTypeMaterialization);
184  // New operand doesn't match? Add wire + connect.
185  typeConverter.addTargetMaterialization(firrtlBaseTypeMaterialization);
186 
187  RewritePatternSet patterns(context);
188  size_t count = 0;
189  patterns.add<IntrinsicOpConversion>(typeConverter, context, conversions,
190  count, allowUnknownIntrinsics);
191 
192  if (failed(mlir::applyPartialConversion(mod, target, std::move(patterns))))
193  return failure();
194 
195  return count;
196 }
197 
198 //===----------------------------------------------------------------------===//
199 // IntrinsicLoweringInterfaceCollection
200 //===----------------------------------------------------------------------===//
201 
203  IntrinsicLowerings &lowering) const {
204  for (const IntrinsicLoweringDialectInterface &interface : *this)
205  interface.populateIntrinsicLowerings(lowering);
206 }
207 
208 //===----------------------------------------------------------------------===//
209 // FIRRTL intrinsic lowering converters
210 //===----------------------------------------------------------------------===//
211 
212 namespace {
213 
214 class CirctSizeofConverter : public IntrinsicOpConverter<SizeOfIntrinsicOp> {
215 public:
216  using IntrinsicOpConverter::IntrinsicOpConverter;
217 
218  bool check(GenericIntrinsic gi) override {
219  return gi.hasNInputs(1) || gi.sizedOutput<UIntType>(32) || gi.hasNParam(0);
220  }
221 };
222 
223 class CirctIsXConverter : public IntrinsicOpConverter<IsXIntrinsicOp> {
224 public:
225  using IntrinsicOpConverter::IntrinsicOpConverter;
226 
227  bool check(GenericIntrinsic gi) override {
228  return gi.hasNInputs(1) || gi.sizedOutput<UIntType>(1) || gi.hasNParam(0);
229  }
230 };
231 
232 class CirctPlusArgTestConverter : public IntrinsicConverter {
233 public:
234  using IntrinsicConverter::IntrinsicConverter;
235 
236  bool check(GenericIntrinsic gi) override {
237  return gi.hasNInputs(0) || gi.sizedOutput<UIntType>(1) ||
238  gi.namedParam("FORMAT") || gi.hasNParam(1);
239  }
240 
241  void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
242  PatternRewriter &rewriter) override {
243  rewriter.replaceOpWithNewOp<PlusArgsTestIntrinsicOp>(
244  gi.op, gi.getParamValue<StringAttr>("FORMAT"));
245  }
246 };
247 
248 class CirctPlusArgValueConverter : public IntrinsicConverter {
249 public:
250  using IntrinsicConverter::IntrinsicConverter;
251 
252  bool check(GenericIntrinsic gi) override {
253  return gi.hasNOutputElements(2) ||
254  gi.sizedOutputElement<UIntType>(0, "found", 1) ||
255  gi.hasOutputElement(1, "result") || gi.namedParam("FORMAT") ||
256  gi.hasNParam(1);
257  }
258 
259  void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
260  PatternRewriter &rewriter) override {
261  auto bty = gi.getOutputBundle().getType();
262  auto newop = rewriter.create<PlusArgsValueIntrinsicOp>(
263  gi.op.getLoc(), bty.getElementTypePreservingConst(0),
264  bty.getElementTypePreservingConst(1),
265  gi.getParamValue<StringAttr>("FORMAT"));
266  rewriter.replaceOpWithNewOp<BundleCreateOp>(
267  gi.op, bty, ValueRange({newop.getFound(), newop.getResult()}));
268  }
269 };
270 
271 class CirctClockGateConverter
272  : public IntrinsicOpConverter<ClockGateIntrinsicOp> {
273 public:
274  using IntrinsicOpConverter::IntrinsicOpConverter;
275 
276  bool check(GenericIntrinsic gi) override {
277  if (gi.op.getNumOperands() == 3) {
278  return gi.typedInput<ClockType>(0) || gi.sizedInput<UIntType>(1, 1) ||
279  gi.sizedInput<UIntType>(2, 1) || gi.typedOutput<ClockType>() ||
280  gi.hasNParam(0);
281  }
282  if (gi.op.getNumOperands() == 2) {
283  return gi.typedInput<ClockType>(0) || gi.sizedInput<UIntType>(1, 1) ||
284  gi.typedOutput<ClockType>() || gi.hasNParam(0);
285  }
286  gi.emitError() << " has " << gi.op.getNumOperands()
287  << " ports instead of 3 or 4";
288  return true;
289  }
290 };
291 
292 class CirctClockInverterConverter
293  : public IntrinsicOpConverter<ClockInverterIntrinsicOp> {
294 public:
295  using IntrinsicOpConverter::IntrinsicOpConverter;
296 
297  bool check(GenericIntrinsic gi) override {
298  return gi.hasNInputs(1) || gi.typedInput<ClockType>(0) ||
299  gi.typedOutput<ClockType>() || gi.hasNParam(0);
300  }
301 };
302 
303 class CirctClockDividerConverter : public IntrinsicConverter {
304 public:
305  using IntrinsicConverter::IntrinsicConverter;
306 
307  bool check(GenericIntrinsic gi) override {
308  return gi.hasNInputs(1) || gi.typedInput<ClockType>(0) ||
309  gi.typedOutput<ClockType>() || gi.namedIntParam("POW_2") ||
310  gi.hasNParam(1);
311  }
312 
313  void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
314  PatternRewriter &rewriter) override {
315  auto pow2 =
316  gi.getParamValue<IntegerAttr>("POW_2").getValue().getZExtValue();
317 
318  auto pow2Attr = rewriter.getI64IntegerAttr(pow2);
319 
320  rewriter.replaceOpWithNewOp<ClockDividerIntrinsicOp>(
321  gi.op, adaptor.getOperands()[0], pow2Attr);
322  }
323 };
324 
325 template <typename OpTy>
326 class CirctLTLBinaryConverter : public IntrinsicOpConverter<OpTy> {
327 public:
329 
330  bool check(GenericIntrinsic gi) override {
331  return gi.hasNInputs(2) || gi.sizedInput<UIntType>(0, 1) ||
332  gi.sizedInput<UIntType>(1, 1) || gi.sizedOutput<UIntType>(1) ||
333  gi.hasNParam(0);
334  }
335 };
336 
337 template <typename OpTy>
338 class CirctLTLUnaryConverter : public IntrinsicOpConverter<OpTy> {
339 public:
341 
342  bool check(GenericIntrinsic gi) override {
343  return gi.hasNInputs(1) || gi.sizedInput<UIntType>(0, 1) ||
344  gi.sizedOutput<UIntType>(1) || gi.hasNParam(0);
345  }
346 };
347 
348 class CirctLTLDelayConverter : public IntrinsicConverter {
349 public:
350  using IntrinsicConverter::IntrinsicConverter;
351 
352  bool check(GenericIntrinsic gi) override {
353  return gi.hasNInputs(1) || gi.sizedInput<UIntType>(0, 1) ||
354  gi.sizedOutput<UIntType>(1) || gi.namedIntParam("delay") ||
355  gi.namedIntParam("length", true) || gi.hasNParam(1, 1);
356  }
357 
358  void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
359  PatternRewriter &rewriter) override {
360  auto getI64Attr = [&](IntegerAttr val) {
361  if (!val)
362  return IntegerAttr();
363  return rewriter.getI64IntegerAttr(val.getValue().getZExtValue());
364  };
365  auto delay = getI64Attr(gi.getParamValue<IntegerAttr>("delay"));
366  auto length = getI64Attr(gi.getParamValue<IntegerAttr>("length"));
367  rewriter.replaceOpWithNewOp<LTLDelayIntrinsicOp>(
368  gi.op, gi.op.getResultTypes(), adaptor.getOperands()[0], delay, length);
369  }
370 };
371 
372 class CirctLTLClockConverter
373  : public IntrinsicOpConverter<LTLClockIntrinsicOp> {
374 public:
375  using IntrinsicOpConverter::IntrinsicOpConverter;
376 
377  bool check(GenericIntrinsic gi) override {
378  return gi.hasNInputs(2) || gi.sizedInput<UIntType>(0, 1) ||
379  gi.typedInput<ClockType>(1) || gi.sizedOutput<UIntType>(1) ||
380  gi.hasNParam(0);
381  }
382 };
383 
384 class CirctLTLRepeatConverter : public IntrinsicConverter {
385 public:
386  using IntrinsicConverter::IntrinsicConverter;
387 
388  bool check(GenericIntrinsic gi) override {
389  return gi.hasNInputs(1) || gi.sizedInput<UIntType>(0, 1) ||
390  gi.sizedOutput<UIntType>(1) || gi.namedIntParam("base") ||
391  gi.namedIntParam("more", true) || gi.hasNParam(1, 1);
392  }
393 
394  void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
395  PatternRewriter &rewriter) override {
396  auto getI64Attr = [&](IntegerAttr val) {
397  if (!val)
398  return IntegerAttr();
399  return rewriter.getI64IntegerAttr(val.getValue().getZExtValue());
400  };
401  auto base = getI64Attr(gi.getParamValue<IntegerAttr>("base"));
402  auto more = getI64Attr(gi.getParamValue<IntegerAttr>("more"));
403  rewriter.replaceOpWithNewOp<LTLRepeatIntrinsicOp>(
404  gi.op, gi.op.getResultTypes(), adaptor.getOperands()[0], base, more);
405  }
406 };
407 
408 class CirctLTLGoToRepeatConverter : public IntrinsicConverter {
409 public:
410  using IntrinsicConverter::IntrinsicConverter;
411 
412  bool check(GenericIntrinsic gi) override {
413  return gi.hasNInputs(1) || gi.sizedInput<UIntType>(0, 1) ||
414  gi.sizedOutput<UIntType>(1) || gi.namedIntParam("base") ||
415  gi.namedIntParam("more") || gi.hasNParam(1, 1);
416  }
417 
418  void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
419  PatternRewriter &rewriter) override {
420  auto getI64Attr = [&](IntegerAttr val) {
421  if (!val)
422  return IntegerAttr();
423  return rewriter.getI64IntegerAttr(val.getValue().getZExtValue());
424  };
425  auto base = getI64Attr(gi.getParamValue<IntegerAttr>("base"));
426  auto more = getI64Attr(gi.getParamValue<IntegerAttr>("more"));
427  rewriter.replaceOpWithNewOp<LTLGoToRepeatIntrinsicOp>(
428  gi.op, gi.op.getResultTypes(), adaptor.getOperands()[0], base, more);
429  }
430 };
431 
432 class CirctLTLNonConsecutiveRepeatConverter : public IntrinsicConverter {
433 public:
434  using IntrinsicConverter::IntrinsicConverter;
435 
436  bool check(GenericIntrinsic gi) override {
437  return gi.hasNInputs(1) || gi.sizedInput<UIntType>(0, 1) ||
438  gi.sizedOutput<UIntType>(1) || gi.namedIntParam("base") ||
439  gi.namedIntParam("more") || gi.hasNParam(1, 1);
440  }
441 
442  void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
443  PatternRewriter &rewriter) override {
444  auto getI64Attr = [&](IntegerAttr val) {
445  if (!val)
446  return IntegerAttr();
447  return rewriter.getI64IntegerAttr(val.getValue().getZExtValue());
448  };
449  auto base = getI64Attr(gi.getParamValue<IntegerAttr>("base"));
450  auto more = getI64Attr(gi.getParamValue<IntegerAttr>("more"));
451  rewriter.replaceOpWithNewOp<LTLNonConsecutiveRepeatIntrinsicOp>(
452  gi.op, gi.op.getResultTypes(), adaptor.getOperands()[0], base, more);
453  }
454 };
455 
456 template <class Op>
457 class CirctVerifConverter : public IntrinsicConverter {
458 public:
459  using IntrinsicConverter::IntrinsicConverter;
460 
461  bool check(GenericIntrinsic gi) override {
462  return gi.hasNInputs(1, 2) || gi.sizedInput<UIntType>(0, 1) ||
463  gi.namedParam("label", true) || gi.hasNParam(0, 1) ||
464  gi.hasNoOutput();
465  }
466 
467  void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
468  PatternRewriter &rewriter) override {
469  auto label = gi.getParamValue<StringAttr>("label");
470  auto operands = adaptor.getOperands();
471 
472  // Check if an enable was provided
473  Value enable;
474  if (gi.getNumInputs() == 2)
475  enable = operands[1];
476 
477  rewriter.replaceOpWithNewOp<Op>(gi.op, operands[0], enable, label);
478  }
479 };
480 
481 class CirctMux2CellConverter : public IntrinsicConverter {
482  using IntrinsicConverter::IntrinsicConverter;
483 
484  bool check(GenericIntrinsic gi) override {
485  return gi.hasNInputs(3) || gi.typedInput<UIntType>(0) || gi.hasNParam(0) ||
486  gi.hasOutput();
487  }
488 
489  void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
490  PatternRewriter &rewriter) override {
491  auto operands = adaptor.getOperands();
492  rewriter.replaceOpWithNewOp<Mux2CellIntrinsicOp>(gi.op, operands[0],
493  operands[1], operands[2]);
494  }
495 };
496 
497 class CirctMux4CellConverter : public IntrinsicConverter {
498  using IntrinsicConverter::IntrinsicConverter;
499 
500  bool check(GenericIntrinsic gi) override {
501  return gi.hasNInputs(5) || gi.typedInput<UIntType>(0) || gi.hasNParam(0) ||
502  gi.hasOutput();
503  }
504 
505  void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
506  PatternRewriter &rewriter) override {
507  auto operands = adaptor.getOperands();
508  rewriter.replaceOpWithNewOp<Mux4CellIntrinsicOp>(
509  gi.op, operands[0], operands[1], operands[2], operands[3], operands[4]);
510  }
511 };
512 
513 class CirctHasBeenResetConverter
514  : public IntrinsicOpConverter<HasBeenResetIntrinsicOp> {
515 public:
516  using IntrinsicOpConverter::IntrinsicOpConverter;
517 
518  bool check(GenericIntrinsic gi) override {
519  return gi.hasNInputs(2) || gi.typedInput<ClockType>(0) ||
520  gi.hasResetInput(1) || gi.sizedOutput<UIntType>(1) ||
521  gi.hasNParam(0);
522  }
523 };
524 
525 class CirctProbeConverter : public IntrinsicOpConverter<FPGAProbeIntrinsicOp> {
526 public:
527  using IntrinsicOpConverter::IntrinsicOpConverter;
528 
529  bool check(GenericIntrinsic gi) override {
530  return gi.hasNInputs(2) || gi.typedInput<ClockType>(1) || gi.hasNParam(0) ||
531  gi.hasNoOutput();
532  }
533 };
534 
535 template <class OpTy, bool ifElseFatal = false>
536 class CirctAssertConverter : public IntrinsicConverter {
537 public:
538  using IntrinsicConverter::IntrinsicConverter;
539 
540  bool check(GenericIntrinsic gi) override {
541  return gi.typedInput<ClockType>(0) || gi.sizedInput<UIntType>(1, 1) ||
542  gi.sizedInput<UIntType>(2, 1) ||
543  gi.namedParam("format", /*optional=*/true) ||
544  gi.namedParam("label", /*optional=*/true) ||
545  gi.namedParam("guards", /*optional=*/true) || gi.hasNParam(0, 3) ||
546  gi.hasNoOutput();
547  }
548 
549  void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
550  PatternRewriter &rewriter) override {
551  auto format = gi.getParamValue<StringAttr>("format");
552  auto label = gi.getParamValue<StringAttr>("label");
553  auto guards = gi.getParamValue<StringAttr>("guards");
554 
555  auto clock = adaptor.getOperands()[0];
556  auto predicate = adaptor.getOperands()[1];
557  auto enable = adaptor.getOperands()[2];
558 
559  auto substitutions = adaptor.getOperands().drop_front(3);
560  auto name = label ? label.strref() : "";
561  // Message is not optional, so provide empty string if not present.
562  auto message = format ? format : rewriter.getStringAttr("");
563  auto op = rewriter.template replaceOpWithNewOp<OpTy>(
564  gi.op, clock, predicate, enable, message, substitutions, name,
565  /*isConcurrent=*/true);
566  if (guards) {
567  SmallVector<StringRef> guardStrings;
568  guards.strref().split(guardStrings, ';', /*MaxSplit=*/-1,
569  /*KeepEmpty=*/false);
570  rewriter.startOpModification(op);
571  op->setAttr("guards", rewriter.getStrArrayAttr(guardStrings));
572  rewriter.finalizeOpModification(op);
573  }
574 
575  if constexpr (ifElseFatal) {
576  rewriter.startOpModification(op);
577  op->setAttr("format", rewriter.getStringAttr("ifElseFatal"));
578  rewriter.finalizeOpModification(op);
579  }
580  }
581 };
582 
583 class CirctCoverConverter : public IntrinsicConverter {
584 public:
585  using IntrinsicConverter::IntrinsicConverter;
586 
587  bool check(GenericIntrinsic gi) override {
588  return gi.hasNInputs(3) || gi.hasNoOutput() ||
589  gi.typedInput<ClockType>(0) || gi.sizedInput<UIntType>(1, 1) ||
590  gi.sizedInput<UIntType>(2, 1) ||
591  gi.namedParam("label", /*optional=*/true) ||
592  gi.namedParam("guards", /*optional=*/true) || gi.hasNParam(0, 2);
593  }
594 
595  void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
596  PatternRewriter &rewriter) override {
597  auto label = gi.getParamValue<StringAttr>("label");
598  auto guards = gi.getParamValue<StringAttr>("guards");
599 
600  auto clock = adaptor.getOperands()[0];
601  auto predicate = adaptor.getOperands()[1];
602  auto enable = adaptor.getOperands()[2];
603 
604  auto name = label ? label.strref() : "";
605  // Empty message string for cover, only 'name' / label.
606  auto message = rewriter.getStringAttr("");
607  auto op = rewriter.replaceOpWithNewOp<CoverOp>(
608  gi.op, clock, predicate, enable, message, ValueRange{}, name,
609  /*isConcurrent=*/true);
610  if (guards) {
611  SmallVector<StringRef> guardStrings;
612  guards.strref().split(guardStrings, ';', /*MaxSplit=*/-1,
613  /*KeepEmpty=*/false);
614  rewriter.startOpModification(op);
615  op->setAttr("guards", rewriter.getStrArrayAttr(guardStrings));
616  rewriter.finalizeOpModification(op);
617  }
618  }
619 };
620 
621 class CirctUnclockedAssumeConverter : public IntrinsicConverter {
622 public:
623  using IntrinsicConverter::IntrinsicConverter;
624 
625  bool check(GenericIntrinsic gi) override {
626  return gi.sizedInput<UIntType>(0, 1) || gi.sizedInput<UIntType>(1, 1) ||
627  gi.namedParam("format", /*optional=*/true) ||
628  gi.namedParam("label", /*optional=*/true) ||
629  gi.namedParam("guards", /*optional=*/true) || gi.hasNParam(0, 3) ||
630  gi.hasNoOutput();
631  }
632 
633  void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
634  PatternRewriter &rewriter) override {
635  auto format = gi.getParamValue<StringAttr>("format");
636  auto label = gi.getParamValue<StringAttr>("label");
637  auto guards = gi.getParamValue<StringAttr>("guards");
638 
639  auto predicate = adaptor.getOperands()[0];
640  auto enable = adaptor.getOperands()[1];
641 
642  auto substitutions = adaptor.getOperands().drop_front(2);
643  auto name = label ? label.strref() : "";
644  // Message is not optional, so provide empty string if not present.
645  auto message = format ? format : rewriter.getStringAttr("");
646  auto op = rewriter.template replaceOpWithNewOp<UnclockedAssumeIntrinsicOp>(
647  gi.op, predicate, enable, message, substitutions, name);
648  if (guards) {
649  SmallVector<StringRef> guardStrings;
650  guards.strref().split(guardStrings, ';', /*MaxSplit=*/-1,
651  /*KeepEmpty=*/false);
652  rewriter.startOpModification(op);
653  op->setAttr("guards", rewriter.getStrArrayAttr(guardStrings));
654  rewriter.finalizeOpModification(op);
655  }
656  }
657 };
658 
659 class CirctDPICallConverter : public IntrinsicConverter {
660  static bool getIsClocked(GenericIntrinsic gi) {
661  return !gi.getParamValue<IntegerAttr>("isClocked").getValue().isZero();
662  }
663 
664 public:
665  using IntrinsicConverter::IntrinsicConverter;
666 
667  bool check(GenericIntrinsic gi) override {
668  if (gi.hasNParam(2, 2) || gi.namedIntParam("isClocked") ||
669  gi.namedParam("functionName") ||
670  gi.namedParam("inputNames", /*optional=*/true) ||
671  gi.namedParam("outputName", /*optional=*/true))
672  return true;
673  auto isClocked = getIsClocked(gi);
674  // If clocked, the first operand must be a clock.
675  if (isClocked && gi.typedInput<ClockType>(0))
676  return true;
677  // Enable must be UInt<1>.
678  if (gi.sizedInput<UIntType>(isClocked, 1))
679  return true;
680 
681  return false;
682  }
683 
684  void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
685  PatternRewriter &rewriter) override {
686  auto isClocked = getIsClocked(gi);
687  auto functionName = gi.getParamValue<StringAttr>("functionName");
688  ArrayAttr inputNamesStrArray;
689  StringAttr outputStr = gi.getParamValue<StringAttr>("outputName");
690  if (auto inputNames = gi.getParamValue<StringAttr>("inputNames")) {
691  SmallVector<StringRef> inputNamesTemporary;
692  inputNames.strref().split(inputNamesTemporary, ';', /*MaxSplit=*/-1,
693  /*KeepEmpty=*/false);
694  inputNamesStrArray = rewriter.getStrArrayAttr(inputNamesTemporary);
695  }
696  // Clock and enable are optional.
697  Value clock = isClocked ? adaptor.getOperands()[0] : Value();
698  Value enable = adaptor.getOperands()[static_cast<size_t>(isClocked)];
699 
700  auto inputs =
701  adaptor.getOperands().drop_front(static_cast<size_t>(isClocked) + 1);
702 
703  rewriter.replaceOpWithNewOp<DPICallIntrinsicOp>(
704  gi.op, gi.op.getResultTypes(), functionName, inputNamesStrArray,
705  outputStr, clock, enable, inputs);
706  }
707 };
708 
709 } // namespace
710 
711 //===----------------------------------------------------------------------===//
712 // FIRRTL intrinsic lowering dialect interface
713 //===----------------------------------------------------------------------===//
714 
716  IntrinsicLowerings &lowering) const {
717  lowering.add<CirctSizeofConverter>("circt.sizeof", "circt_sizeof");
718  lowering.add<CirctIsXConverter>("circt.isX", "circt_isX");
719  lowering.add<CirctPlusArgTestConverter>("circt.plusargs.test",
720  "circt_plusargs_test");
721  lowering.add<CirctPlusArgValueConverter>("circt.plusargs.value",
722  "circt_plusargs_value");
723  lowering.add<CirctClockGateConverter>("circt.clock_gate", "circt_clock_gate");
724  lowering.add<CirctClockInverterConverter>("circt.clock_inv",
725  "circt_clock_inv");
726  lowering.add<CirctClockDividerConverter>("circt.clock_div",
727  "circt_clock_div");
728  lowering.add<CirctLTLBinaryConverter<LTLAndIntrinsicOp>>("circt.ltl.and",
729  "circt_ltl_and");
730  lowering.add<CirctLTLBinaryConverter<LTLOrIntrinsicOp>>("circt.ltl.or",
731  "circt_ltl_or");
732  lowering.add<CirctLTLBinaryConverter<LTLIntersectIntrinsicOp>>(
733  "circt.ltl.intersect", "circt_ltl_intersect");
734  lowering.add<CirctLTLBinaryConverter<LTLConcatIntrinsicOp>>(
735  "circt.ltl.concat", "circt_ltl_concat");
736  lowering.add<CirctLTLBinaryConverter<LTLImplicationIntrinsicOp>>(
737  "circt.ltl.implication", "circt_ltl_implication");
738  lowering.add<CirctLTLBinaryConverter<LTLUntilIntrinsicOp>>("circt.ltl.until",
739  "circt_ltl_until");
740  lowering.add<CirctLTLUnaryConverter<LTLNotIntrinsicOp>>("circt.ltl.not",
741  "circt_ltl_not");
742  lowering.add<CirctLTLUnaryConverter<LTLEventuallyIntrinsicOp>>(
743  "circt.ltl.eventually", "circt_ltl_eventually");
744 
745  lowering.add<CirctLTLDelayConverter>("circt.ltl.delay", "circt_ltl_delay");
746  lowering.add<CirctLTLRepeatConverter>("circt.ltl.repeat", "circt_ltl_repeat");
747  lowering.add<CirctLTLGoToRepeatConverter>("circt.ltl.goto_repeat",
748  "circt_ltl_goto_repeat");
749  lowering.add<CirctLTLNonConsecutiveRepeatConverter>(
750  "circt.ltl.non_consecutive_repeat", "circt_ltl_non_consecutive_repeat");
751  lowering.add<CirctLTLClockConverter>("circt.ltl.clock", "circt_ltl_clock");
752 
753  lowering.add<CirctVerifConverter<VerifAssertIntrinsicOp>>(
754  "circt.verif.assert", "circt_verif_assert");
755  lowering.add<CirctVerifConverter<VerifAssumeIntrinsicOp>>(
756  "circt.verif.assume", "circt_verif_assume");
757  lowering.add<CirctVerifConverter<VerifCoverIntrinsicOp>>("circt.verif.cover",
758  "circt_verif_cover");
759  lowering.add<CirctMux2CellConverter>("circt.mux2cell", "circt_mux2cell");
760  lowering.add<CirctMux4CellConverter>("circt.mux4cell", "circt_mux4cell");
761  lowering.add<CirctHasBeenResetConverter>("circt.has_been_reset",
762  "circt_has_been_reset");
763  lowering.add<CirctProbeConverter>("circt.fpga_probe", "circt_fpga_probe");
764  lowering.add<CirctAssertConverter<AssertOp>>("circt.chisel_assert",
765  "circt_chisel_assert");
766  lowering.add<CirctAssertConverter<AssertOp, /*ifElseFatal=*/true>>(
767  "circt.chisel_ifelsefatal", "circt_chisel_ifelsefatal");
768  lowering.add<CirctAssertConverter<AssumeOp>>("circt.chisel_assume",
769  "circt_chisel_assume");
770  lowering.add<CirctCoverConverter>("circt.chisel_cover", "circt_chisel_cover");
771  lowering.add<CirctUnclockedAssumeConverter>("circt.unclocked_assume",
772  "circt_unclocked_assume");
773  lowering.add<CirctDPICallConverter>("circt.dpi_call", "circt_dpi_call");
774 }
Base class for Intrinsic Converters.
Lowering helper which collects all intrinsic converters.
FailureOr< size_t > lower(FModuleOp mod, bool allowUnknownIntrinsics=false)
Lowers all intrinsics in a module. Returns number converted or failure.
llvm::DenseMap< StringAttr, std::unique_ptr< IntrinsicConverter > > ConversionMapTy
void add(Args... args)
Registers a converter to one or more intrinsic names.
MLIRContext * context
Reference to the MLIR context.
ConversionMapTy conversions
Mapping from intrinsic names to converters.
bool areTypesEquivalent(FIRRTLType destType, FIRRTLType srcType, bool destOuterTypeIsConst=false, bool srcOuterTypeIsConst=false, bool requireSameWidths=false)
Returns whether the two types are equivalent.
bool isTypeLarger(FIRRTLBaseType dstType, FIRRTLBaseType srcType)
Returns true if the destination is at least as wide as a source.
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
Definition: FIRRTLUtils.cpp:25
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
void populateIntrinsicLowerings(IntrinsicLowerings &lowerings) const override
Helper class for checking and extracting information from the generic instrinsic op.
ParseResult sizedInput(unsigned n, int32_t size)
mlir::TypedValue< BundleType > getOutputBundle()
T getParamValue(StringRef name)
Get parameter value by name, if present, as requested type.
ParseResult hasResetInput(unsigned n)
ParseResult typedInput(unsigned n)
ParseResult hasNOutputElements(unsigned n)
InFlightDiagnostic emitError()
ParseResult namedIntParam(StringRef paramName, bool optional=false)
ParseResult namedParam(StringRef paramName, bool optional=false)
ParseResult sizedOutput(int32_t size)
ParseResult sizedOutputElement(unsigned n, StringRef name, int32_t size)
ParseResult hasNParam(unsigned n, unsigned c=0)
ParseResult hasOutputElement(unsigned n, StringRef name)
ParseResult hasNInputs(unsigned n, unsigned c=0)
A dialect interface to provide lowering conversions.
virtual void populateIntrinsicLowerings(IntrinsicLowerings &lowerings) const =0
void populateIntrinsicLowerings(IntrinsicLowerings &lowerings) const