CIRCT  19.0.0git
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 
21 namespace circt {
22 namespace 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);
38 
39  template <typename C>
40  ParseResult checkInputType(unsigned n, const Twine &msg, C &&call) {
41  if (n >= op.getNumOperands())
42  return emitError() << " missing input " << n;
43  if (!std::invoke(std::forward<C>(call), op.getOperand(n).getType()))
44  return emitError() << " input " << n << " " << msg;
45  return success();
46  }
47 
48  template <typename C>
49  ParseResult checkInputType(unsigned n, C &&call) {
50  return checkInputType(n, "not of correct type", std::forward<C>(call));
51  }
52 
53  template <typename T>
54  ParseResult typedInput(unsigned n) {
55  return checkInputType(n, [](auto ty) { return isa<T>(ty); });
56  }
57 
58  ParseResult hasResetInput(unsigned n) {
59  return checkInputType(n, "must be reset type", [](auto ty) {
60  auto baseType = dyn_cast<FIRRTLBaseType>(ty);
61  return baseType && baseType.isResetType();
62  });
63  }
64 
65  template <typename T>
66  ParseResult sizedInput(unsigned n, int32_t size) {
67  return checkInputType(n, "not size " + Twine(size), [size](auto ty) {
68  auto t = dyn_cast<T>(ty);
69  return t && t.getWidth() == size;
70  });
71  }
72 
73  //===--------------------------------------------------------------------===//
74  // Parameter checking
75  //===--------------------------------------------------------------------===//
76 
77  ParseResult hasNParam(unsigned n, unsigned c = 0);
78 
79  ParseResult namedParam(StringRef paramName, bool optional = false);
80 
81  ParseResult namedIntParam(StringRef paramName, bool optional = false);
82 
83  ParamDeclAttr getParamByName(StringRef name) {
84  for (auto param : op.getParameters().getAsRange<ParamDeclAttr>())
85  if (param.getName().getValue().equals(name))
86  return param;
87  return {};
88  }
89 
90  /// Get parameter value by name, if present, as requested type.
91  template <typename T>
92  T getParamValue(StringRef name) {
93  auto p = getParamByName(name);
94  if (!p)
95  return {};
96  return cast<T>(p.getValue());
97  }
98 
99  //===--------------------------------------------------------------------===//
100  // Output checking
101  //===--------------------------------------------------------------------===//
102 
103  ParseResult hasOutput() {
104  if (op.getNumResults() == 0)
105  return emitError() << " missing output";
106  return success();
107  }
108  ParseResult hasNoOutput() {
109  if (op.getNumResults() != 0)
110  return emitError() << " should not have outputs";
111  return success();
112  }
113 
114  template <typename T>
115  ParseResult typedOutput() {
116  if (failed(hasOutput()))
117  return failure();
118  if (!isa<T>(op.getResult().getType()))
119  return emitError() << " output not of correct type";
120  return success();
121  }
122 
123  template <typename T>
124  ParseResult sizedOutput(int32_t size) {
125  if (failed(typedOutput<T>()))
126  return failure();
127  if (cast<T>(op.getResult().getType()).getWidth() != size)
128  return emitError() << " output not size " << size;
129  return success();
130  }
131 
132  //===--------------------------------------------------------------------===//
133  // Output bundle element checking
134  //===--------------------------------------------------------------------===//
135 
136  mlir::TypedValue<BundleType> getOutputBundle() {
137  return dyn_cast_or_null<mlir::TypedValue<BundleType>>(op.getResult());
138  }
139 
140  ParseResult hasNOutputElements(unsigned n);
141 
142  template <typename C>
143  ParseResult checkOutputElement(unsigned n, StringRef name, const Twine &msg,
144  C &&call) {
145  auto b = getOutputBundle();
146  if (!b)
147  return emitError() << " missing output bundle";
148  auto ty = b.getType();
149  if (n >= ty.getNumElements())
150  return emitError() << " missing output element " << n;
151  auto elementName = ty.getElementName(n);
152  if (elementName != name)
153  return emitError() << " output element " << n << " is named "
154  << elementName << " not " << name;
155  if (!std::invoke(std::forward<C>(call),
156  ty.getElementTypePreservingConst(n)))
157  return emitError() << " output element " << n << " " << msg;
158  return success();
159  }
160 
161  template <typename C>
162  ParseResult checkOutputElement(unsigned n, StringRef name, C &&call) {
163  return checkOutputElement(n, name, "not of correct type",
164  std::forward<C>(call));
165  }
166 
167  ParseResult hasOutputElement(unsigned n, StringRef name) {
168  return checkOutputElement(n, name, [](auto ty) { return true; });
169  }
170 
171  template <typename T>
172  ParseResult typedOutputElement(unsigned n, StringRef name) {
173  return checkOutputElement(n, name, [](auto ty) { return isa<T>(ty); });
174  }
175 
176  template <typename T>
177  ParseResult sizedOutputElement(unsigned n, StringRef name, int32_t size) {
178  return checkOutputElement(n, name, "not size " + Twine(size),
179  [size](auto ty) {
180  auto t = dyn_cast<T>(ty);
181  return t && t.getWidth() == size;
182  });
183  }
184 };
185 
186 /// Base class for Intrinsic Converters.
187 ///
188 /// Intrinsic converters contain validation logic, along with a converter
189 /// method to transform generic intrinsic ops to their implementation.
191 public:
192  virtual ~IntrinsicConverter() = default;
193 
194  /// Checks whether the intrinsic is well-formed.
195  ///
196  /// This or's multiple ParseResults together, returning true on failure.
197  virtual bool check(GenericIntrinsic gi) = 0;
198 
199  /// Transform the intrinsic to its implementation.
200  virtual void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
201  PatternRewriter &rewriter) = 0;
202 };
203 
204 template <typename OpTy>
206 public:
207  /// Transform the intrinsic to its implementation.
208  /// Handles the simple case of just forwarding to new op kind.
209  void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
210  PatternRewriter &rewriter) final {
211  // Pass along result type and operands. No attributes.
212  rewriter.replaceOpWithNewOp<OpTy>(gi.op, gi.op.getResultTypes(),
213  adaptor.getOperands());
214  }
215 };
216 
217 /// Lowering helper which collects all intrinsic converters.
219 public:
221  llvm::DenseMap<StringAttr, std::unique_ptr<IntrinsicConverter>>;
222 
223 private:
224  /// Reference to the MLIR context.
225  MLIRContext *context;
226 
227  /// Mapping from intrinsic names to converters.
229 
230 public:
232 
233  /// Registers a converter to one or more intrinsic names.
234  template <typename T, typename... Args>
235  void add(Args... args) {
236  (addConverter<T>(args), ...);
237  }
238 
239  /// Lowers all intrinsics in a module.
240  LogicalResult lower(FModuleOp mod, bool allowUnknownIntrinsics = false);
241 
242 private:
243  template <typename T>
244  typename std::enable_if_t<std::is_base_of_v<IntrinsicConverter, T>>
245  addConverter(StringRef name) {
246  auto nameAttr = StringAttr::get(context, name);
247  assert(!conversions.contains(nameAttr) &&
248  "duplicate conversion for intrinsic");
249  conversions.try_emplace(nameAttr, std::make_unique<T>());
250  }
251 };
252 
253 /// A dialect interface to provide lowering conversions.
255  : public mlir::DialectInterface::Base<IntrinsicLoweringDialectInterface> {
256  IntrinsicLoweringDialectInterface(Dialect *dialect) : Base(dialect) {}
257 
258  virtual void
260 };
261 
263  : public mlir::DialectInterfaceCollection<
264  IntrinsicLoweringDialectInterface> {
265  using Base::Base;
266 
267  // Collect the intrinsic lowerings defined by each dialect.
268  void populateIntrinsicLowerings(IntrinsicLowerings &lowerings) const;
269 };
270 
274  void populateIntrinsicLowerings(IntrinsicLowerings &lowerings) const override;
275 };
276 
277 } // namespace firrtl
278 } // namespace circt
279 
280 #endif // CIRCT_DIALECT_FIRRTL_FIRRTLINTRINSICS_H
assert(baseType &&"element must be base type")
Base class for Intrinsic Converters.
virtual ~IntrinsicConverter()=default
virtual void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor, PatternRewriter &rewriter)=0
Transform the intrinsic to its implementation.
virtual bool check(GenericIntrinsic gi)=0
Checks whether the intrinsic is well-formed.
Lowering helper which collects all intrinsic converters.
IntrinsicLowerings(MLIRContext *context)
llvm::DenseMap< StringAttr, std::unique_ptr< IntrinsicConverter > > ConversionMapTy
std::enable_if_t< std::is_base_of_v< IntrinsicConverter, T > > addConverter(StringRef name)
LogicalResult lower(FModuleOp mod, bool allowUnknownIntrinsics=false)
Lowers all intrinsics in a module.
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.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
void populateIntrinsicLowerings(IntrinsicLowerings &lowerings) const override
Helper class for checking and extracting information from the generic instrinsic op.
ParseResult sizedInput(unsigned n, int32_t size)
mlir::TypedValue< BundleType > getOutputBundle()
T getParamValue(StringRef name)
Get parameter value by name, if present, as requested type.
ParseResult hasResetInput(unsigned n)
ParseResult checkInputType(unsigned n, const Twine &msg, C &&call)
GenericIntrinsic(GenericIntrinsicOp op)
ParseResult typedInput(unsigned n)
ParseResult hasNOutputElements(unsigned n)
InFlightDiagnostic emitError()
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)
ParseResult typedOutputElement(unsigned n, StringRef name)
ParseResult checkInputType(unsigned n, C &&call)
A dialect interface to provide lowering conversions.
virtual void populateIntrinsicLowerings(IntrinsicLowerings &lowerings) const =0
void populateIntrinsicLowerings(IntrinsicLowerings &lowerings) const