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