xref: /llvm-project/mlir/include/mlir/TableGen/CodeGenHelpers.h (revision b60c6cbc0b444b2d40742ec60c962c0be700f6b7)
1 //
2 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3 // See https://llvm.org/LICENSE.txt for license information.
4 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5 //
6 //===----------------------------------------------------------------------===//
7 //
8 // This file defines common utilities for generating C++ from tablegen
9 // structures.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef MLIR_TABLEGEN_CODEGENHELPERS_H
14 #define MLIR_TABLEGEN_CODEGENHELPERS_H
15 
16 #include "mlir/TableGen/Constraint.h"
17 #include "mlir/TableGen/Dialect.h"
18 #include "mlir/TableGen/Format.h"
19 #include "llvm/ADT/DenseMap.h"
20 #include "llvm/ADT/MapVector.h"
21 #include "llvm/ADT/StringExtras.h"
22 #include "llvm/ADT/StringRef.h"
23 
24 namespace llvm {
25 class RecordKeeper;
26 } // namespace llvm
27 
28 namespace mlir {
29 namespace tblgen {
30 class Constraint;
31 class DagLeaf;
32 
33 // Format into a std::string
34 template <typename... Parameters>
35 std::string strfmt(const char *fmt, Parameters &&...parameters) {
36   return llvm::formatv(fmt, std::forward<Parameters>(parameters)...).str();
37 }
38 
39 // Simple RAII helper for defining ifdef-undef-endif scopes.
40 class IfDefScope {
41 public:
42   IfDefScope(llvm::StringRef name, llvm::raw_ostream &os)
43       : name(name.str()), os(os) {
44     os << "#ifdef " << name << "\n"
45        << "#undef " << name << "\n\n";
46   }
47   ~IfDefScope() { os << "\n#endif  // " << name << "\n\n"; }
48 
49 private:
50   std::string name;
51   llvm::raw_ostream &os;
52 };
53 
54 // A helper RAII class to emit nested namespaces for this op.
55 class NamespaceEmitter {
56 public:
57   NamespaceEmitter(raw_ostream &os, const Dialect &dialect) : os(os) {
58     if (!dialect)
59       return;
60     emitNamespaceStarts(os, dialect.getCppNamespace());
61   }
62   NamespaceEmitter(raw_ostream &os, StringRef cppNamespace) : os(os) {
63     emitNamespaceStarts(os, cppNamespace);
64   }
65 
66   ~NamespaceEmitter() {
67     for (StringRef ns : llvm::reverse(namespaces))
68       os << "} // namespace " << ns << "\n";
69   }
70 
71 private:
72   void emitNamespaceStarts(raw_ostream &os, StringRef cppNamespace) {
73     llvm::SplitString(cppNamespace, namespaces, "::");
74     for (StringRef ns : namespaces)
75       os << "namespace " << ns << " {\n";
76   }
77   raw_ostream &os;
78   SmallVector<StringRef, 2> namespaces;
79 };
80 
81 /// This class deduplicates shared operation verification code by emitting
82 /// static functions alongside the op definitions. These methods are local to
83 /// the definition file, and are invoked within the operation verify methods.
84 /// An example is shown below:
85 ///
86 /// static LogicalResult localVerify(...)
87 ///
88 /// LogicalResult OpA::verify(...) {
89 ///  if (failed(localVerify(...)))
90 ///    return failure();
91 ///  ...
92 /// }
93 ///
94 /// LogicalResult OpB::verify(...) {
95 ///  if (failed(localVerify(...)))
96 ///    return failure();
97 ///  ...
98 /// }
99 ///
100 class StaticVerifierFunctionEmitter {
101 public:
102   /// Create a constraint uniquer with a unique prefix derived from the record
103   /// keeper with an optional tag.
104   StaticVerifierFunctionEmitter(raw_ostream &os,
105                                 const llvm::RecordKeeper &records,
106                                 StringRef tag = "");
107 
108   /// Collect and unique all the constraints used by operations.
109   void collectOpConstraints(ArrayRef<const llvm::Record *> opDefs);
110 
111   /// Collect and unique all compatible type, attribute, successor, and region
112   /// constraints from the operations in the file and emit them at the top of
113   /// the generated file.
114   ///
115   /// Constraints that do not meet the restriction that they can only reference
116   /// `$_self` and `$_op` are not uniqued.
117   void emitOpConstraints(ArrayRef<const llvm::Record *> opDefs);
118 
119   /// Unique all compatible type and attribute constraints from a pattern file
120   /// and emit them at the top of the generated file.
121   ///
122   /// Constraints that do not meet the restriction that they can only reference
123   /// `$_self`, `$_op`, and `$_builder` are not uniqued.
124   void emitPatternConstraints(const ArrayRef<DagLeaf> constraints);
125 
126   /// Get the name of the static function used for the given type constraint.
127   /// These functions are used for operand and result constraints and have the
128   /// form:
129   ///
130   ///   LogicalResult(Operation *op, Type type, StringRef valueKind,
131   ///                 unsigned valueIndex);
132   ///
133   /// Pattern constraints have the form:
134   ///
135   ///   LogicalResult(PatternRewriter &rewriter, Operation *op, Type type,
136   ///                 StringRef failureStr);
137   ///
138   StringRef getTypeConstraintFn(const Constraint &constraint) const;
139 
140   /// Get the name of the static function used for the given attribute
141   /// constraint. These functions are in the form:
142   ///
143   ///   LogicalResult(Operation *op, Attribute attr, StringRef attrName);
144   ///
145   /// If a uniqued constraint was not found, this function returns std::nullopt.
146   /// The uniqued constraints cannot be used in the context of an OpAdaptor.
147   ///
148   /// Pattern constraints have the form:
149   ///
150   ///   LogicalResult(PatternRewriter &rewriter, Operation *op, Attribute attr,
151   ///                 StringRef failureStr);
152   ///
153   std::optional<StringRef>
154   getAttrConstraintFn(const Constraint &constraint) const;
155 
156   /// Get the name of the static function used for the given successor
157   /// constraint. These functions are in the form:
158   ///
159   ///   LogicalResult(Operation *op, Block *successor, StringRef successorName,
160   ///                 unsigned successorIndex);
161   ///
162   StringRef getSuccessorConstraintFn(const Constraint &constraint) const;
163 
164   /// Get the name of the static function used for the given region constraint.
165   /// These functions are in the form:
166   ///
167   ///   LogicalResult(Operation *op, Region &region, StringRef regionName,
168   ///                 unsigned regionIndex);
169   ///
170   /// The region name may be empty.
171   StringRef getRegionConstraintFn(const Constraint &constraint) const;
172 
173 private:
174   /// Emit static type constraint functions.
175   void emitTypeConstraints();
176   /// Emit static attribute constraint functions.
177   void emitAttrConstraints();
178   /// Emit static successor constraint functions.
179   void emitSuccessorConstraints();
180   /// Emit static region constraint functions.
181   void emitRegionConstraints();
182 
183   /// Emit pattern constraints.
184   void emitPatternConstraints();
185 
186   /// Collect and unique all pattern constraints.
187   void collectPatternConstraints(ArrayRef<DagLeaf> constraints);
188 
189   /// The output stream.
190   raw_ostream &os;
191 
192   /// A unique label for the file currently being generated. This is used to
193   /// ensure that the static functions have a unique name.
194   std::string uniqueOutputLabel;
195 
196   /// Use a MapVector to ensure that functions are generated deterministically.
197   using ConstraintMap = llvm::MapVector<Constraint, std::string,
198                                         llvm::DenseMap<Constraint, unsigned>>;
199 
200   /// A generic function to emit constraints
201   void emitConstraints(const ConstraintMap &constraints, StringRef selfName,
202                        const char *codeTemplate);
203 
204   /// Assign a unique name to a unique constraint.
205   std::string getUniqueName(StringRef kind, unsigned index);
206   /// Unique a constraint in the map.
207   void collectConstraint(ConstraintMap &map, StringRef kind,
208                          Constraint constraint);
209 
210   /// The set of type constraints used for operand and result verification in
211   /// the current file.
212   ConstraintMap typeConstraints;
213   /// The set of attribute constraints used in the current file.
214   ConstraintMap attrConstraints;
215   /// The set of successor constraints used in the current file.
216   ConstraintMap successorConstraints;
217   /// The set of region constraints used in the current file.
218   ConstraintMap regionConstraints;
219 };
220 
221 /// Escape a string using C++ encoding. E.g. foo"bar -> foo\x22bar.
222 std::string escapeString(StringRef value);
223 
224 namespace detail {
225 template <typename>
226 struct stringifier {
227   template <typename T>
228   static std::string apply(T &&t) {
229     return std::string(std::forward<T>(t));
230   }
231 };
232 template <>
233 struct stringifier<Twine> {
234   static std::string apply(const Twine &twine) { return twine.str(); }
235 };
236 template <typename OptionalT>
237 struct stringifier<std::optional<OptionalT>> {
238   static std::string apply(std::optional<OptionalT> optional) {
239     return optional ? stringifier<OptionalT>::apply(*optional) : std::string();
240   }
241 };
242 } // namespace detail
243 
244 /// Generically convert a value to a std::string.
245 template <typename T>
246 std::string stringify(T &&t) {
247   return detail::stringifier<std::remove_reference_t<std::remove_const_t<T>>>::
248       apply(std::forward<T>(t));
249 }
250 
251 } // namespace tblgen
252 } // namespace mlir
253 
254 #endif // MLIR_TABLEGEN_CODEGENHELPERS_H
255