MLIR  22.0.0git
CodeGenHelpers.cpp
Go to the documentation of this file.
1 //===- CodeGenHelpers.cpp - MLIR op definitions generator ---------------===//
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 // OpDefinitionsGen uses the description of operations to generate C++
10 // definitions for ops.
11 //
12 //===----------------------------------------------------------------------===//
13 
15 #include "mlir/TableGen/Operator.h"
16 #include "mlir/TableGen/Pattern.h"
17 #include "llvm/Support/FormatVariadic.h"
18 #include "llvm/Support/Path.h"
19 #include "llvm/TableGen/CodeGenHelpers.h"
20 #include "llvm/TableGen/Record.h"
21 
22 using namespace llvm;
23 using namespace mlir;
24 using namespace mlir::tblgen;
25 
26 /// Generate a unique label based on the current file name to prevent name
27 /// collisions if multiple generated files are included at once.
28 static std::string getUniqueOutputLabel(const RecordKeeper &records,
29  StringRef tag) {
30  // Use the input file name when generating a unique name.
31  StringRef inputFilename = records.getInputFilename();
32 
33  // Drop all but the base filename.
34  StringRef nameRef = sys::path::filename(inputFilename);
35  nameRef.consume_back(".td");
36 
37  // Sanitize any invalid characters.
38  std::string uniqueName(tag);
39  for (char c : nameRef) {
40  if (isAlnum(c) || c == '_')
41  uniqueName.push_back(c);
42  else
43  uniqueName.append(utohexstr((unsigned char)c));
44  }
45  return uniqueName;
46 }
47 
48 StaticVerifierFunctionEmitter::StaticVerifierFunctionEmitter(
49  raw_ostream &os, const RecordKeeper &records, StringRef tag)
50  : os(os), uniqueOutputLabel(getUniqueOutputLabel(records, tag)) {}
51 
53  ArrayRef<const Record *> opDefs) {
54  NamespaceEmitter namespaceEmitter(os, Operator(*opDefs[0]).getCppNamespace());
55  emitTypeConstraints();
56  emitAttrConstraints();
57  emitPropConstraints();
58  emitSuccessorConstraints();
59  emitRegionConstraints();
60 }
61 
62 void StaticVerifierFunctionEmitter::emitPatternConstraints(
63  const ArrayRef<DagLeaf> constraints) {
64  collectPatternConstraints(constraints);
66 }
67 
68 //===----------------------------------------------------------------------===//
69 // Constraint Getters
70 //===----------------------------------------------------------------------===//
71 
73  const Constraint &constraint) const {
74  const auto *it = typeConstraints.find(constraint);
75  assert(it != typeConstraints.end() && "expected to find a type constraint");
76  return it->second;
77 }
78 
79 // Find a uniqued attribute constraint. Since not all attribute constraints can
80 // be uniqued, return std::nullopt if one was not found.
82  const Constraint &constraint) const {
83  const auto *it = attrConstraints.find(constraint);
84  return it == attrConstraints.end() ? std::optional<StringRef>()
85  : StringRef(it->second);
86 }
87 
88 // Find a uniqued property constraint. Since not all property constraints can
89 // be uniqued, return std::nullopt if one was not found.
91  const Constraint &constraint) const {
92  const auto *it = propConstraints.find(constraint);
93  return it == propConstraints.end() ? std::optional<StringRef>()
94  : StringRef(it->second);
95 }
96 
98  const Constraint &constraint) const {
99  const auto *it = successorConstraints.find(constraint);
100  assert(it != successorConstraints.end() &&
101  "expected to find a sucessor constraint");
102  return it->second;
103 }
104 
106  const Constraint &constraint) const {
107  const auto *it = regionConstraints.find(constraint);
108  assert(it != regionConstraints.end() &&
109  "expected to find a region constraint");
110  return it->second;
111 }
112 
113 //===----------------------------------------------------------------------===//
114 // Constraint Emission
115 //===----------------------------------------------------------------------===//
116 
117 /// Code templates for emitting type, attribute, successor, and region
118 /// constraints. Each of these templates require the following arguments:
119 ///
120 /// {0}: The unique constraint name.
121 /// {1}: The constraint code.
122 /// {2}: The constraint description.
123 
124 /// Code for a type constraint. These may be called on the type of either
125 /// operands or results.
126 static const char *const typeConstraintCode = R"(
127 static ::llvm::LogicalResult {0}(
128  ::mlir::Operation *op, ::mlir::Type type, ::llvm::StringRef valueKind,
129  unsigned valueIndex) {
130  if (!({1})) {
131  return op->emitOpError(valueKind) << " #" << valueIndex
132  << " must be {2}, but got " << type;
133  }
134  return ::mlir::success();
135 }
136 )";
137 
138 /// Code for an attribute constraint. These may be called from ops only.
139 /// Attribute constraints cannot reference anything other than `$_self` and
140 /// `$_op`.
141 ///
142 /// TODO: Unique constraints for adaptors. However, most Adaptor::verify
143 /// functions are stripped anyways.
144 static const char *const attrConstraintCode = R"(
145 static ::llvm::LogicalResult {0}(
146  ::mlir::Attribute attr, ::llvm::StringRef attrName, llvm::function_ref<::mlir::InFlightDiagnostic()> emitError) {{
147  if (attr && !({1}))
148  return emitError() << "attribute '" << attrName
149  << "' failed to satisfy constraint: {2}";
150  return ::mlir::success();
151 }
152 static ::llvm::LogicalResult {0}(
153  ::mlir::Operation *op, ::mlir::Attribute attr, ::llvm::StringRef attrName) {{
154  return {0}(attr, attrName, [op]() {{
155  return op->emitOpError();
156  });
157 }
158 )";
159 
160 /// Code for a property constraint. These may be called from ops only.
161 /// Property constraints cannot reference anything other than `$_self` and
162 /// `$_op`. {3} is the interface type of the property.
163 static const char *const propConstraintCode = R"(
164  static ::llvm::LogicalResult {0}(
165  {3} prop, ::llvm::StringRef propName, llvm::function_ref<::mlir::InFlightDiagnostic()> emitError) {{
166  if (!({1}))
167  return emitError() << "property '" << propName
168  << "' failed to satisfy constraint: {2}";
169  return ::mlir::success();
170  }
171  static ::llvm::LogicalResult {0}(
172  ::mlir::Operation *op, {3} prop, ::llvm::StringRef propName) {{
173  return {0}(prop, propName, [op]() {{
174  return op->emitOpError();
175  });
176  }
177  )";
178 
179 /// Code for a successor constraint.
180 static const char *const successorConstraintCode = R"(
181 static ::llvm::LogicalResult {0}(
182  ::mlir::Operation *op, ::mlir::Block *successor,
183  ::llvm::StringRef successorName, unsigned successorIndex) {
184  if (!({1})) {
185  return op->emitOpError("successor #") << successorIndex << " ('"
186  << successorName << ")' failed to verify constraint: {2}";
187  }
188  return ::mlir::success();
189 }
190 )";
191 
192 /// Code for a region constraint. Callers will need to pass in the region's name
193 /// for emitting an error message.
194 static const char *const regionConstraintCode = R"(
195 static ::llvm::LogicalResult {0}(
196  ::mlir::Operation *op, ::mlir::Region &region, ::llvm::StringRef regionName,
197  unsigned regionIndex) {
198  if (!({1})) {
199  return op->emitOpError("region #") << regionIndex
200  << (regionName.empty() ? " " : " ('" + regionName + "') ")
201  << "failed to verify constraint: {2}";
202  }
203  return ::mlir::success();
204 }
205 )";
206 
207 /// Code for a pattern type or attribute constraint.
208 ///
209 /// {0}: name of function
210 /// {1}: Condition template
211 /// {2}: Constraint summary
212 /// {3}: "::mlir::Type type" or "::mlirAttribute attr" or "propType prop".
213 /// Can be "T prop" for generic property constraints.
214 static const char *const patternConstraintCode = R"(
215 static ::llvm::LogicalResult {0}(
216  ::mlir::PatternRewriter &rewriter, ::mlir::Operation *op, {3},
217  ::llvm::StringRef failureStr) {
218  if (!({1})) {
219  return rewriter.notifyMatchFailure(op, [&](::mlir::Diagnostic &diag) {
220  diag << failureStr << ": {2}";
221  });
222  }
223  return ::mlir::success();
224 }
225 )";
226 
227 void StaticVerifierFunctionEmitter::emitConstraints(
228  const ConstraintMap &constraints, StringRef selfName,
229  const char *const codeTemplate) {
230  FmtContext ctx;
231  ctx.addSubst("_op", "*op").withSelf(selfName);
232  for (auto &it : constraints) {
233  os << formatv(codeTemplate, it.second,
234  tgfmt(it.first.getConditionTemplate(), &ctx),
235  escapeString(it.first.getSummary()));
236  }
237 }
238 
239 void StaticVerifierFunctionEmitter::emitTypeConstraints() {
240  emitConstraints(typeConstraints, "type", typeConstraintCode);
241 }
242 
243 void StaticVerifierFunctionEmitter::emitAttrConstraints() {
244  emitConstraints(attrConstraints, "attr", attrConstraintCode);
245 }
246 
247 /// Unlike with the other helpers, this one has to substitute in the interface
248 /// type of the property, so we can't just use the generic function.
249 void StaticVerifierFunctionEmitter::emitPropConstraints() {
250  FmtContext ctx;
251  ctx.addSubst("_op", "*op").withSelf("prop");
252  for (auto &it : propConstraints) {
253  auto propConstraint = cast<PropConstraint>(it.first);
254  os << formatv(propConstraintCode, it.second,
255  tgfmt(propConstraint.getConditionTemplate(), &ctx),
256  escapeString(it.first.getSummary()),
257  propConstraint.getInterfaceType());
258  }
259 }
260 
261 void StaticVerifierFunctionEmitter::emitSuccessorConstraints() {
262  emitConstraints(successorConstraints, "successor", successorConstraintCode);
263 }
264 
265 void StaticVerifierFunctionEmitter::emitRegionConstraints() {
266  emitConstraints(regionConstraints, "region", regionConstraintCode);
267 }
268 
269 void StaticVerifierFunctionEmitter::emitPatternConstraints() {
270  FmtContext ctx;
271  ctx.addSubst("_op", "*op").withBuilder("rewriter").withSelf("type");
272  for (auto &it : typeConstraints) {
273  os << formatv(patternConstraintCode, it.second,
274  tgfmt(it.first.getConditionTemplate(), &ctx),
275  escapeString(it.first.getSummary()), "::mlir::Type type");
276  }
277  ctx.withSelf("attr");
278  for (auto &it : attrConstraints) {
279  os << formatv(patternConstraintCode, it.second,
280  tgfmt(it.first.getConditionTemplate(), &ctx),
281  escapeString(it.first.getSummary()),
282  "::mlir::Attribute attr");
283  }
284  ctx.withSelf("prop");
285  for (auto &it : propConstraints) {
286  PropConstraint propConstraint = cast<PropConstraint>(it.first);
287  StringRef interfaceType = propConstraint.getInterfaceType();
288  // Constraints that are generic over multiple interface types are
289  // templatized under the assumption that they'll be used correctly.
290  if (interfaceType.empty()) {
291  interfaceType = "T";
292  os << "template <typename T>";
293  }
294  os << formatv(patternConstraintCode, it.second,
295  tgfmt(propConstraint.getConditionTemplate(), &ctx),
296  escapeString(propConstraint.getSummary()),
297  Twine(interfaceType) + " prop");
298  }
299 }
300 
301 //===----------------------------------------------------------------------===//
302 // Constraint Uniquing
303 //===----------------------------------------------------------------------===//
304 
305 /// An attribute constraint that references anything other than itself and the
306 /// current op cannot be generically extracted into a function. Most
307 /// prohibitive are operands and results, which require calls to
308 /// `getODSOperands` or `getODSResults`. Attribute references are tricky too
309 /// because ops use cached identifiers.
311  FmtContext ctx;
312  auto test = tgfmt(attr.getConditionTemplate(),
313  &ctx.withSelf("attr").addSubst("_op", "*op"))
314  .str();
315  return !StringRef(test).contains("<no-subst-found>");
316 }
317 
318 /// A property constraint that references anything other than itself and the
319 /// current op cannot be generically extracted into a function, just as with
320 /// canUnequePropConstraint(). Additionally, property constraints without
321 /// an interface type specified can't be uniqued, and ones that are a literal
322 /// "true" shouldn't be constrained.
324  FmtContext ctx;
325  auto test = tgfmt(prop.getConditionTemplate(),
326  &ctx.withSelf("prop").addSubst("_op", "*op"))
327  .str();
328  return !StringRef(test).contains("<no-subst-found>") && test != "true" &&
329  !prop.getInterfaceType().empty();
330 }
331 
332 std::string StaticVerifierFunctionEmitter::getUniqueName(StringRef kind,
333  unsigned index) {
334  return ("__mlir_ods_local_" + kind + "_constraint_" + uniqueOutputLabel +
335  Twine(index))
336  .str();
337 }
338 
339 void StaticVerifierFunctionEmitter::collectConstraint(ConstraintMap &map,
340  StringRef kind,
341  Constraint constraint) {
342  auto [it, inserted] = map.try_emplace(constraint);
343  if (inserted)
344  it->second = getUniqueName(kind, map.size());
345 }
346 
348  ArrayRef<const Record *> opDefs) {
349  const auto collectTypeConstraints = [&](Operator::const_value_range values) {
350  for (const NamedTypeConstraint &value : values)
351  if (value.hasPredicate())
352  collectConstraint(typeConstraints, "type", value.constraint);
353  };
354 
355  for (const Record *def : opDefs) {
356  Operator op(*def);
357  /// Collect type constraints.
358  collectTypeConstraints(op.getOperands());
359  collectTypeConstraints(op.getResults());
360  /// Collect attribute constraints.
361  for (const NamedAttribute &namedAttr : op.getAttributes()) {
362  if (!namedAttr.attr.getPredicate().isNull() &&
363  !namedAttr.attr.isDerivedAttr() &&
364  canUniqueAttrConstraint(namedAttr.attr))
365  collectConstraint(attrConstraints, "attr", namedAttr.attr);
366  }
367  /// Collect non-trivial property constraints.
368  for (const NamedProperty &namedProp : op.getProperties()) {
369  if (!namedProp.prop.getPredicate().isNull() &&
370  canUniquePropConstraint(namedProp.prop)) {
371  collectConstraint(propConstraints, "prop", namedProp.prop);
372  }
373  }
374  /// Collect successor constraints.
375  for (const NamedSuccessor &successor : op.getSuccessors()) {
376  if (!successor.constraint.getPredicate().isNull()) {
377  collectConstraint(successorConstraints, "successor",
378  successor.constraint);
379  }
380  }
381  /// Collect region constraints.
382  for (const NamedRegion &region : op.getRegions())
383  if (!region.constraint.getPredicate().isNull())
384  collectConstraint(regionConstraints, "region", region.constraint);
385  }
386 }
387 
388 void StaticVerifierFunctionEmitter::collectPatternConstraints(
389  const ArrayRef<DagLeaf> constraints) {
390  for (auto &leaf : constraints) {
391  assert(leaf.isOperandMatcher() || leaf.isAttrMatcher() ||
392  leaf.isPropMatcher());
393  Constraint constraint = leaf.getAsConstraint();
394  if (leaf.isOperandMatcher())
395  collectConstraint(typeConstraints, "type", constraint);
396  else if (leaf.isAttrMatcher())
397  collectConstraint(attrConstraints, "attr", constraint);
398  else if (leaf.isPropMatcher())
399  collectConstraint(propConstraints, "prop", constraint);
400  }
401 }
402 
403 //===----------------------------------------------------------------------===//
404 // Public Utility Functions
405 //===----------------------------------------------------------------------===//
406 
407 std::string mlir::tblgen::escapeString(StringRef value) {
408  std::string ret;
409  raw_string_ostream os(ret);
410  os.write_escaped(value);
411  return ret;
412 }
static const char *const successorConstraintCode
Code for a successor constraint.
static const char *const patternConstraintCode
Code for a pattern type or attribute constraint.
static const char *const regionConstraintCode
Code for a region constraint.
static const char *const typeConstraintCode
Code templates for emitting type, attribute, successor, and region constraints.
static std::string getUniqueOutputLabel(const RecordKeeper &records, StringRef tag)
Generate a unique label based on the current file name to prevent name collisions if multiple generat...
static const char *const propConstraintCode
Code for a property constraint.
static bool canUniqueAttrConstraint(Attribute attr)
An attribute constraint that references anything other than itself and the current op cannot be gener...
static bool canUniquePropConstraint(Property prop)
A property constraint that references anything other than itself and the current op cannot be generic...
static const char *const attrConstraintCode
Code for an attribute constraint.
union mlir::linalg::@1252::ArityGroupAndKind::Kind kind
Attributes are known-constant values of operations.
Definition: Attributes.h:25
StringRef getSummary() const
Definition: Constraint.cpp:57
std::string getConditionTemplate() const
Definition: Constraint.cpp:53
Format context containing substitutions for special placeholders.
Definition: Format.h:40
FmtContext & withBuilder(Twine subst)
Definition: Format.cpp:36
FmtContext & withSelf(Twine subst)
Definition: Format.cpp:41
FmtContext & addSubst(StringRef placeholder, const Twine &subst)
Definition: Format.cpp:31
Wrapper class that contains a MLIR op's information (e.g., operands, attributes) defined in TableGen ...
Definition: Operator.h:77
llvm::iterator_range< const_region_iterator > getRegions() const
Definition: Operator.cpp:279
const_value_range getResults() const
Definition: Operator.cpp:197
const_value_range getOperands() const
Definition: Operator.cpp:353
llvm::iterator_range< const_attribute_iterator > getAttributes() const
Definition: Operator.cpp:333
llvm::iterator_range< const_successor_iterator > getSuccessors() const
Definition: Operator.cpp:301
llvm::iterator_range< const_property_iterator > getProperties() const
Definition: Operator.h:200
StringRef getInterfaceType() const
Definition: Property.cpp:35
StringRef getInterfaceType() const
Definition: Property.h:70
StringRef getRegionConstraintFn(const Constraint &constraint) const
Get the name of the static function used for the given region constraint.
void emitPatternConstraints(const ArrayRef< DagLeaf > constraints)
Unique all compatible type and attribute constraints from a pattern file and emit them at the top of ...
std::optional< StringRef > getAttrConstraintFn(const Constraint &constraint) const
Get the name of the static function used for the given attribute constraint.
std::optional< StringRef > getPropConstraintFn(const Constraint &constraint) const
Get the name of the static function used for the given property constraint.
void emitOpConstraints(ArrayRef< const llvm::Record * > opDefs)
Collect and unique all compatible type, attribute, successor, and region constraints from the operati...
void collectOpConstraints(ArrayRef< const llvm::Record * > opDefs)
Collect and unique all the constraints used by operations.
StringRef getTypeConstraintFn(const Constraint &constraint) const
Get the name of the static function used for the given type constraint.
StringRef getSuccessorConstraintFn(const Constraint &constraint) const
Get the name of the static function used for the given successor constraint.
The OpAsmOpInterface, see OpAsmInterface.td for more details.
Definition: CallGraph.h:229
auto tgfmt(StringRef fmt, const FmtContext *ctx, Ts &&...vals) -> FmtObject< decltype(std::make_tuple(llvm::support::detail::build_format_adapter(std::forward< Ts >(vals))...))>
Formats text by substituting placeholders in format string with replacement parameters.
Definition: Format.h:262
std::string escapeString(StringRef value)
Escape a string using C++ encoding. E.g. foo"bar -> foo\x22bar.
Include the generated interface declarations.