CIRCT 24.0.0git
Loading...
Searching...
No Matches
FIRRTLIntrinsics.h
Go to the documentation of this file.
1//===- FIRRTLIntrinsics.h - FIRRTL 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//
9// This file defines helpers for the lowering of intrinsics.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef CIRCT_DIALECT_FIRRTL_FIRRTLINTRINSICS_H
14#define CIRCT_DIALECT_FIRRTL_FIRRTLINTRINSICS_H
15
18#include "mlir/IR/Diagnostics.h"
19#include "mlir/Transforms/DialectConversion.h"
20
21namespace circt {
22namespace firrtl {
23
24/// Helper class for checking and extracting information from the generic
25/// instrinsic op.
27 GenericIntrinsicOp op;
28
29 GenericIntrinsic(GenericIntrinsicOp op) : op(op) {}
30
31 InFlightDiagnostic emitError() { return op.emitError(op.getIntrinsic()); }
32
33 //===--------------------------------------------------------------------===//
34 // Input checking
35 //===--------------------------------------------------------------------===//
36
37 ParseResult hasNInputs(unsigned n, unsigned c = 0);
38 unsigned getNumInputs();
39
40 template <typename C>
41 ParseResult checkInputType(unsigned n, const Twine &msg, C &&call) {
42 if (n >= op.getNumOperands())
43 return emitError() << " missing input " << n;
44 if (!std::invoke(std::forward<C>(call), op.getOperand(n).getType()))
45 return emitError() << " input " << n << " " << msg;
46 return success();
47 }
48
49 template <typename C>
50 ParseResult checkInputType(unsigned n, C &&call) {
51 return checkInputType(n, "not of correct type", std::forward<C>(call));
52 }
53
54 template <typename T>
55 ParseResult typedInput(unsigned n) {
56 return checkInputType(n, [](auto ty) { return isa<T>(ty); });
57 }
58
59 ParseResult hasResetInput(unsigned n) {
60 return checkInputType(n, "must be reset type", [](auto ty) {
61 auto baseType = dyn_cast<FIRRTLBaseType>(ty);
62 return baseType && baseType.isResetType();
63 });
64 }
65
66 template <typename T>
67 ParseResult sizedInput(unsigned n, int32_t size) {
68 return checkInputType(n, "not size " + Twine(size), [size](auto ty) {
69 auto t = dyn_cast<T>(ty);
70 return t && t.getWidth() == size;
71 });
72 }
73
74 //===--------------------------------------------------------------------===//
75 // Parameter checking
76 //===--------------------------------------------------------------------===//
77
78 ParseResult hasNParam(unsigned n, unsigned c = 0);
79
80 ParseResult namedParam(StringRef paramName, bool optional = false);
81
82 ParseResult namedIntParam(StringRef paramName, bool optional = false);
83
84 ParamDeclAttr getParamByName(StringRef name) {
85 for (auto param : op.getParameters().getAsRange<ParamDeclAttr>())
86 if (param.getName().getValue() == name)
87 return param;
88 return {};
89 }
90
91 /// Get parameter value by name, if present, as requested type.
92 template <typename T>
93 T getParamValue(StringRef name) {
94 auto p = getParamByName(name);
95 if (!p)
96 return {};
97 return cast<T>(p.getValue());
98 }
99
100 //===--------------------------------------------------------------------===//
101 // Output checking
102 //===--------------------------------------------------------------------===//
103
104 ParseResult hasOutput() {
105 if (op.getNumResults() == 0)
106 return emitError() << " missing output";
107 return success();
108 }
109 ParseResult hasNoOutput() {
110 if (op.getNumResults() != 0)
111 return emitError() << " should not have outputs";
112 return success();
113 }
114
115 template <typename T>
116 ParseResult typedOutput() {
117 if (failed(hasOutput()))
118 return failure();
119 if (!isa<T>(op.getResult().getType()))
120 return emitError() << " output not of correct type";
121 return success();
122 }
123
124 template <typename T>
125 ParseResult sizedOutput(int32_t size) {
126 if (failed(typedOutput<T>()))
127 return failure();
128 if (cast<T>(op.getResult().getType()).getWidth() != size)
129 return emitError() << " output not size " << size;
130 return success();
131 }
132
133 //===--------------------------------------------------------------------===//
134 // Output bundle element checking
135 //===--------------------------------------------------------------------===//
136
137 mlir::TypedValue<BundleType> getOutputBundle() {
138 return dyn_cast_or_null<mlir::TypedValue<BundleType>>(op.getResult());
139 }
140
141 ParseResult hasNOutputElements(unsigned n);
142
143 template <typename C>
144 ParseResult checkOutputElement(unsigned n, StringRef name, const Twine &msg,
145 C &&call) {
146 auto b = getOutputBundle();
147 if (!b)
148 return emitError() << " missing output bundle";
149 auto ty = b.getType();
150 if (n >= ty.getNumElements())
151 return emitError() << " missing output element " << n;
152 auto elementName = ty.getElementName(n);
153 if (elementName != name)
154 return emitError() << " output element " << n << " is named "
155 << elementName << " not " << name;
156 if (!std::invoke(std::forward<C>(call),
157 ty.getElementTypePreservingConst(n)))
158 return emitError() << " output element " << n << " " << msg;
159 return success();
160 }
161
162 template <typename C>
163 ParseResult checkOutputElement(unsigned n, StringRef name, C &&call) {
164 return checkOutputElement(n, name, "not of correct type",
165 std::forward<C>(call));
166 }
167
168 ParseResult hasOutputElement(unsigned n, StringRef name) {
169 return checkOutputElement(n, name, [](auto ty) { return true; });
170 }
171
172 template <typename T>
173 ParseResult typedOutputElement(unsigned n, StringRef name) {
174 return checkOutputElement(n, name, [](auto ty) { return isa<T>(ty); });
175 }
176
177 template <typename T>
178 ParseResult sizedOutputElement(unsigned n, StringRef name, int32_t size) {
179 return checkOutputElement(n, name, "not size " + Twine(size),
180 [size](auto ty) {
181 auto t = dyn_cast<T>(ty);
182 return t && t.getWidth() == size;
183 });
184 }
185};
186
187/// Base class for Intrinsic Converters.
188///
189/// Intrinsic converters contain validation logic, along with a converter
190/// method to transform generic intrinsic ops to their implementation.
191
192/// Instances of a converter must be stateless and are expected to be able
193/// to be used simultaneously on different intrinsic operations in parallel.
195public:
196 virtual void anchor(); // vtable anchor
197
198 virtual ~IntrinsicConverter() = default;
199
200 /// Checks whether the intrinsic is well-formed.
201 ///
202 /// This or's multiple ParseResults together, returning true on failure.
203 virtual bool check(GenericIntrinsic gi) {
204 llvm::report_fatal_error(
205 "check() or checkAndConvert() must be implemented");
206 return true;
207 }
208
209 /// Transform the intrinsic to its implementation.
210 virtual void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
211 PatternRewriter &rewriter) {
212 llvm::report_fatal_error(
213 "convert() or checkAndConvert() must be implemented");
214 }
215
216 /// Perform both check and convert, defaults to using check() and convert().
217 /// Return failure if check fails (no changes to IR should be made).
218 /// Otherwise, transform the intrinsic and return success.
219 /// Prefer overriding check and convert, but overriding this can avoid
220 /// recomputing work done during check needed for the conversion.
221 virtual LogicalResult checkAndConvert(GenericIntrinsic gi,
222 GenericIntrinsicOpAdaptor adaptor,
223 PatternRewriter &rewriter) {
224 if (check(gi))
225 return failure();
226 convert(gi, adaptor, rewriter);
227 return success();
228 };
229};
230
231template <typename OpTy>
233public:
234 /// Transform the intrinsic to its implementation.
235 /// Handles the simple case of just forwarding to new op kind.
236 void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
237 PatternRewriter &rewriter) final {
238 // Pass along result type and operands. No attributes.
239 rewriter.replaceOpWithNewOp<OpTy>(gi.op, gi.op.getResultTypes(),
240 adaptor.getOperands());
241 }
242};
243
244/// Lowering helper which collects all intrinsic converters.
246public:
248 llvm::DenseMap<StringAttr, std::unique_ptr<IntrinsicConverter>>;
249
250private:
251 /// Reference to the MLIR context.
252 MLIRContext *context;
253
254 /// Mapping from intrinsic names to converters.
256
257public:
259
260 /// Registers a converter to one or more intrinsic names.
261 template <typename T, typename... Args>
262 void add(Args... args) {
263 (addConverter<T>(args), ...);
264 }
265
266 /// Lowers all intrinsics in a module. Returns number converted or failure.
267 FailureOr<size_t> lower(FModuleOp mod, bool allowUnknownIntrinsics = false);
268
269private:
270 template <typename T>
271 typename std::enable_if_t<std::is_base_of_v<IntrinsicConverter, T>>
272 addConverter(StringRef name) {
273 auto nameAttr = StringAttr::get(context, name);
274 assert(!conversions.contains(nameAttr) &&
275 "duplicate conversion for intrinsic");
276 conversions.try_emplace(nameAttr, std::make_unique<T>());
277 }
278};
279
280/// A dialect interface to provide lowering conversions.
282 : public mlir::DialectInterface::Base<IntrinsicLoweringDialectInterface> {
283 IntrinsicLoweringDialectInterface(Dialect *dialect) : Base(dialect) {}
284
285 virtual void
287};
288
290 : public mlir::DialectInterfaceCollection<
291 IntrinsicLoweringDialectInterface> {
292 using Base::Base;
293
294 // Collect the intrinsic lowerings defined by each dialect.
295 void populateIntrinsicLowerings(IntrinsicLowerings &lowerings) const;
296};
297
303
304} // namespace firrtl
305} // namespace circt
306
307#endif // CIRCT_DIALECT_FIRRTL_FIRRTLINTRINSICS_H
assert(baseType &&"element must be base type")
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
Base class for Intrinsic Converters.
virtual bool check(GenericIntrinsic gi)
Checks whether the intrinsic is well-formed.
virtual ~IntrinsicConverter()=default
virtual void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor, PatternRewriter &rewriter)
Transform the intrinsic to its implementation.
virtual LogicalResult checkAndConvert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor, PatternRewriter &rewriter)
Perform both check and convert, defaults to using check() and convert().
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.
IntrinsicLowerings(MLIRContext *context)
std::enable_if_t< std::is_base_of_v< IntrinsicConverter, T > > addConverter(StringRef name)
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.
void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor, PatternRewriter &rewriter) final
Transform the intrinsic to its implementation.
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 checkInputType(unsigned n, const Twine &msg, C &&call)
GenericIntrinsic(GenericIntrinsicOp op)
ParseResult typedInput(unsigned n)
ParseResult hasNOutputElements(unsigned n)
ParseResult checkOutputElement(unsigned n, StringRef name, C &&call)
ParseResult namedIntParam(StringRef paramName, bool optional=false)
ParamDeclAttr getParamByName(StringRef name)
ParseResult checkOutputElement(unsigned n, StringRef name, const Twine &msg, C &&call)
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)
ParseResult typedOutputElement(unsigned n, StringRef name)
ParseResult checkInputType(unsigned n, C &&call)
virtual void populateIntrinsicLowerings(IntrinsicLowerings &lowerings) const =0
void populateIntrinsicLowerings(IntrinsicLowerings &lowerings) const