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