CIRCT  20.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, 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.
192 public:
193  virtual ~IntrinsicConverter() = default;
194 
195  /// Checks whether the intrinsic is well-formed.
196  ///
197  /// This or's multiple ParseResults together, returning true on failure.
198  virtual bool check(GenericIntrinsic gi) = 0;
199 
200  /// Transform the intrinsic to its implementation.
201  virtual void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
202  PatternRewriter &rewriter) = 0;
203 };
204 
205 template <typename OpTy>
207 public:
208  /// Transform the intrinsic to its implementation.
209  /// Handles the simple case of just forwarding to new op kind.
210  void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
211  PatternRewriter &rewriter) final {
212  // Pass along result type and operands. No attributes.
213  rewriter.replaceOpWithNewOp<OpTy>(gi.op, gi.op.getResultTypes(),
214  adaptor.getOperands());
215  }
216 };
217 
218 /// Lowering helper which collects all intrinsic converters.
220 public:
222  llvm::DenseMap<StringAttr, std::unique_ptr<IntrinsicConverter>>;
223 
224 private:
225  /// Reference to the MLIR context.
226  MLIRContext *context;
227 
228  /// Mapping from intrinsic names to converters.
230 
231 public:
233 
234  /// Registers a converter to one or more intrinsic names.
235  template <typename T, typename... Args>
236  void add(Args... args) {
237  (addConverter<T>(args), ...);
238  }
239 
240  /// Lowers all intrinsics in a module. Returns number converted or failure.
241  FailureOr<size_t> lower(FModuleOp mod, bool allowUnknownIntrinsics = false);
242 
243 private:
244  template <typename T>
245  typename std::enable_if_t<std::is_base_of_v<IntrinsicConverter, T>>
246  addConverter(StringRef name) {
247  auto nameAttr = StringAttr::get(context, name);
248  assert(!conversions.contains(nameAttr) &&
249  "duplicate conversion for intrinsic");
250  conversions.try_emplace(nameAttr, std::make_unique<T>());
251  }
252 };
253 
254 /// A dialect interface to provide lowering conversions.
256  : public mlir::DialectInterface::Base<IntrinsicLoweringDialectInterface> {
257  IntrinsicLoweringDialectInterface(Dialect *dialect) : Base(dialect) {}
258 
259  virtual void
261 };
262 
264  : public mlir::DialectInterfaceCollection<
265  IntrinsicLoweringDialectInterface> {
266  using Base::Base;
267 
268  // Collect the intrinsic lowerings defined by each dialect.
269  void populateIntrinsicLowerings(IntrinsicLowerings &lowerings) const;
270 };
271 
275  void populateIntrinsicLowerings(IntrinsicLowerings &lowerings) const override;
276 };
277 
278 } // namespace firrtl
279 } // namespace circt
280 
281 #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.
FailureOr< size_t > lower(FModuleOp mod, bool allowUnknownIntrinsics=false)
Lowers all intrinsics in a module. Returns number converted or failure.
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)
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:55
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, unsigned c=0)
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