CIRCT 22.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
14#include "circt/Support/JSON.h"
15#include "mlir/Transforms/DialectConversion.h"
16#include "llvm/Support/JSON.h"
17
18using namespace circt;
19using namespace firrtl;
20
21// vtable anchor
23
24//===----------------------------------------------------------------------===//
25// GenericIntrinsic
26//===----------------------------------------------------------------------===//
27
28// Checks for a number of operands between n and n+c (allows for c optional
29// inputs)
30ParseResult GenericIntrinsic::hasNInputs(unsigned n, unsigned c) {
31 auto numOps = op.getNumOperands();
32 unsigned m = n + c;
33 if (numOps < n || numOps > m) {
34 auto err = emitError() << " has " << numOps << " inputs instead of ";
35 if (c == 0)
36 err << n;
37 else
38 err << " between " << n << " and " << m;
39 return failure();
40 }
41 return success();
42}
43
44// Accessor method for the number of inputs
45unsigned GenericIntrinsic::getNumInputs() { return op.getNumOperands(); }
46
47ParseResult GenericIntrinsic::hasNOutputElements(unsigned n) {
48 auto b = getOutputBundle();
49 if (!b)
50 return emitError() << " missing output bundle";
51 if (b.getType().getNumElements() != n)
52 return emitError() << " has " << b.getType().getNumElements()
53 << " output elements instead of " << n;
54 return success();
55}
56
57ParseResult GenericIntrinsic::hasNParam(unsigned n, unsigned c) {
58 unsigned num = 0;
59 if (op.getParameters())
60 num = op.getParameters().size();
61 if (num < n || num > n + c) {
62 auto d = emitError() << " has " << num << " parameters instead of ";
63 if (c == 0)
64 d << n;
65 else
66 d << " between " << n << " and " << (n + c);
67 return failure();
68 }
69 return success();
70}
71
72ParseResult GenericIntrinsic::namedParam(StringRef paramName, bool optional) {
73 for (auto a : op.getParameters()) {
74 auto param = cast<ParamDeclAttr>(a);
75 if (param.getName().getValue() == paramName) {
76 if (isa<StringAttr>(param.getValue()))
77 return success();
78
79 return emitError() << " has parameter '" << param.getName()
80 << "' which should be a string but is not";
81 }
82 }
83 if (optional)
84 return success();
85 return emitError() << " is missing parameter " << paramName;
86}
87
88ParseResult GenericIntrinsic::namedIntParam(StringRef paramName,
89 bool optional) {
90 for (auto a : op.getParameters()) {
91 auto param = cast<ParamDeclAttr>(a);
92 if (param.getName().getValue() == paramName) {
93 if (isa<IntegerAttr>(param.getValue()))
94 return success();
95
96 return emitError() << " has parameter '" << param.getName()
97 << "' which should be an integer but is not";
98 }
99 }
100 if (optional)
101 return success();
102 return emitError() << " is missing parameter " << paramName;
103}
104
105//===----------------------------------------------------------------------===//
106// IntrinsicOpConversion
107//===----------------------------------------------------------------------===//
108
109/// Conversion pattern adaptor dispatching via generic intrinsic name.
110namespace {
111class IntrinsicOpConversion final
112 : public OpConversionPattern<GenericIntrinsicOp> {
113public:
114 using ConversionMapTy = IntrinsicLowerings::ConversionMapTy;
115
116 IntrinsicOpConversion(TypeConverter &typeConverter, MLIRContext *context,
117 const ConversionMapTy &conversions,
118 size_t &numConversions,
119 bool allowUnknownIntrinsics = false)
120 : OpConversionPattern(typeConverter, context), conversions(conversions),
121 numConversions(numConversions),
122 allowUnknownIntrinsics(allowUnknownIntrinsics) {}
123
124 LogicalResult
125 matchAndRewrite(GenericIntrinsicOp op, OpAdaptor adaptor,
126 ConversionPatternRewriter &rewriter) const override {
127
128 auto it = conversions.find(op.getIntrinsicAttr());
129 if (it == conversions.end()) {
130 if (!allowUnknownIntrinsics)
131 return op.emitError("unknown intrinsic ") << op.getIntrinsicAttr();
132 return failure();
133 }
134
135 auto &conv = *it->second;
136 auto result = conv.checkAndConvert(GenericIntrinsic(op), adaptor, rewriter);
137 if (succeeded(result))
138 ++numConversions;
139 return result;
140 }
141
142private:
143 const ConversionMapTy &conversions;
144 size_t &numConversions;
145 const bool allowUnknownIntrinsics;
146};
147} // namespace
148
149//===----------------------------------------------------------------------===//
150// IntrinsicLowerings
151//===----------------------------------------------------------------------===//
152
153FailureOr<size_t> IntrinsicLowerings::lower(FModuleOp mod,
154 bool allowUnknownIntrinsics) {
155
156 ConversionTarget target(*context);
157
158 target.markUnknownOpDynamicallyLegal([](Operation *op) { return true; });
159 if (allowUnknownIntrinsics)
160 target.addDynamicallyLegalOp<GenericIntrinsicOp>(
161 [this](GenericIntrinsicOp op) {
162 return !conversions.contains(op.getIntrinsicAttr());
163 });
164 else
165 target.addIllegalOp<GenericIntrinsicOp>();
166
167 // Automatically insert wires + connect for compatible FIRRTL base types.
168 // For now, this is not customizable/extendable.
169 TypeConverter typeConverter;
170 typeConverter.addConversion([](Type type) { return type; });
171 auto firrtlBaseTypeMaterialization =
172 [](OpBuilder &builder, FIRRTLBaseType resultType, ValueRange inputs,
173 Location loc) -> Value {
174 if (inputs.size() != 1)
175 return {};
176 auto inputType = type_dyn_cast<FIRRTLBaseType>(inputs.front().getType());
177 if (!inputType)
178 return {};
179
180 if (!areTypesEquivalent(resultType, inputType) ||
181 !isTypeLarger(resultType, inputType))
182 return {};
183
184 auto w = WireOp::create(builder, loc, resultType).getResult();
185 emitConnect(builder, loc, w, inputs.front());
186 return w;
187 };
188 // New result doesn't match? Add wire + connect.
189 typeConverter.addSourceMaterialization(firrtlBaseTypeMaterialization);
190 // New operand doesn't match? Add wire + connect.
191 typeConverter.addTargetMaterialization(firrtlBaseTypeMaterialization);
192
193 RewritePatternSet patterns(context);
194 size_t count = 0;
195 patterns.add<IntrinsicOpConversion>(typeConverter, context, conversions,
196 count, allowUnknownIntrinsics);
197
198 if (failed(mlir::applyPartialConversion(mod, target, std::move(patterns))))
199 return failure();
200
201 return count;
202}
203
204//===----------------------------------------------------------------------===//
205// IntrinsicLoweringInterfaceCollection
206//===----------------------------------------------------------------------===//
207
209 IntrinsicLowerings &lowering) const {
210 for (const IntrinsicLoweringDialectInterface &interface : *this)
211 interface.populateIntrinsicLowerings(lowering);
212}
213
214//===----------------------------------------------------------------------===//
215// FIRRTL intrinsic lowering converters
216//===----------------------------------------------------------------------===//
217
218namespace {
219
220class CirctSizeofConverter : public IntrinsicOpConverter<SizeOfIntrinsicOp> {
221public:
222 using IntrinsicOpConverter::IntrinsicOpConverter;
223
224 bool check(GenericIntrinsic gi) override {
225 return gi.hasNInputs(1) || gi.sizedOutput<UIntType>(32) || gi.hasNParam(0);
226 }
227};
228
229class CirctIsXConverter : public IntrinsicOpConverter<IsXIntrinsicOp> {
230public:
231 using IntrinsicOpConverter::IntrinsicOpConverter;
232
233 bool check(GenericIntrinsic gi) override {
234 return gi.hasNInputs(1) || gi.sizedOutput<UIntType>(1) || gi.hasNParam(0);
235 }
236};
237
238class CirctPlusArgTestConverter : public IntrinsicConverter {
239public:
240 using IntrinsicConverter::IntrinsicConverter;
241
242 bool check(GenericIntrinsic gi) override {
243 return gi.hasNInputs(0) || gi.sizedOutput<UIntType>(1) ||
244 gi.namedParam("FORMAT") || gi.hasNParam(1);
245 }
246
247 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
248 PatternRewriter &rewriter) override {
249 rewriter.replaceOpWithNewOp<PlusArgsTestIntrinsicOp>(
250 gi.op, gi.getParamValue<StringAttr>("FORMAT"));
251 }
252};
253
254class CirctPlusArgValueConverter : public IntrinsicConverter {
255public:
256 using IntrinsicConverter::IntrinsicConverter;
257
258 bool check(GenericIntrinsic gi) override {
259 return gi.hasNOutputElements(2) ||
260 gi.sizedOutputElement<UIntType>(0, "found", 1) ||
261 gi.hasOutputElement(1, "result") || gi.namedParam("FORMAT") ||
262 gi.hasNParam(1);
263 }
264
265 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
266 PatternRewriter &rewriter) override {
267 auto bty = gi.getOutputBundle().getType();
268 auto newop = PlusArgsValueIntrinsicOp::create(
269 rewriter, gi.op.getLoc(), bty.getElementTypePreservingConst(0),
270 bty.getElementTypePreservingConst(1),
271 gi.getParamValue<StringAttr>("FORMAT"));
272 rewriter.replaceOpWithNewOp<BundleCreateOp>(
273 gi.op, bty, ValueRange({newop.getFound(), newop.getResult()}));
274 }
275};
276
277class CirctClockGateConverter
278 : public IntrinsicOpConverter<ClockGateIntrinsicOp> {
279public:
280 using IntrinsicOpConverter::IntrinsicOpConverter;
281
282 bool check(GenericIntrinsic gi) override {
283 if (gi.op.getNumOperands() == 3) {
284 return gi.typedInput<ClockType>(0) || gi.sizedInput<UIntType>(1, 1) ||
285 gi.sizedInput<UIntType>(2, 1) || gi.typedOutput<ClockType>() ||
286 gi.hasNParam(0);
287 }
288 if (gi.op.getNumOperands() == 2) {
289 return gi.typedInput<ClockType>(0) || gi.sizedInput<UIntType>(1, 1) ||
290 gi.typedOutput<ClockType>() || gi.hasNParam(0);
291 }
292 gi.emitError() << " has " << gi.op.getNumOperands()
293 << " ports instead of 3 or 4";
294 return true;
295 }
296};
297
298class CirctClockInverterConverter
299 : public IntrinsicOpConverter<ClockInverterIntrinsicOp> {
300public:
301 using IntrinsicOpConverter::IntrinsicOpConverter;
302
303 bool check(GenericIntrinsic gi) override {
304 return gi.hasNInputs(1) || gi.typedInput<ClockType>(0) ||
305 gi.typedOutput<ClockType>() || gi.hasNParam(0);
306 }
307};
308
309class CirctClockDividerConverter : public IntrinsicConverter {
310public:
311 using IntrinsicConverter::IntrinsicConverter;
312
313 bool check(GenericIntrinsic gi) override {
314 return gi.hasNInputs(1) || gi.typedInput<ClockType>(0) ||
315 gi.typedOutput<ClockType>() || gi.namedIntParam("POW_2") ||
316 gi.hasNParam(1);
317 }
318
319 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
320 PatternRewriter &rewriter) override {
321 auto pow2 =
322 gi.getParamValue<IntegerAttr>("POW_2").getValue().getZExtValue();
323
324 auto pow2Attr = rewriter.getI64IntegerAttr(pow2);
325
326 rewriter.replaceOpWithNewOp<ClockDividerIntrinsicOp>(
327 gi.op, adaptor.getOperands()[0], pow2Attr);
328 }
329};
330
331template <typename OpTy>
332class CirctLTLBinaryConverter : public IntrinsicOpConverter<OpTy> {
333public:
335
336 bool check(GenericIntrinsic gi) override {
337 return gi.hasNInputs(2) || gi.sizedInput<UIntType>(0, 1) ||
338 gi.sizedInput<UIntType>(1, 1) || gi.sizedOutput<UIntType>(1) ||
339 gi.hasNParam(0);
340 }
341};
342
343template <typename OpTy>
344class CirctLTLUnaryConverter : public IntrinsicOpConverter<OpTy> {
345public:
347
348 bool check(GenericIntrinsic gi) override {
349 return gi.hasNInputs(1) || gi.sizedInput<UIntType>(0, 1) ||
350 gi.sizedOutput<UIntType>(1) || gi.hasNParam(0);
351 }
352};
353
354class CirctLTLDelayConverter : public IntrinsicConverter {
355public:
356 using IntrinsicConverter::IntrinsicConverter;
357
358 bool check(GenericIntrinsic gi) override {
359 return gi.hasNInputs(1) || gi.sizedInput<UIntType>(0, 1) ||
360 gi.sizedOutput<UIntType>(1) || gi.namedIntParam("delay") ||
361 gi.namedIntParam("length", true) || gi.hasNParam(1, 1);
362 }
363
364 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
365 PatternRewriter &rewriter) override {
366 auto getI64Attr = [&](IntegerAttr val) {
367 if (!val)
368 return IntegerAttr();
369 return rewriter.getI64IntegerAttr(val.getValue().getZExtValue());
370 };
371 auto delay = getI64Attr(gi.getParamValue<IntegerAttr>("delay"));
372 auto length = getI64Attr(gi.getParamValue<IntegerAttr>("length"));
373 rewriter.replaceOpWithNewOp<LTLDelayIntrinsicOp>(
374 gi.op, gi.op.getResultTypes(), adaptor.getOperands()[0], delay, length);
375 }
376};
377
378class CirctLTLClockConverter
379 : public IntrinsicOpConverter<LTLClockIntrinsicOp> {
380public:
381 using IntrinsicOpConverter::IntrinsicOpConverter;
382
383 bool check(GenericIntrinsic gi) override {
384 return gi.hasNInputs(2) || gi.sizedInput<UIntType>(0, 1) ||
385 gi.typedInput<ClockType>(1) || gi.sizedOutput<UIntType>(1) ||
386 gi.hasNParam(0);
387 }
388};
389
390class CirctLTLRepeatConverter : public IntrinsicConverter {
391public:
392 using IntrinsicConverter::IntrinsicConverter;
393
394 bool check(GenericIntrinsic gi) override {
395 return gi.hasNInputs(1) || gi.sizedInput<UIntType>(0, 1) ||
396 gi.sizedOutput<UIntType>(1) || gi.namedIntParam("base") ||
397 gi.namedIntParam("more", true) || gi.hasNParam(1, 1);
398 }
399
400 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
401 PatternRewriter &rewriter) override {
402 auto getI64Attr = [&](IntegerAttr val) {
403 if (!val)
404 return IntegerAttr();
405 return rewriter.getI64IntegerAttr(val.getValue().getZExtValue());
406 };
407 auto base = getI64Attr(gi.getParamValue<IntegerAttr>("base"));
408 auto more = getI64Attr(gi.getParamValue<IntegerAttr>("more"));
409 rewriter.replaceOpWithNewOp<LTLRepeatIntrinsicOp>(
410 gi.op, gi.op.getResultTypes(), adaptor.getOperands()[0], base, more);
411 }
412};
413
414class CirctLTLGoToRepeatConverter : public IntrinsicConverter {
415public:
416 using IntrinsicConverter::IntrinsicConverter;
417
418 bool check(GenericIntrinsic gi) override {
419 return gi.hasNInputs(1) || gi.sizedInput<UIntType>(0, 1) ||
420 gi.sizedOutput<UIntType>(1) || gi.namedIntParam("base") ||
421 gi.namedIntParam("more") || gi.hasNParam(1, 1);
422 }
423
424 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
425 PatternRewriter &rewriter) override {
426 auto getI64Attr = [&](IntegerAttr val) {
427 if (!val)
428 return IntegerAttr();
429 return rewriter.getI64IntegerAttr(val.getValue().getZExtValue());
430 };
431 auto base = getI64Attr(gi.getParamValue<IntegerAttr>("base"));
432 auto more = getI64Attr(gi.getParamValue<IntegerAttr>("more"));
433 rewriter.replaceOpWithNewOp<LTLGoToRepeatIntrinsicOp>(
434 gi.op, gi.op.getResultTypes(), adaptor.getOperands()[0], base, more);
435 }
436};
437
438class CirctLTLNonConsecutiveRepeatConverter : public IntrinsicConverter {
439public:
440 using IntrinsicConverter::IntrinsicConverter;
441
442 bool check(GenericIntrinsic gi) override {
443 return gi.hasNInputs(1) || gi.sizedInput<UIntType>(0, 1) ||
444 gi.sizedOutput<UIntType>(1) || gi.namedIntParam("base") ||
445 gi.namedIntParam("more") || gi.hasNParam(1, 1);
446 }
447
448 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
449 PatternRewriter &rewriter) override {
450 auto getI64Attr = [&](IntegerAttr val) {
451 if (!val)
452 return IntegerAttr();
453 return rewriter.getI64IntegerAttr(val.getValue().getZExtValue());
454 };
455 auto base = getI64Attr(gi.getParamValue<IntegerAttr>("base"));
456 auto more = getI64Attr(gi.getParamValue<IntegerAttr>("more"));
457 rewriter.replaceOpWithNewOp<LTLNonConsecutiveRepeatIntrinsicOp>(
458 gi.op, gi.op.getResultTypes(), adaptor.getOperands()[0], base, more);
459 }
460};
461
462template <class Op>
463class CirctVerifConverter : public IntrinsicConverter {
464public:
465 using IntrinsicConverter::IntrinsicConverter;
466
467 bool check(GenericIntrinsic gi) override {
468 return gi.hasNInputs(1, 2) || gi.sizedInput<UIntType>(0, 1) ||
469 gi.namedParam("label", true) || gi.hasNParam(0, 1) ||
470 gi.hasNoOutput();
471 }
472
473 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
474 PatternRewriter &rewriter) override {
475 auto label = gi.getParamValue<StringAttr>("label");
476 auto operands = adaptor.getOperands();
477
478 // Check if an enable was provided
479 Value enable;
480 if (gi.getNumInputs() == 2)
481 enable = operands[1];
482
483 rewriter.replaceOpWithNewOp<Op>(gi.op, operands[0], enable, label);
484 }
485};
486
487class CirctMux2CellConverter : public IntrinsicConverter {
488 using IntrinsicConverter::IntrinsicConverter;
489
490 bool check(GenericIntrinsic gi) override {
491 return gi.hasNInputs(3) || gi.typedInput<UIntType>(0) || gi.hasNParam(0) ||
492 gi.hasOutput();
493 }
494
495 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
496 PatternRewriter &rewriter) override {
497 auto operands = adaptor.getOperands();
498 rewriter.replaceOpWithNewOp<Mux2CellIntrinsicOp>(gi.op, operands[0],
499 operands[1], operands[2]);
500 }
501};
502
503class CirctMux4CellConverter : public IntrinsicConverter {
504 using IntrinsicConverter::IntrinsicConverter;
505
506 bool check(GenericIntrinsic gi) override {
507 return gi.hasNInputs(5) || gi.typedInput<UIntType>(0) || gi.hasNParam(0) ||
508 gi.hasOutput();
509 }
510
511 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
512 PatternRewriter &rewriter) override {
513 auto operands = adaptor.getOperands();
514 rewriter.replaceOpWithNewOp<Mux4CellIntrinsicOp>(
515 gi.op, operands[0], operands[1], operands[2], operands[3], operands[4]);
516 }
517};
518
519class CirctHasBeenResetConverter
520 : public IntrinsicOpConverter<HasBeenResetIntrinsicOp> {
521public:
522 using IntrinsicOpConverter::IntrinsicOpConverter;
523
524 bool check(GenericIntrinsic gi) override {
525 return gi.hasNInputs(2) || gi.typedInput<ClockType>(0) ||
526 gi.hasResetInput(1) || gi.sizedOutput<UIntType>(1) ||
527 gi.hasNParam(0);
528 }
529};
530
531class CirctProbeConverter : public IntrinsicOpConverter<FPGAProbeIntrinsicOp> {
532public:
533 using IntrinsicOpConverter::IntrinsicOpConverter;
534
535 bool check(GenericIntrinsic gi) override {
536 return gi.hasNInputs(2) || gi.typedInput<ClockType>(1) || gi.hasNParam(0) ||
537 gi.hasNoOutput();
538 }
539};
540
541template <class OpTy, bool ifElseFatal = false>
542class CirctAssertConverter : public IntrinsicConverter {
543public:
544 using IntrinsicConverter::IntrinsicConverter;
545
546 LogicalResult checkAndConvert(GenericIntrinsic gi,
547 GenericIntrinsicOpAdaptor adaptor,
548 PatternRewriter &rewriter) override {
549 // Check structure of the intrinsic.
550 if (gi.typedInput<ClockType>(0) || gi.sizedInput<UIntType>(1, 1) ||
551 gi.sizedInput<UIntType>(2, 1) ||
552 gi.namedParam("format", /*optional=*/true) ||
553 gi.namedParam("label", /*optional=*/true) ||
554 gi.namedParam("guards", /*optional=*/true) || gi.hasNParam(0, 3) ||
555 gi.hasNoOutput())
556 return failure();
557
558 auto format = gi.getParamValue<StringAttr>("format");
559 auto label = gi.getParamValue<StringAttr>("label");
560 auto guards = gi.getParamValue<StringAttr>("guards");
561
562 auto clock = adaptor.getOperands()[0];
563 auto predicate = adaptor.getOperands()[1];
564 auto enable = adaptor.getOperands()[2];
565
566 auto substitutions = adaptor.getOperands().drop_front(3);
567 auto name = label ? label.strref() : "";
568
569 // Parse the format string to handle special substitutions like
570 // {{SimulationTime}} and {{HierarchicalModuleName}}
571 StringAttr message;
572 SmallVector<Value> allOperands;
573 if (format) {
574 SmallVector<Value> substitutionVec(substitutions.begin(),
575 substitutions.end());
576 if (failed(parseFormatString(rewriter, gi.op->getLoc(), format.getValue(),
577 substitutionVec, message, allOperands)))
578 return failure();
579 } else {
580 // Message is not optional, so provide empty string if not present.
581 message = rewriter.getStringAttr("");
582 allOperands.append(substitutions.begin(), substitutions.end());
583 }
584
585 auto op = rewriter.template replaceOpWithNewOp<OpTy>(
586 gi.op, clock, predicate, enable, message, allOperands, name,
587 /*isConcurrent=*/true);
588 if (guards) {
589 SmallVector<StringRef> guardStrings;
590 guards.strref().split(guardStrings, ';', /*MaxSplit=*/-1,
591 /*KeepEmpty=*/false);
592 rewriter.startOpModification(op);
593 op->setAttr("guards", rewriter.getStrArrayAttr(guardStrings));
594 rewriter.finalizeOpModification(op);
595 }
596
597 if constexpr (ifElseFatal) {
598 rewriter.startOpModification(op);
599 op->setAttr("format", rewriter.getStringAttr("ifElseFatal"));
600 rewriter.finalizeOpModification(op);
601 }
602
603 return success();
604 }
605};
606
607class CirctCoverConverter : public IntrinsicConverter {
608public:
609 using IntrinsicConverter::IntrinsicConverter;
610
611 bool check(GenericIntrinsic gi) override {
612 return gi.hasNInputs(3) || gi.hasNoOutput() ||
613 gi.typedInput<ClockType>(0) || gi.sizedInput<UIntType>(1, 1) ||
614 gi.sizedInput<UIntType>(2, 1) ||
615 gi.namedParam("label", /*optional=*/true) ||
616 gi.namedParam("guards", /*optional=*/true) || gi.hasNParam(0, 2);
617 }
618
619 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
620 PatternRewriter &rewriter) override {
621 auto label = gi.getParamValue<StringAttr>("label");
622 auto guards = gi.getParamValue<StringAttr>("guards");
623
624 auto clock = adaptor.getOperands()[0];
625 auto predicate = adaptor.getOperands()[1];
626 auto enable = adaptor.getOperands()[2];
627
628 auto name = label ? label.strref() : "";
629 // Empty message string for cover, only 'name' / label.
630 auto message = rewriter.getStringAttr("");
631 auto op = rewriter.replaceOpWithNewOp<CoverOp>(
632 gi.op, clock, predicate, enable, message, ValueRange{}, name,
633 /*isConcurrent=*/true);
634 if (guards) {
635 SmallVector<StringRef> guardStrings;
636 guards.strref().split(guardStrings, ';', /*MaxSplit=*/-1,
637 /*KeepEmpty=*/false);
638 rewriter.startOpModification(op);
639 op->setAttr("guards", rewriter.getStrArrayAttr(guardStrings));
640 rewriter.finalizeOpModification(op);
641 }
642 }
643};
644
645class CirctUnclockedAssumeConverter : public IntrinsicConverter {
646public:
647 using IntrinsicConverter::IntrinsicConverter;
648
649 bool check(GenericIntrinsic gi) override {
650 return gi.sizedInput<UIntType>(0, 1) || gi.sizedInput<UIntType>(1, 1) ||
651 gi.namedParam("format", /*optional=*/true) ||
652 gi.namedParam("label", /*optional=*/true) ||
653 gi.namedParam("guards", /*optional=*/true) || gi.hasNParam(0, 3) ||
654 gi.hasNoOutput();
655 }
656
657 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
658 PatternRewriter &rewriter) override {
659 auto format = gi.getParamValue<StringAttr>("format");
660 auto label = gi.getParamValue<StringAttr>("label");
661 auto guards = gi.getParamValue<StringAttr>("guards");
662
663 auto predicate = adaptor.getOperands()[0];
664 auto enable = adaptor.getOperands()[1];
665
666 auto substitutions = adaptor.getOperands().drop_front(2);
667 auto name = label ? label.strref() : "";
668 // Message is not optional, so provide empty string if not present.
669 auto message = format ? format : rewriter.getStringAttr("");
670 auto op = rewriter.template replaceOpWithNewOp<UnclockedAssumeIntrinsicOp>(
671 gi.op, predicate, enable, message, substitutions, name);
672 if (guards) {
673 SmallVector<StringRef> guardStrings;
674 guards.strref().split(guardStrings, ';', /*MaxSplit=*/-1,
675 /*KeepEmpty=*/false);
676 rewriter.startOpModification(op);
677 op->setAttr("guards", rewriter.getStrArrayAttr(guardStrings));
678 rewriter.finalizeOpModification(op);
679 }
680 }
681};
682
683class CirctDPICallConverter : public IntrinsicConverter {
684 static bool getIsClocked(GenericIntrinsic gi) {
685 return !gi.getParamValue<IntegerAttr>("isClocked").getValue().isZero();
686 }
687
688public:
689 using IntrinsicConverter::IntrinsicConverter;
690
691 bool check(GenericIntrinsic gi) override {
692 if (gi.hasNParam(2, 2) || gi.namedIntParam("isClocked") ||
693 gi.namedParam("functionName") ||
694 gi.namedParam("inputNames", /*optional=*/true) ||
695 gi.namedParam("outputName", /*optional=*/true))
696 return true;
697 auto isClocked = getIsClocked(gi);
698 // If clocked, the first operand must be a clock.
699 if (isClocked && gi.typedInput<ClockType>(0))
700 return true;
701 // Enable must be UInt<1>.
702 if (gi.sizedInput<UIntType>(isClocked, 1))
703 return true;
704
705 return false;
706 }
707
708 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
709 PatternRewriter &rewriter) override {
710 auto isClocked = getIsClocked(gi);
711 auto functionName = gi.getParamValue<StringAttr>("functionName");
712 ArrayAttr inputNamesStrArray;
713 StringAttr outputStr = gi.getParamValue<StringAttr>("outputName");
714 if (auto inputNames = gi.getParamValue<StringAttr>("inputNames")) {
715 SmallVector<StringRef> inputNamesTemporary;
716 inputNames.strref().split(inputNamesTemporary, ';', /*MaxSplit=*/-1,
717 /*KeepEmpty=*/false);
718 inputNamesStrArray = rewriter.getStrArrayAttr(inputNamesTemporary);
719 }
720 // Clock and enable are optional.
721 Value clock = isClocked ? adaptor.getOperands()[0] : Value();
722 Value enable = adaptor.getOperands()[static_cast<size_t>(isClocked)];
723
724 auto inputs =
725 adaptor.getOperands().drop_front(static_cast<size_t>(isClocked) + 1);
726
727 rewriter.replaceOpWithNewOp<DPICallIntrinsicOp>(
728 gi.op, gi.op.getResultTypes(), functionName, inputNamesStrArray,
729 outputStr, clock, enable, inputs);
730 }
731};
732
733//===----------------------------------------------------------------------===//
734// View intrinsic converter and helpers
735//===----------------------------------------------------------------------===//
736
737template <typename A>
738A tryGetAs(DictionaryAttr dict, Attribute root, StringRef key, Location loc,
739 Twine path = Twine()) {
740 return tryGetAsBase<A>(dict, root, key, loc, "View 'info'",
741 "'info' attribute", path);
742}
743
744/// Recursively walk a sifive.enterprise.grandcentral.AugmentedType to extract
745/// and slightly restructure information needed for a view.
746std::optional<DictionaryAttr>
747parseAugmentedType(MLIRContext *context, Location loc,
748 DictionaryAttr augmentedType, DictionaryAttr root,
749 StringAttr name, StringAttr defName,
750 std::optional<StringAttr> description, Twine path = {}) {
751 auto classAttr =
752 tryGetAs<StringAttr>(augmentedType, root, "class", loc, path);
753 if (!classAttr)
754 return std::nullopt;
755 StringRef classBase = classAttr.getValue();
756 if (!classBase.consume_front("sifive.enterprise.grandcentral.Augmented")) {
757 mlir::emitError(loc,
758 "the 'class' was expected to start with "
759 "'sifive.enterprise.grandCentral.Augmented*', but was '" +
760 classAttr.getValue() + "' (Did you misspell it?)")
761 .attachNote()
762 << "see attribute: " << augmentedType;
763 return std::nullopt;
764 }
765
766 // An AugmentedBundleType looks like:
767 // "defName": String
768 // "elements": Seq[AugmentedField]
769 if (classBase == "BundleType") {
770 defName = tryGetAs<StringAttr>(augmentedType, root, "defName", loc, path);
771 if (!defName)
772 return std::nullopt;
773
774 // Each element is an AugmentedField with members:
775 // "name": String
776 // "description": Option[String]
777 // "tpe": AugmentedType
778 SmallVector<Attribute> elements;
779 auto elementsAttr =
780 tryGetAs<ArrayAttr>(augmentedType, root, "elements", loc, path);
781 if (!elementsAttr)
782 return std::nullopt;
783 for (size_t i = 0, e = elementsAttr.size(); i != e; ++i) {
784 auto field = dyn_cast_or_null<DictionaryAttr>(elementsAttr[i]);
785 if (!field) {
786 mlir::emitError(
787 loc,
788 "View 'info' attribute with path '.elements[" + Twine(i) +
789 "]' contained an unexpected type (expected a DictionaryAttr).")
790 .attachNote()
791 << "The received element was: " << elementsAttr[i];
792 return std::nullopt;
793 }
794 auto ePath = (path + ".elements[" + Twine(i) + "]").str();
795 auto name = tryGetAs<StringAttr>(field, root, "name", loc, ePath);
796 if (!name)
797 return std::nullopt;
798 auto tpe = tryGetAs<DictionaryAttr>(field, root, "tpe", loc, ePath);
799 if (!tpe)
800 return std::nullopt;
801 std::optional<StringAttr> description;
802 if (auto maybeDescription = field.get("description"))
803 description = cast<StringAttr>(maybeDescription);
804 auto eltAttr =
805 parseAugmentedType(context, loc, tpe, root, name, defName,
806 description, path + "_" + name.getValue());
807 if (!eltAttr)
808 return std::nullopt;
809
810 // Collect information necessary to build a module with this view later.
811 // This includes the optional description and name.
812 NamedAttrList attrs;
813 if (auto maybeDescription = field.get("description"))
814 attrs.append("description", cast<StringAttr>(maybeDescription));
815 attrs.append("name", name);
816 auto tpeClass = tpe.getAs<StringAttr>("class");
817 if (!tpeClass) {
818 mlir::emitError(loc, "missing 'class' key in") << tpe;
819 return std::nullopt;
820 }
821 attrs.append("tpe", tpeClass);
822 elements.push_back(*eltAttr);
823 }
824 // Add an attribute that stores information necessary to construct the
825 // interface for the view. This needs the name of the interface (defName)
826 // and the names of the components inside it.
827 NamedAttrList attrs;
828 attrs.append("class", classAttr);
829 attrs.append("defName", defName);
830 if (description)
831 attrs.append("description", *description);
832 attrs.append("elements", ArrayAttr::get(context, elements));
833 attrs.append("name", name);
834 return DictionaryAttr::getWithSorted(context, attrs);
835 }
836
837 // An AugmentedGroundType has no contents.
838 if (classBase == "GroundType") {
839 NamedAttrList elementIface;
840
841 // Populate the attribute for the interface element.
842 elementIface.append("class", classAttr);
843 if (description)
844 elementIface.append("description", *description);
845 elementIface.append("name", name);
846
847 return DictionaryAttr::getWithSorted(context, elementIface);
848 }
849
850 // An AugmentedVectorType looks like:
851 // "elements": Seq[AugmentedType]
852 if (classBase == "VectorType") {
853 auto elementsAttr =
854 tryGetAs<ArrayAttr>(augmentedType, root, "elements", loc, path);
855 if (!elementsAttr)
856 return std::nullopt;
857 SmallVector<Attribute> elements;
858 for (auto [i, elt] : llvm::enumerate(elementsAttr)) {
859 auto eltAttr = parseAugmentedType(
860 context, loc, cast<DictionaryAttr>(elt), root, name,
861 StringAttr::get(context, ""), std::nullopt, path + "_" + Twine(i));
862 if (!eltAttr)
863 return std::nullopt;
864 elements.push_back(*eltAttr);
865 }
866 NamedAttrList attrs;
867 attrs.append("class", classAttr);
868 if (description)
869 attrs.append("description", *description);
870 attrs.append("elements", ArrayAttr::get(context, elements));
871 attrs.append("name", name);
872 return DictionaryAttr::getWithSorted(context, attrs);
873 }
874
875 // Anything else is unexpected or a user error if they manually wrote
876 // the JSON/attribute. Print an error and error out.
877 mlir::emitError(loc, "found unknown AugmentedType '" + classAttr.getValue() +
878 "' (Did you misspell it?)")
879 .attachNote()
880 << "see attribute: " << augmentedType;
881 return std::nullopt;
882}
883
884class ViewConverter : public IntrinsicConverter {
885public:
886 LogicalResult checkAndConvert(GenericIntrinsic gi,
887 GenericIntrinsicOpAdaptor adaptor,
888 PatternRewriter &rewriter) override {
889 // Check structure of the intrinsic.
890 if (gi.hasNoOutput() || gi.namedParam("info") || gi.namedParam("name") ||
891 gi.namedParam("yaml", true))
892 return failure();
893
894 // Check operands.
895 for (auto idx : llvm::seq(gi.getNumInputs()))
896 if (gi.checkInputType(idx, "must be ground type", [](auto ty) {
897 auto base = type_dyn_cast<FIRRTLBaseType>(ty);
898 return base && base.isGround();
899 }))
900 return failure();
901
902 // Parse "info" string parameter as JSON.
903 auto view =
904 llvm::json::parse(gi.getParamValue<StringAttr>("info").getValue());
905 if (auto err = view.takeError()) {
906 handleAllErrors(std::move(err), [&](const llvm::json::ParseError &a) {
907 gi.emitError() << ": error parsing view JSON: " << a.message();
908 });
909 return failure();
910 }
911
912 // Convert JSON to MLIR attribute.
913 llvm::json::Path::Root root;
914 auto value = convertJSONToAttribute(gi.op.getContext(), view.get(), root);
915 assert(value && "JSON to attribute failed but should not ever fail");
916
917 // Check attribute is a dictionary, for AugmentedBundleTypeAttr
918 // construction.
919 auto dict = dyn_cast<DictionaryAttr>(value);
920 if (!dict)
921 return gi.emitError() << ": 'info' parameter must be a dictionary";
922
923 auto nameAttr = gi.getParamValue<StringAttr>("name");
924 auto result = parseAugmentedType(
925 gi.op.getContext(), gi.op.getLoc(), dict, dict, nameAttr,
926 /* defName= */ {}, /* description= */ std::nullopt);
927
928 if (!result)
929 return failure();
930
931 // Build AugmentedBundleTypeAttr, unchecked.
932 auto augmentedType =
933 AugmentedBundleTypeAttr::get(gi.op.getContext(), *result);
934 if (augmentedType.getClass() != augmentedBundleTypeClass)
935 return gi.emitError() << ": 'info' must be augmented bundle";
936
937 // Scan for ground-type (leaves) and count.
938 SmallVector<DictionaryAttr> worklist;
939 worklist.push_back(augmentedType.getUnderlying());
940 size_t numLeaves = 0;
941 auto augGroundAttr =
942 StringAttr::get(gi.op.getContext(), augmentedGroundTypeClass);
943 [[maybe_unused]] auto augBundleAttr =
944 StringAttr::get(gi.op.getContext(), augmentedBundleTypeClass);
945 [[maybe_unused]] auto augVectorAttr =
946 StringAttr::get(gi.op.getContext(), augmentedVectorTypeClass);
947 while (!worklist.empty()) {
948 auto dict = worklist.pop_back_val();
949 auto clazz = dict.getAs<StringAttr>("class");
950 if (clazz == augGroundAttr) {
951 ++numLeaves;
952 continue;
953 }
954 assert(clazz == augBundleAttr || clazz == augVectorAttr);
955 llvm::append_range(
956 worklist,
957 dict.getAs<ArrayAttr>("elements").getAsRange<DictionaryAttr>());
958 }
959
960 if (numLeaves != gi.getNumInputs())
961 return gi.emitError()
962 << " has " << gi.getNumInputs() << " operands but view 'info' has "
963 << numLeaves << " leaf elements";
964
965 // Check complete, convert!
966 auto yaml = gi.getParamValue<StringAttr>("yaml");
967 rewriter.replaceOpWithNewOp<ViewIntrinsicOp>(
968 gi.op, nameAttr.getValue(), yaml, augmentedType, adaptor.getOperands());
969 return success();
970 }
971};
972
973} // namespace
974
975//===----------------------------------------------------------------------===//
976// FIRRTL intrinsic lowering dialect interface
977//===----------------------------------------------------------------------===//
978
979#include "FIRRTLIntrinsics.cpp.inc"
980
982 IntrinsicLowerings &lowering) const {
983 populateLowerings(lowering);
984 lowering.add<CirctPlusArgTestConverter>("circt.plusargs.test",
985 "circt_plusargs_test");
986 lowering.add<CirctPlusArgValueConverter>("circt.plusargs.value",
987 "circt_plusargs_value");
988 lowering.add<CirctClockGateConverter>("circt.clock_gate", "circt_clock_gate");
989 lowering.add<CirctClockInverterConverter>("circt.clock_inv",
990 "circt_clock_inv");
991 lowering.add<CirctClockDividerConverter>("circt.clock_div",
992 "circt_clock_div");
993 lowering.add<CirctLTLBinaryConverter<LTLAndIntrinsicOp>>("circt.ltl.and",
994 "circt_ltl_and");
995 lowering.add<CirctLTLBinaryConverter<LTLOrIntrinsicOp>>("circt.ltl.or",
996 "circt_ltl_or");
997 lowering.add<CirctLTLBinaryConverter<LTLIntersectIntrinsicOp>>(
998 "circt.ltl.intersect", "circt_ltl_intersect");
999 lowering.add<CirctLTLBinaryConverter<LTLConcatIntrinsicOp>>(
1000 "circt.ltl.concat", "circt_ltl_concat");
1001 lowering.add<CirctLTLBinaryConverter<LTLImplicationIntrinsicOp>>(
1002 "circt.ltl.implication", "circt_ltl_implication");
1003 lowering.add<CirctLTLBinaryConverter<LTLUntilIntrinsicOp>>("circt.ltl.until",
1004 "circt_ltl_until");
1005 lowering.add<CirctLTLUnaryConverter<LTLNotIntrinsicOp>>("circt.ltl.not",
1006 "circt_ltl_not");
1007 lowering.add<CirctLTLUnaryConverter<LTLEventuallyIntrinsicOp>>(
1008 "circt.ltl.eventually", "circt_ltl_eventually");
1009
1010 lowering.add<CirctLTLDelayConverter>("circt.ltl.delay", "circt_ltl_delay");
1011 lowering.add<CirctLTLRepeatConverter>("circt.ltl.repeat", "circt_ltl_repeat");
1012 lowering.add<CirctLTLGoToRepeatConverter>("circt.ltl.goto_repeat",
1013 "circt_ltl_goto_repeat");
1014 lowering.add<CirctLTLNonConsecutiveRepeatConverter>(
1015 "circt.ltl.non_consecutive_repeat", "circt_ltl_non_consecutive_repeat");
1016 lowering.add<CirctLTLClockConverter>("circt.ltl.clock", "circt_ltl_clock");
1017
1018 lowering.add<CirctVerifConverter<VerifAssertIntrinsicOp>>(
1019 "circt.verif.assert", "circt_verif_assert");
1020 lowering.add<CirctVerifConverter<VerifAssumeIntrinsicOp>>(
1021 "circt.verif.assume", "circt_verif_assume");
1022 lowering.add<CirctVerifConverter<VerifCoverIntrinsicOp>>("circt.verif.cover",
1023 "circt_verif_cover");
1024 lowering.add<CirctVerifConverter<VerifRequireIntrinsicOp>>(
1025 "circt.verif.require", "circt_verif_require");
1026 lowering.add<CirctVerifConverter<VerifEnsureIntrinsicOp>>(
1027 "circt.verif.ensure", "circt_verif_ensure");
1028 lowering.add<CirctMux2CellConverter>("circt.mux2cell", "circt_mux2cell");
1029 lowering.add<CirctMux4CellConverter>("circt.mux4cell", "circt_mux4cell");
1030 lowering.add<CirctHasBeenResetConverter>("circt.has_been_reset",
1031 "circt_has_been_reset");
1032 lowering.add<CirctProbeConverter>("circt.fpga_probe", "circt_fpga_probe");
1033 lowering.add<CirctAssertConverter<AssertOp>>("circt.chisel_assert",
1034 "circt_chisel_assert");
1035 lowering.add<CirctAssertConverter<AssertOp, /*ifElseFatal=*/true>>(
1036 "circt.chisel_ifelsefatal", "circt_chisel_ifelsefatal");
1037 lowering.add<CirctAssertConverter<AssumeOp>>("circt.chisel_assume",
1038 "circt_chisel_assume");
1039 lowering.add<CirctCoverConverter>("circt.chisel_cover", "circt_chisel_cover");
1040 lowering.add<CirctUnclockedAssumeConverter>("circt.unclocked_assume",
1041 "circt_unclocked_assume");
1042 lowering.add<CirctDPICallConverter>("circt.dpi_call", "circt_dpi_call");
1043
1044 lowering.add<ViewConverter>("circt.view", "circt_view");
1045}
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static std::optional< DictionaryAttr > parseAugmentedType(ApplyState &state, DictionaryAttr augmentedType, DictionaryAttr root, StringAttr name, StringAttr defName, std::optional< IntegerAttr > id, std::optional< StringAttr > description, Twine clazz, StringAttr companionAttr, Twine path={})
Recursively walk a sifive.enterprise.grandcentral.AugmentedType to extract any annotations it may con...
static LogicalResult convert(arc::ExecuteOp op, arc::ExecuteOp::Adaptor adaptor, ConversionPatternRewriter &rewriter, const TypeConverter &converter)
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.
constexpr const char * augmentedVectorTypeClass
constexpr const char * augmentedBundleTypeClass
A tryGetAs(DictionaryAttr dict, Attribute root, StringRef key, Location loc, Twine clazz, Twine path=Twine())
Implements the same behavior as DictionaryAttr::getAs<A> to return the value of a specific type assoc...
constexpr const char * augmentedGroundTypeClass
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.
mlir::ParseResult parseFormatString(mlir::OpBuilder &builder, mlir::Location loc, llvm::StringRef formatString, llvm::ArrayRef< mlir::Value > specOperands, mlir::StringAttr &formatStringResult, llvm::SmallVectorImpl< mlir::Value > &operands)
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.
Attribute convertJSONToAttribute(MLIRContext *context, llvm::json::Value &value, llvm::json::Path p)
Convert arbitrary JSON to an MLIR Attribute.
Definition seq.py:1
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