CIRCT 24.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:
334 using IntrinsicOpConverter<OpTy>::IntrinsicOpConverter;
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:
346 using IntrinsicOpConverter<OpTy>::IntrinsicOpConverter;
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
354template <bool Sync>
355class CirctLTLAcceptOnConverter : public IntrinsicConverter {
356public:
357 using IntrinsicConverter::IntrinsicConverter;
358
359 bool check(GenericIntrinsic gi) override {
360 return gi.hasNInputs(2) || gi.sizedInput<UIntType>(0, 1) ||
361 gi.sizedInput<UIntType>(1, 1) || gi.sizedOutput<UIntType>(1) ||
362 gi.hasNParam(0);
363 }
364
365 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
366 PatternRewriter &rewriter) override {
367 auto operands = adaptor.getOperands();
368 rewriter.replaceOpWithNewOp<LTLAcceptOnIntrinsicOp>(
369 gi.op, gi.op.getResultTypes(), operands[0], operands[1],
370 Sync ? rewriter.getUnitAttr() : UnitAttr());
371 }
372};
373
374class CirctLTLDelayConverter : public IntrinsicConverter {
375public:
376 using IntrinsicConverter::IntrinsicConverter;
377
378 bool check(GenericIntrinsic gi) override {
379 return gi.hasNInputs(1) || gi.sizedInput<UIntType>(0, 1) ||
380 gi.sizedOutput<UIntType>(1) || gi.namedIntParam("delay") ||
381 gi.namedIntParam("length", true) || gi.hasNParam(1, 1);
382 }
383
384 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
385 PatternRewriter &rewriter) override {
386 auto getI64Attr = [&](IntegerAttr val) {
387 if (!val)
388 return IntegerAttr();
389 return rewriter.getI64IntegerAttr(val.getValue().getZExtValue());
390 };
391 auto delay = getI64Attr(gi.getParamValue<IntegerAttr>("delay"));
392 auto length = getI64Attr(gi.getParamValue<IntegerAttr>("length"));
393 rewriter.replaceOpWithNewOp<LTLDelayIntrinsicOp>(
394 gi.op, gi.op.getResultTypes(), adaptor.getOperands()[0], delay, length);
395 }
396};
397
398class CirctLTLPastConverter : public IntrinsicConverter {
399public:
400 using IntrinsicConverter::IntrinsicConverter;
401
402 bool check(GenericIntrinsic gi) override {
403 if (gi.hasNInputs(2) || gi.sizedInput<UIntType>(0, 1) ||
404 gi.sizedOutput<UIntType>(1) || gi.namedIntParam("delay") ||
405 gi.hasNParam(1))
406 return true;
407 if (gi.typedInput<ClockType>(1))
408 return true;
409 return false;
410 }
411
412 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
413 PatternRewriter &rewriter) override {
414 auto delay = rewriter.getI64IntegerAttr(
415 gi.getParamValue<IntegerAttr>("delay").getValue().getZExtValue());
416 auto operands = adaptor.getOperands();
417 Value clock = operands[1];
418 rewriter.replaceOpWithNewOp<LTLPastIntrinsicOp>(
419 gi.op, gi.op.getResultTypes(), operands[0], delay, clock);
420 }
421};
422
423class CirctLTLClockConverter : public IntrinsicConverter {
424public:
425 using IntrinsicConverter::IntrinsicConverter;
426
427 bool check(GenericIntrinsic gi) override {
428 if (gi.hasNInputs(2) || gi.sizedInput<UIntType>(0, 1) ||
429 gi.typedInput<ClockType>(1) || gi.sizedOutput<UIntType>(1) ||
430 gi.hasNParam(0, 1))
431 return true;
432
433 auto params = gi.op.getParameters();
434 if (!params || params.empty())
435 return false;
436
437 auto param = cast<ParamDeclAttr>(*params.begin());
438 if (param.getName().getValue() != "edge") {
439 gi.emitError() << " has unexpected parameter '" << param.getName()
440 << "', expected 'edge'";
441 return true;
442 }
443 if (!isa<StringAttr>(param.getValue())) {
444 gi.emitError() << " has parameter '" << param.getName()
445 << "' which should be a string but is not";
446 return true;
447 }
448 return false;
449 }
450
451 LogicalResult checkAndConvert(GenericIntrinsic gi,
452 GenericIntrinsicOpAdaptor adaptor,
453 PatternRewriter &rewriter) override {
454 if (check(gi))
455 return failure();
456
457 auto edge = EventControl::AtPosEdge;
458 if (auto edgeAttr = gi.getParamValue<StringAttr>("edge")) {
459 auto parsedEdge = symbolizeEventControl(edgeAttr.getValue());
460 if (!parsedEdge)
461 return gi.emitError()
462 << " has invalid edge parameter '" << edgeAttr.getValue()
463 << "', expected one of [posedge, negedge, edge]";
464 edge = *parsedEdge;
465 }
466
467 auto operands = adaptor.getOperands();
468 rewriter.replaceOpWithNewOp<LTLClockIntrinsicOp>(
469 gi.op, gi.op.getResultTypes(), operands[0],
470 EventControlAttr::get(rewriter.getContext(), edge), operands[1]);
471 return success();
472 }
473};
474
475class CirctLTLRepeatConverter : public IntrinsicConverter {
476public:
477 using IntrinsicConverter::IntrinsicConverter;
478
479 bool check(GenericIntrinsic gi) override {
480 return gi.hasNInputs(1) || gi.sizedInput<UIntType>(0, 1) ||
481 gi.sizedOutput<UIntType>(1) || gi.namedIntParam("base") ||
482 gi.namedIntParam("more", true) || gi.hasNParam(1, 1);
483 }
484
485 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
486 PatternRewriter &rewriter) override {
487 auto getI64Attr = [&](IntegerAttr val) {
488 if (!val)
489 return IntegerAttr();
490 return rewriter.getI64IntegerAttr(val.getValue().getZExtValue());
491 };
492 auto base = getI64Attr(gi.getParamValue<IntegerAttr>("base"));
493 auto more = getI64Attr(gi.getParamValue<IntegerAttr>("more"));
494 rewriter.replaceOpWithNewOp<LTLRepeatIntrinsicOp>(
495 gi.op, gi.op.getResultTypes(), adaptor.getOperands()[0], base, more);
496 }
497};
498
499class CirctLTLGoToRepeatConverter : public IntrinsicConverter {
500public:
501 using IntrinsicConverter::IntrinsicConverter;
502
503 bool check(GenericIntrinsic gi) override {
504 return gi.hasNInputs(1) || gi.sizedInput<UIntType>(0, 1) ||
505 gi.sizedOutput<UIntType>(1) || gi.namedIntParam("base") ||
506 gi.namedIntParam("more") || gi.hasNParam(1, 1);
507 }
508
509 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
510 PatternRewriter &rewriter) override {
511 auto getI64Attr = [&](IntegerAttr val) {
512 if (!val)
513 return IntegerAttr();
514 return rewriter.getI64IntegerAttr(val.getValue().getZExtValue());
515 };
516 auto base = getI64Attr(gi.getParamValue<IntegerAttr>("base"));
517 auto more = getI64Attr(gi.getParamValue<IntegerAttr>("more"));
518 rewriter.replaceOpWithNewOp<LTLGoToRepeatIntrinsicOp>(
519 gi.op, gi.op.getResultTypes(), adaptor.getOperands()[0], base, more);
520 }
521};
522
523class CirctLTLNonConsecutiveRepeatConverter : public IntrinsicConverter {
524public:
525 using IntrinsicConverter::IntrinsicConverter;
526
527 bool check(GenericIntrinsic gi) override {
528 return gi.hasNInputs(1) || gi.sizedInput<UIntType>(0, 1) ||
529 gi.sizedOutput<UIntType>(1) || gi.namedIntParam("base") ||
530 gi.namedIntParam("more") || gi.hasNParam(1, 1);
531 }
532
533 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
534 PatternRewriter &rewriter) override {
535 auto getI64Attr = [&](IntegerAttr val) {
536 if (!val)
537 return IntegerAttr();
538 return rewriter.getI64IntegerAttr(val.getValue().getZExtValue());
539 };
540 auto base = getI64Attr(gi.getParamValue<IntegerAttr>("base"));
541 auto more = getI64Attr(gi.getParamValue<IntegerAttr>("more"));
542 rewriter.replaceOpWithNewOp<LTLNonConsecutiveRepeatIntrinsicOp>(
543 gi.op, gi.op.getResultTypes(), adaptor.getOperands()[0], base, more);
544 }
545};
546
547template <class Op>
548class CirctVerifConverter : public IntrinsicConverter {
549public:
550 using IntrinsicConverter::IntrinsicConverter;
551
552 bool check(GenericIntrinsic gi) override {
553 return gi.hasNInputs(1, 2) || gi.sizedInput<UIntType>(0, 1) ||
554 gi.namedParam("label", true) || gi.hasNParam(0, 1) ||
555 gi.hasNoOutput();
556 }
557
558 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
559 PatternRewriter &rewriter) override {
560 auto label = gi.getParamValue<StringAttr>("label");
561 auto operands = adaptor.getOperands();
562
563 // Check if an enable was provided
564 Value enable;
565 if (gi.getNumInputs() == 2)
566 enable = operands[1];
567
568 rewriter.replaceOpWithNewOp<Op>(gi.op, operands[0], enable, label);
569 }
570};
571
572class CirctMux2CellConverter : public IntrinsicConverter {
573 using IntrinsicConverter::IntrinsicConverter;
574
575 bool check(GenericIntrinsic gi) override {
576 return gi.hasNInputs(3) || gi.typedInput<UIntType>(0) || gi.hasNParam(0) ||
577 gi.hasOutput();
578 }
579
580 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
581 PatternRewriter &rewriter) override {
582 auto operands = adaptor.getOperands();
583 rewriter.replaceOpWithNewOp<Mux2CellIntrinsicOp>(gi.op, operands[0],
584 operands[1], operands[2]);
585 }
586};
587
588class CirctMux4CellConverter : public IntrinsicConverter {
589 using IntrinsicConverter::IntrinsicConverter;
590
591 bool check(GenericIntrinsic gi) override {
592 return gi.hasNInputs(5) || gi.typedInput<UIntType>(0) || gi.hasNParam(0) ||
593 gi.hasOutput();
594 }
595
596 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
597 PatternRewriter &rewriter) override {
598 auto operands = adaptor.getOperands();
599 rewriter.replaceOpWithNewOp<Mux4CellIntrinsicOp>(
600 gi.op, operands[0], operands[1], operands[2], operands[3], operands[4]);
601 }
602};
603
604class CirctHasBeenResetConverter
605 : public IntrinsicOpConverter<HasBeenResetIntrinsicOp> {
606public:
607 using IntrinsicOpConverter::IntrinsicOpConverter;
608
609 bool check(GenericIntrinsic gi) override {
610 return gi.hasNInputs(2) || gi.typedInput<ClockType>(0) ||
611 gi.hasResetInput(1) || gi.sizedOutput<UIntType>(1) ||
612 gi.hasNParam(0);
613 }
614};
615
616class CirctProbeConverter : public IntrinsicOpConverter<FPGAProbeIntrinsicOp> {
617public:
618 using IntrinsicOpConverter::IntrinsicOpConverter;
619
620 bool check(GenericIntrinsic gi) override {
621 return gi.hasNInputs(2) || gi.typedInput<ClockType>(1) || gi.hasNParam(0) ||
622 gi.hasNoOutput();
623 }
624};
625
626template <class OpTy, bool ifElseFatal = false>
627class CirctAssertConverter : public IntrinsicConverter {
628public:
629 using IntrinsicConverter::IntrinsicConverter;
630
631 LogicalResult checkAndConvert(GenericIntrinsic gi,
632 GenericIntrinsicOpAdaptor adaptor,
633 PatternRewriter &rewriter) override {
634 // Check structure of the intrinsic.
635 if (gi.typedInput<ClockType>(0) || gi.sizedInput<UIntType>(1, 1) ||
636 gi.sizedInput<UIntType>(2, 1) ||
637 gi.namedParam("format", /*optional=*/true) ||
638 gi.namedParam("label", /*optional=*/true) ||
639 gi.namedParam("guards", /*optional=*/true) || gi.hasNParam(0, 3) ||
640 gi.hasNoOutput())
641 return failure();
642
643 auto format = gi.getParamValue<StringAttr>("format");
644 auto label = gi.getParamValue<StringAttr>("label");
645 auto guards = gi.getParamValue<StringAttr>("guards");
646
647 auto clock = adaptor.getOperands()[0];
648 auto predicate = adaptor.getOperands()[1];
649 auto enable = adaptor.getOperands()[2];
650
651 auto substitutions = adaptor.getOperands().drop_front(3);
652 auto name = label ? label.strref() : "";
653
654 // Parse the format string to handle special substitutions like
655 // {{SimulationTime}} and {{HierarchicalModuleName}}
656 StringAttr message;
657 SmallVector<Value> allOperands;
658 if (format) {
659 SmallVector<Value> substitutionVec(substitutions.begin(),
660 substitutions.end());
661 if (failed(parseFormatString(rewriter, gi.op->getLoc(), format.getValue(),
662 substitutionVec, message, allOperands)))
663 return failure();
664 } else {
665 // Message is not optional, so provide empty string if not present.
666 message = rewriter.getStringAttr("");
667 allOperands.append(substitutions.begin(), substitutions.end());
668 }
669
670 auto op = rewriter.template replaceOpWithNewOp<OpTy>(
671 gi.op, clock, predicate, enable, message, allOperands, name,
672 /*isConcurrent=*/true);
673 if (guards) {
674 SmallVector<StringRef> guardStrings;
675 guards.strref().split(guardStrings, ';', /*MaxSplit=*/-1,
676 /*KeepEmpty=*/false);
677 rewriter.startOpModification(op);
678 op->setAttr("guards", rewriter.getStrArrayAttr(guardStrings));
679 rewriter.finalizeOpModification(op);
680 }
681
682 if constexpr (ifElseFatal) {
683 rewriter.startOpModification(op);
684 op->setAttr("format", rewriter.getStringAttr("ifElseFatal"));
685 rewriter.finalizeOpModification(op);
686 }
687
688 return success();
689 }
690};
691
692class CirctCoverConverter : public IntrinsicConverter {
693public:
694 using IntrinsicConverter::IntrinsicConverter;
695
696 bool check(GenericIntrinsic gi) override {
697 return gi.hasNInputs(3) || gi.hasNoOutput() ||
698 gi.typedInput<ClockType>(0) || gi.sizedInput<UIntType>(1, 1) ||
699 gi.sizedInput<UIntType>(2, 1) ||
700 gi.namedParam("label", /*optional=*/true) ||
701 gi.namedParam("guards", /*optional=*/true) || gi.hasNParam(0, 2);
702 }
703
704 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
705 PatternRewriter &rewriter) override {
706 auto label = gi.getParamValue<StringAttr>("label");
707 auto guards = gi.getParamValue<StringAttr>("guards");
708
709 auto clock = adaptor.getOperands()[0];
710 auto predicate = adaptor.getOperands()[1];
711 auto enable = adaptor.getOperands()[2];
712
713 auto name = label ? label.strref() : "";
714 // Empty message string for cover, only 'name' / label.
715 auto message = rewriter.getStringAttr("");
716 auto op = rewriter.replaceOpWithNewOp<CoverOp>(
717 gi.op, clock, predicate, enable, message, ValueRange{}, name,
718 /*isConcurrent=*/true);
719 if (guards) {
720 SmallVector<StringRef> guardStrings;
721 guards.strref().split(guardStrings, ';', /*MaxSplit=*/-1,
722 /*KeepEmpty=*/false);
723 rewriter.startOpModification(op);
724 op->setAttr("guards", rewriter.getStrArrayAttr(guardStrings));
725 rewriter.finalizeOpModification(op);
726 }
727 }
728};
729
730class CirctUnclockedAssumeConverter : public IntrinsicConverter {
731public:
732 using IntrinsicConverter::IntrinsicConverter;
733
734 bool check(GenericIntrinsic gi) override {
735 return gi.sizedInput<UIntType>(0, 1) || gi.sizedInput<UIntType>(1, 1) ||
736 gi.namedParam("format", /*optional=*/true) ||
737 gi.namedParam("label", /*optional=*/true) ||
738 gi.namedParam("guards", /*optional=*/true) || gi.hasNParam(0, 3) ||
739 gi.hasNoOutput();
740 }
741
742 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
743 PatternRewriter &rewriter) override {
744 auto format = gi.getParamValue<StringAttr>("format");
745 auto label = gi.getParamValue<StringAttr>("label");
746 auto guards = gi.getParamValue<StringAttr>("guards");
747
748 auto predicate = adaptor.getOperands()[0];
749 auto enable = adaptor.getOperands()[1];
750
751 auto substitutions = adaptor.getOperands().drop_front(2);
752 auto name = label ? label.strref() : "";
753 // Message is not optional, so provide empty string if not present.
754 auto message = format ? format : rewriter.getStringAttr("");
755 auto op = rewriter.template replaceOpWithNewOp<UnclockedAssumeIntrinsicOp>(
756 gi.op, predicate, enable, message, substitutions, name);
757 if (guards) {
758 SmallVector<StringRef> guardStrings;
759 guards.strref().split(guardStrings, ';', /*MaxSplit=*/-1,
760 /*KeepEmpty=*/false);
761 rewriter.startOpModification(op);
762 op->setAttr("guards", rewriter.getStrArrayAttr(guardStrings));
763 rewriter.finalizeOpModification(op);
764 }
765 }
766};
767
768class CirctDPICallConverter : public IntrinsicConverter {
769 static bool getIsClocked(GenericIntrinsic gi) {
770 return !gi.getParamValue<IntegerAttr>("isClocked").getValue().isZero();
771 }
772
773public:
774 using IntrinsicConverter::IntrinsicConverter;
775
776 bool check(GenericIntrinsic gi) override {
777 if (gi.hasNParam(2, 2) || gi.namedIntParam("isClocked") ||
778 gi.namedParam("functionName") ||
779 gi.namedParam("inputNames", /*optional=*/true) ||
780 gi.namedParam("outputName", /*optional=*/true))
781 return true;
782 auto isClocked = getIsClocked(gi);
783 // If clocked, the first operand must be a clock.
784 if (isClocked && gi.typedInput<ClockType>(0))
785 return true;
786 // Enable must be UInt<1>.
787 if (gi.sizedInput<UIntType>(isClocked, 1))
788 return true;
789
790 return false;
791 }
792
793 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
794 PatternRewriter &rewriter) override {
795 auto isClocked = getIsClocked(gi);
796 auto functionName = gi.getParamValue<StringAttr>("functionName");
797 ArrayAttr inputNamesStrArray;
798 StringAttr outputStr = gi.getParamValue<StringAttr>("outputName");
799 if (auto inputNames = gi.getParamValue<StringAttr>("inputNames")) {
800 SmallVector<StringRef> inputNamesTemporary;
801 inputNames.strref().split(inputNamesTemporary, ';', /*MaxSplit=*/-1,
802 /*KeepEmpty=*/false);
803 inputNamesStrArray = rewriter.getStrArrayAttr(inputNamesTemporary);
804 }
805 // Clock and enable are optional.
806 Value clock = isClocked ? adaptor.getOperands()[0] : Value();
807 Value enable = adaptor.getOperands()[static_cast<size_t>(isClocked)];
808
809 auto inputs =
810 adaptor.getOperands().drop_front(static_cast<size_t>(isClocked) + 1);
811
812 rewriter.replaceOpWithNewOp<DPICallIntrinsicOp>(
813 gi.op, gi.op.getResultTypes(), functionName, inputNamesStrArray,
814 outputStr, clock, enable, inputs);
815 }
816};
817
818//===----------------------------------------------------------------------===//
819// View intrinsic converter and helpers
820//===----------------------------------------------------------------------===//
821
822template <typename A>
823A tryGetAs(DictionaryAttr dict, Attribute root, StringRef key, Location loc,
824 Twine path = Twine()) {
825 return tryGetAsBase<A>(dict, root, key, loc, "View 'info'",
826 "'info' attribute", path);
827}
828
829/// Recursively walk a sifive.enterprise.grandcentral.AugmentedType to extract
830/// and slightly restructure information needed for a view.
831std::optional<DictionaryAttr>
832parseAugmentedType(MLIRContext *context, Location loc,
833 DictionaryAttr augmentedType, DictionaryAttr root,
834 StringAttr name, StringAttr defName,
835 std::optional<StringAttr> description, Twine path = {}) {
836 auto classAttr =
837 tryGetAs<StringAttr>(augmentedType, root, "class", loc, path);
838 if (!classAttr)
839 return std::nullopt;
840 StringRef classBase = classAttr.getValue();
841 if (!classBase.consume_front("sifive.enterprise.grandcentral.Augmented")) {
842 mlir::emitError(loc,
843 "the 'class' was expected to start with "
844 "'sifive.enterprise.grandCentral.Augmented*', but was '" +
845 classAttr.getValue() + "' (Did you misspell it?)")
846 .attachNote()
847 << "see attribute: " << augmentedType;
848 return std::nullopt;
849 }
850
851 // An AugmentedBundleType looks like:
852 // "defName": String
853 // "elements": Seq[AugmentedField]
854 if (classBase == "BundleType") {
855 defName = tryGetAs<StringAttr>(augmentedType, root, "defName", loc, path);
856 if (!defName)
857 return std::nullopt;
858
859 // Each element is an AugmentedField with members:
860 // "name": String
861 // "description": Option[String]
862 // "tpe": AugmentedType
863 SmallVector<Attribute> elements;
864 auto elementsAttr =
865 tryGetAs<ArrayAttr>(augmentedType, root, "elements", loc, path);
866 if (!elementsAttr)
867 return std::nullopt;
868 for (size_t i = 0, e = elementsAttr.size(); i != e; ++i) {
869 auto field = dyn_cast_or_null<DictionaryAttr>(elementsAttr[i]);
870 if (!field) {
871 mlir::emitError(
872 loc,
873 "View 'info' attribute with path '.elements[" + Twine(i) +
874 "]' contained an unexpected type (expected a DictionaryAttr).")
875 .attachNote()
876 << "The received element was: " << elementsAttr[i];
877 return std::nullopt;
878 }
879 auto ePath = (path + ".elements[" + Twine(i) + "]").str();
880 auto name = tryGetAs<StringAttr>(field, root, "name", loc, ePath);
881 if (!name)
882 return std::nullopt;
883 auto tpe = tryGetAs<DictionaryAttr>(field, root, "tpe", loc, ePath);
884 if (!tpe)
885 return std::nullopt;
886 std::optional<StringAttr> description;
887 if (auto maybeDescription = field.get("description"))
888 description = cast<StringAttr>(maybeDescription);
889 auto eltAttr =
890 parseAugmentedType(context, loc, tpe, root, name, defName,
891 description, path + "_" + name.getValue());
892 if (!eltAttr)
893 return std::nullopt;
894
895 // Collect information necessary to build a module with this view later.
896 // This includes the optional description and name.
897 NamedAttrList attrs;
898 if (auto maybeDescription = field.get("description"))
899 attrs.append("description", cast<StringAttr>(maybeDescription));
900 attrs.append("name", name);
901 auto tpeClass = tpe.getAs<StringAttr>("class");
902 if (!tpeClass) {
903 mlir::emitError(loc, "missing 'class' key in") << tpe;
904 return std::nullopt;
905 }
906 attrs.append("tpe", tpeClass);
907 elements.push_back(*eltAttr);
908 }
909 // Add an attribute that stores information necessary to construct the
910 // interface for the view. This needs the name of the interface (defName)
911 // and the names of the components inside it.
912 NamedAttrList attrs;
913 attrs.append("class", classAttr);
914 attrs.append("defName", defName);
915 if (description)
916 attrs.append("description", *description);
917 attrs.append("elements", ArrayAttr::get(context, elements));
918 attrs.append("name", name);
919 return DictionaryAttr::getWithSorted(context, attrs);
920 }
921
922 // An AugmentedGroundType has no contents.
923 if (classBase == "GroundType") {
924 NamedAttrList elementIface;
925
926 // Populate the attribute for the interface element.
927 elementIface.append("class", classAttr);
928 if (description)
929 elementIface.append("description", *description);
930 elementIface.append("name", name);
931
932 return DictionaryAttr::getWithSorted(context, elementIface);
933 }
934
935 // An AugmentedVectorType looks like:
936 // "elements": Seq[AugmentedType]
937 if (classBase == "VectorType") {
938 auto elementsAttr =
939 tryGetAs<ArrayAttr>(augmentedType, root, "elements", loc, path);
940 if (!elementsAttr)
941 return std::nullopt;
942 SmallVector<Attribute> elements;
943 for (auto [i, elt] : llvm::enumerate(elementsAttr)) {
944 auto eltAttr = parseAugmentedType(
945 context, loc, cast<DictionaryAttr>(elt), root, name,
946 StringAttr::get(context, ""), std::nullopt, path + "_" + Twine(i));
947 if (!eltAttr)
948 return std::nullopt;
949 elements.push_back(*eltAttr);
950 }
951 NamedAttrList attrs;
952 attrs.append("class", classAttr);
953 if (description)
954 attrs.append("description", *description);
955 attrs.append("elements", ArrayAttr::get(context, elements));
956 attrs.append("name", name);
957 return DictionaryAttr::getWithSorted(context, attrs);
958 }
959
960 // Anything else is unexpected or a user error if they manually wrote
961 // the JSON/attribute. Print an error and error out.
962 mlir::emitError(loc, "found unknown AugmentedType '" + classAttr.getValue() +
963 "' (Did you misspell it?)")
964 .attachNote()
965 << "see attribute: " << augmentedType;
966 return std::nullopt;
967}
968
969class ViewConverter : public IntrinsicConverter {
970public:
971 LogicalResult checkAndConvert(GenericIntrinsic gi,
972 GenericIntrinsicOpAdaptor adaptor,
973 PatternRewriter &rewriter) override {
974 // Check structure of the intrinsic.
975 if (gi.hasNoOutput() || gi.namedParam("info") || gi.namedParam("name") ||
976 gi.namedParam("yaml", true))
977 return failure();
978
979 // Check operands.
980 for (auto idx : llvm::seq(gi.getNumInputs()))
981 if (gi.checkInputType(idx, "must be ground type", [](auto ty) {
982 auto base = type_dyn_cast<FIRRTLBaseType>(ty);
983 return base && base.isGround();
984 }))
985 return failure();
986
987 // Parse "info" string parameter as JSON.
988 auto view =
989 llvm::json::parse(gi.getParamValue<StringAttr>("info").getValue());
990 if (auto err = view.takeError()) {
991 handleAllErrors(std::move(err), [&](const llvm::json::ParseError &a) {
992 gi.emitError() << ": error parsing view JSON: " << a.message();
993 });
994 return failure();
995 }
996
997 // Convert JSON to MLIR attribute.
998 llvm::json::Path::Root root;
999 auto value = convertJSONToAttribute(gi.op.getContext(), view.get(), root);
1000 assert(value && "JSON to attribute failed but should not ever fail");
1001
1002 // Check attribute is a dictionary, for AugmentedBundleTypeAttr
1003 // construction.
1004 auto dict = dyn_cast<DictionaryAttr>(value);
1005 if (!dict)
1006 return gi.emitError() << ": 'info' parameter must be a dictionary";
1007
1008 auto nameAttr = gi.getParamValue<StringAttr>("name");
1009 auto result = parseAugmentedType(
1010 gi.op.getContext(), gi.op.getLoc(), dict, dict, nameAttr,
1011 /* defName= */ {}, /* description= */ std::nullopt);
1012
1013 if (!result)
1014 return failure();
1015
1016 // Build AugmentedBundleTypeAttr, unchecked.
1017 auto augmentedType =
1018 AugmentedBundleTypeAttr::get(gi.op.getContext(), *result);
1019 if (augmentedType.getClass() != augmentedBundleTypeAnnoClass)
1020 return gi.emitError() << ": 'info' must be augmented bundle";
1021
1022 // Scan for ground-type (leaves) and count.
1023 SmallVector<DictionaryAttr> worklist;
1024 worklist.push_back(augmentedType.getUnderlying());
1025 size_t numLeaves = 0;
1026 auto augGroundAttr =
1027 StringAttr::get(gi.op.getContext(), augmentedGroundTypeAnnoClass);
1028 [[maybe_unused]] auto augBundleAttr =
1029 StringAttr::get(gi.op.getContext(), augmentedBundleTypeAnnoClass);
1030 [[maybe_unused]] auto augVectorAttr =
1031 StringAttr::get(gi.op.getContext(), augmentedVectorTypeAnnoClass);
1032 while (!worklist.empty()) {
1033 auto dict = worklist.pop_back_val();
1034 auto clazz = dict.getAs<StringAttr>("class");
1035 if (clazz == augGroundAttr) {
1036 ++numLeaves;
1037 continue;
1038 }
1039 assert(clazz == augBundleAttr || clazz == augVectorAttr);
1040 llvm::append_range(
1041 worklist,
1042 dict.getAs<ArrayAttr>("elements").getAsRange<DictionaryAttr>());
1043 }
1044
1045 if (numLeaves != gi.getNumInputs())
1046 return gi.emitError()
1047 << " has " << gi.getNumInputs() << " operands but view 'info' has "
1048 << numLeaves << " leaf elements";
1049
1050 // Check complete, convert!
1051 auto yaml = gi.getParamValue<StringAttr>("yaml");
1052 rewriter.replaceOpWithNewOp<ViewIntrinsicOp>(
1053 gi.op, nameAttr.getValue(), yaml, augmentedType, adaptor.getOperands());
1054 return success();
1055 }
1056};
1057
1058} // namespace
1059
1060//===----------------------------------------------------------------------===//
1061// FIRRTL intrinsic lowering dialect interface
1062//===----------------------------------------------------------------------===//
1063
1064#include "FIRRTLIntrinsics.cpp.inc"
1065
1067 IntrinsicLowerings &lowering) const {
1068 populateLowerings(lowering);
1069}
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)
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
NamedAttrList attrs
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
MLIRContext * context
Reference to the MLIR context.
ConversionMapTy conversions
Mapping from intrinsic names to converters.
A tryGetAs(DictionaryAttr dict, Attribute root, StringRef key, Location loc, Twine clazz, Twine path=Twine())
Implements the same behavior as DictionaryAttr::getAs to return the value of a specific type associat...
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, bool warnOnTruncation=false)
Emit a connect between two values.
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)
BaseTy type_dyn_cast(Type type)
A tryGetAsBase(DictionaryAttr dict, Attribute root, StringRef key, Location loc, Twine whatSpecific, Twine whatFull, Twine path=Twine())
Implements the same behavior as DictionaryAttr::getAs to return the value of a specific type associat...
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.
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 checkInputType(unsigned n, const Twine &msg, C &&call)
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.
virtual void populateIntrinsicLowerings(IntrinsicLowerings &lowerings) const =0
void populateIntrinsicLowerings(IntrinsicLowerings &lowerings) const