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/Support/LLVM.h"
24#include "llvm/ADT/StringExtras.h"
25#include "llvm/ADT/StringRef.h"
26#include "llvm/Support/FormatVariadic.h"
27#include "llvm/Support/Path.h"
28#include "llvm/Support/raw_ostream.h"
29#include "llvm/TableGen/CodeGenHelpers.h"
30#include "llvm/TableGen/Error.h"
31#include "llvm/TableGen/Record.h"
32#include <cassert>
33#include <optional>
34#include <string>
35
36using namespace llvm;
37using namespace mlir;
38using namespace mlir::tblgen;
39
40/// Generate a unique label based on the current file name to prevent name
41/// collisions if multiple generated files are included at once.
42static std::string getUniqueOutputLabel(const RecordKeeper &records,
43 StringRef tag) {
44 // Use the input file name when generating a unique name.
45 StringRef inputFilename = records.getInputFilename();
46
47 // Drop all but the base filename.
48 StringRef nameRef = sys::path::filename(inputFilename);
49 nameRef.consume_back(".td");
50
51 // Sanitize any invalid characters.
52 std::string uniqueName(tag);
53 for (char c : nameRef) {
54 if (isAlnum(c) || c == '_')
55 uniqueName.push_back(c);
56 else
57 uniqueName.append(utohexstr((unsigned char)c));
58 }
59 return uniqueName;
60}
61
63 raw_ostream &os, const RecordKeeper &records, StringRef tag)
64 : os(os), uniqueOutputLabel(getUniqueOutputLabel(records, tag)) {}
65
67 emitTypeConstraints();
68 emitAttrConstraints();
69 emitPropConstraints();
70 emitSuccessorConstraints();
71 emitRegionConstraints();
72}
73
75 const ArrayRef<DagLeaf> constraints) {
76 collectPatternConstraints(constraints);
78}
79
80//===----------------------------------------------------------------------===//
81// Constraint Getters
82//===----------------------------------------------------------------------===//
83
85 const Constraint &constraint) const {
86 const auto *it = typeConstraints.find(constraint);
87 assert(it != typeConstraints.end() && "expected to find a type constraint");
88 return it->second;
89}
90
91// Find a uniqued attribute constraint. Since not all attribute constraints can
92// be uniqued, return std::nullopt if one was not found.
94 const Constraint &constraint) const {
95 const auto *it = attrConstraints.find(constraint);
96 return it == attrConstraints.end() ? std::optional<StringRef>()
97 : StringRef(it->second);
98}
99
100// Find a uniqued property constraint. Since not all property constraints can
101// be uniqued, return std::nullopt if one was not found.
103 const Constraint &constraint) const {
104 const auto *it = propConstraints.find(constraint);
105 return it == propConstraints.end() ? std::optional<StringRef>()
106 : StringRef(it->second);
107}
108
110 const Constraint &constraint) const {
111 const auto *it = successorConstraints.find(constraint);
112 assert(it != successorConstraints.end() &&
113 "expected to find a sucessor constraint");
114 return it->second;
115}
116
118 const Constraint &constraint) const {
119 const auto *it = regionConstraints.find(constraint);
120 assert(it != regionConstraints.end() &&
121 "expected to find a region constraint");
122 return it->second;
123}
124
125//===----------------------------------------------------------------------===//
126// Constraint Emission
127//===----------------------------------------------------------------------===//
128
129/// Helper to generate a C++ string expression from a given message.
130/// Message can contain '{{...}}' placeholders that are substituted with
131/// C-expressions via tgfmt.
133 StringRef message, const FmtContext &ctx, ErrorStreamType errorStreamType) {
134 std::string result;
135 raw_string_ostream os(result);
136
137 std::string msgStr = escapeString(message);
138 StringRef msg = msgStr;
139
140 // Split the message by '{{' and '}}' and build a streaming expression.
141 auto split = msg.split("{{");
142 os << split.first;
143 if (split.second.empty()) {
144 return msgStr;
145 }
146
147 if (errorStreamType == ErrorStreamType::InsideOpError)
148 os << "\")";
149 else
150 os << '"';
151
152 msg = split.second;
153 while (!msg.empty()) {
154 split = msg.split("}}");
155 StringRef var = split.first;
156 StringRef rest = split.second;
157
158 os << " << " << tgfmt(var, &ctx);
159
160 if (rest.empty())
161 break;
162
163 split = rest.split("{{");
164 if (split.second.empty() &&
165 errorStreamType == ErrorStreamType::InsideOpError) {
166 // To enable having part of string post, this adds a parenthesis before
167 // the last string segment to match the existing one.
168 os << " << (\"" << split.first;
169 } else {
170 os << " << \"" << split.first;
171 }
172 msg = split.second;
173 }
174
175 return os.str();
176}
177
178/// Code templates for emitting type, attribute, successor, and region
179/// constraints. Each of these templates require the following arguments:
180///
181/// {0}: The unique constraint name.
182/// {1}: The constraint code.
183/// {2}: The constraint description.
184
185/// Code for a type constraint. These may be called on the type of either
186/// operands or results.
187static const char *const typeConstraintCode = R"(
188static ::llvm::LogicalResult {0}(
189 ::mlir::Operation *op, ::mlir::Type type, ::llvm::StringRef valueKind,
190 unsigned valueIndex) {
191 if (!({1})) {
192 return op->emitOpError(valueKind) << " #" << valueIndex
193 << " must be {2}, but got " << type;
194 }
195 return ::mlir::success();
196}
197)";
198
199/// Code for an attribute constraint. These may be called from ops only.
200/// Attribute constraints cannot reference anything other than `$_self` and
201/// `$_op`.
202///
203/// TODO: Unique constraints for adaptors. However, most Adaptor::verify
204/// functions are stripped anyways.
205static const char *const attrConstraintCode = R"(
206static ::llvm::LogicalResult {0}(
207 ::mlir::Attribute attr, ::llvm::StringRef attrName, llvm::function_ref<::mlir::InFlightDiagnostic()> emitError) {{
208 if (attr && !({1}))
209 return emitError() << "attribute '" << attrName
210 << "' failed to satisfy constraint: {2}";
211 return ::mlir::success();
212}
213static ::llvm::LogicalResult {0}(
214 ::mlir::Operation *op, ::mlir::Attribute attr, ::llvm::StringRef attrName) {{
215 return {0}(attr, attrName, [op]() {{
216 return op->emitOpError();
217 });
218}
219)";
220
221/// Code for a property constraint. These may be called from ops only.
222/// Property constraints cannot reference anything other than `$_self` and
223/// `$_op`. {3} is the interface type of the property.
224static const char *const propConstraintCode = R"(
225 static ::llvm::LogicalResult {0}(
226 {3} prop, ::llvm::StringRef propName, llvm::function_ref<::mlir::InFlightDiagnostic()> emitError) {{
227 if (!({1}))
228 return emitError() << "property '" << propName
229 << "' failed to satisfy constraint: {2}";
230 return ::mlir::success();
231 }
232 static ::llvm::LogicalResult {0}(
233 ::mlir::Operation *op, {3} prop, ::llvm::StringRef propName) {{
234 return {0}(prop, propName, [op]() {{
235 return op->emitOpError();
236 });
237 }
238 )";
239
240/// Code for a successor constraint.
241static const char *const successorConstraintCode = R"(
242static ::llvm::LogicalResult {0}(
243 ::mlir::Operation *op, ::mlir::Block *successor,
244 ::llvm::StringRef successorName, unsigned successorIndex) {
245 if (!({1})) {
246 return op->emitOpError("successor #") << successorIndex << " ('"
247 << successorName << ")' failed to verify constraint: {2}";
248 }
249 return ::mlir::success();
250}
251)";
252
253/// Code for a region constraint. Callers will need to pass in the region's name
254/// for emitting an error message.
255static const char *const regionConstraintCode = R"(
256static ::llvm::LogicalResult {0}(
257 ::mlir::Operation *op, ::mlir::Region &region, ::llvm::StringRef regionName,
258 unsigned regionIndex) {
259 if (!({1})) {
260 return op->emitOpError("region #") << regionIndex
261 << (regionName.empty() ? " " : " ('" + regionName + "') ")
262 << "failed to verify constraint: {2}";
263 }
264 return ::mlir::success();
265}
266)";
267
268/// Code for a pattern type or attribute constraint.
269///
270/// {0}: name of function
271/// {1}: Condition template
272/// {2}: Constraint summary
273/// {3}: "::mlir::Type type" or "::mlirAttribute attr" or "propType prop".
274/// Can be "T prop" for generic property constraints.
275static const char *const patternConstraintCode = R"(
276static ::llvm::LogicalResult {0}(
277 ::mlir::PatternRewriter &rewriter, ::mlir::Operation *op, {3},
278 ::llvm::StringRef failureStr) {
279 if (!({1})) {
280 return rewriter.notifyMatchFailure(op, [&](::mlir::Diagnostic &diag) {
281 diag << failureStr << ": {2}";
282 });
283 }
284 return ::mlir::success();
285}
286)";
287
288void StaticVerifierFunctionEmitter::emitConstraints(
289 const ConstraintMap &constraints, StringRef selfName,
290 const char *const codeTemplate, ErrorStreamType errorStreamType) {
291 FmtContext ctx;
292 ctx.addSubst("_op", "*op").withSelf(selfName);
293
294 for (auto &it : constraints) {
295 os << formatv(codeTemplate, it.second,
296 tgfmt(it.first.getConditionTemplate(), &ctx),
297 buildErrorStreamingString(it.first.getSummary(), ctx));
298 }
299}
300void StaticVerifierFunctionEmitter::emitTypeConstraints() {
301 emitConstraints(typeConstraints, "type", typeConstraintCode,
303}
304
305void StaticVerifierFunctionEmitter::emitAttrConstraints() {
306 emitConstraints(attrConstraints, "attr", attrConstraintCode,
308}
309
310/// Unlike with the other helpers, this one has to substitute in the interface
311/// type of the property, so we can't just use the generic function.
312void StaticVerifierFunctionEmitter::emitPropConstraints() {
313 FmtContext ctx;
314 ctx.addSubst("_op", "*op").withSelf("prop");
315 for (auto &it : propConstraints) {
316 auto propConstraint = cast<PropConstraint>(it.first);
317 os << formatv(propConstraintCode, it.second,
318 tgfmt(propConstraint.getConditionTemplate(), &ctx),
319 buildErrorStreamingString(it.first.getSummary(), ctx),
320 propConstraint.getInterfaceType());
321 }
322}
323
324void StaticVerifierFunctionEmitter::emitSuccessorConstraints() {
325 emitConstraints(successorConstraints, "successor", successorConstraintCode,
327}
328
329void StaticVerifierFunctionEmitter::emitRegionConstraints() {
330 emitConstraints(regionConstraints, "region", regionConstraintCode,
332}
333
335 FmtContext ctx;
336 ctx.addSubst("_op", "*op").withBuilder("rewriter").withSelf("type");
337 for (auto &it : typeConstraints) {
338 os << formatv(patternConstraintCode, it.second,
339 tgfmt(it.first.getConditionTemplate(), &ctx),
340 buildErrorStreamingString(it.first.getSummary(), ctx),
341 "::mlir::Type type");
342 }
343 ctx.withSelf("attr");
344 for (auto &it : attrConstraints) {
345 os << formatv(patternConstraintCode, it.second,
346 tgfmt(it.first.getConditionTemplate(), &ctx),
347 buildErrorStreamingString(it.first.getSummary(), ctx),
348 "::mlir::Attribute attr");
349 }
350 ctx.withSelf("prop");
351 for (auto &it : propConstraints) {
352 PropConstraint propConstraint = cast<PropConstraint>(it.first);
353 StringRef interfaceType = propConstraint.getInterfaceType();
354 // Constraints that are generic over multiple interface types are
355 // templatized under the assumption that they'll be used correctly.
356 if (interfaceType.empty()) {
357 interfaceType = "T";
358 os << "template <typename T>";
359 }
360 os << formatv(patternConstraintCode, it.second,
361 tgfmt(propConstraint.getConditionTemplate(), &ctx),
362 buildErrorStreamingString(propConstraint.getSummary(), ctx),
363 Twine(interfaceType) + " prop");
364 }
365}
366
367//===----------------------------------------------------------------------===//
368// Constraint Uniquing
369//===----------------------------------------------------------------------===//
370
371/// An attribute constraint that references anything other than itself and the
372/// current op cannot be generically extracted into a function. Most
373/// prohibitive are operands and results, which require calls to
374/// `getODSOperands` or `getODSResults`. Attribute references are tricky too
375/// because ops use cached identifiers.
377 FmtContext ctx;
378 auto test = tgfmt(attr.getConditionTemplate(),
379 &ctx.withSelf("attr").addSubst("_op", "*op"))
380 .str();
381 return !StringRef(test).contains("<no-subst-found>");
382}
383
384/// A property constraint that references anything other than itself and the
385/// current op cannot be generically extracted into a function, just as with
386/// canUnequePropConstraint(). Additionally, property constraints without
387/// an interface type specified can't be uniqued, and ones that are a literal
388/// "true" shouldn't be constrained.
390 FmtContext ctx;
391 auto test = tgfmt(prop.getConditionTemplate(),
392 &ctx.withSelf("prop").addSubst("_op", "*op"))
393 .str();
394 return !StringRef(test).contains("<no-subst-found>") && test != "true" &&
395 !prop.getInterfaceType().empty();
396}
397
398std::string StaticVerifierFunctionEmitter::getUniqueName(StringRef kind,
399 unsigned index) {
400 return ("__mlir_ods_local_" + kind + "_constraint_" + uniqueOutputLabel +
401 Twine(index))
402 .str();
403}
404
405void StaticVerifierFunctionEmitter::collectConstraint(ConstraintMap &map,
406 StringRef kind,
407 Constraint constraint) {
408 auto [it, inserted] = map.try_emplace(constraint);
409 if (inserted)
410 it->second = getUniqueName(kind, map.size());
411}
412
415 const auto collectTypeConstraints = [&](Operator::const_value_range values) {
416 for (const NamedTypeConstraint &value : values)
417 if (value.hasPredicate())
418 collectConstraint(typeConstraints, "type", value.constraint);
419 };
420
421 for (const Record *def : opDefs) {
422 Operator op(*def);
423 /// Collect type constraints.
424 collectTypeConstraints(op.getOperands());
425 collectTypeConstraints(op.getResults());
426 /// Collect attribute constraints.
427 for (const NamedAttribute &namedAttr : op.getAttributes()) {
428 if (!namedAttr.attr.getPredicate().isNull() &&
429 !namedAttr.attr.isDerivedAttr() &&
430 canUniqueAttrConstraint(namedAttr.attr))
431 collectConstraint(attrConstraints, "attr", namedAttr.attr);
432 }
433 /// Collect non-trivial property constraints.
434 for (const NamedProperty &namedProp : op.getProperties()) {
435 if (!namedProp.prop.getPredicate().isNull() &&
436 canUniquePropConstraint(namedProp.prop)) {
437 collectConstraint(propConstraints, "prop", namedProp.prop);
438 }
439 }
440 /// Collect successor constraints.
441 for (const NamedSuccessor &successor : op.getSuccessors()) {
442 if (!successor.constraint.getPredicate().isNull()) {
443 collectConstraint(successorConstraints, "successor",
444 successor.constraint);
445 }
446 }
447 /// Collect region constraints.
448 for (const NamedRegion &region : op.getRegions())
449 if (!region.constraint.getPredicate().isNull())
450 collectConstraint(regionConstraints, "region", region.constraint);
451 }
452}
453
454void StaticVerifierFunctionEmitter::collectPatternConstraints(
455 const ArrayRef<DagLeaf> constraints) {
456 for (auto &leaf : constraints) {
457 assert(leaf.isOperandMatcher() || leaf.isAttrMatcher() ||
458 leaf.isPropMatcher());
459 Constraint constraint = leaf.getAsConstraint();
460 if (leaf.isOperandMatcher())
461 collectConstraint(typeConstraints, "type", constraint);
462 else if (leaf.isAttrMatcher())
463 collectConstraint(attrConstraints, "attr", constraint);
464 else if (leaf.isPropMatcher())
465 collectConstraint(propConstraints, "prop", constraint);
466 }
467}
468
469//===----------------------------------------------------------------------===//
470// Public Utility Functions
471//===----------------------------------------------------------------------===//
472
473std::string mlir::tblgen::escapeString(StringRef value) {
474 std::string ret;
475 raw_string_ostream os(ret);
476 os.write_escaped(value);
477 return ret;
478}
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.
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
Attributes are known-constant values of operations.
Definition Attributes.h:25
StringRef getSummary() const
std::string getConditionTemplate() const
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_property_iterator > getProperties() const
Definition Operator.h:200
llvm::iterator_range< const_attribute_iterator > getAttributes() const
Definition Operator.cpp:333
llvm::iterator_range< const_value_iterator > const_value_range
Definition Operator.h:138
llvm::iterator_range< const_successor_iterator > getSuccessors() const
Definition Operator.cpp:301
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.
StaticVerifierFunctionEmitter(raw_ostream &os, const llvm::RecordKeeper &records, StringRef tag="")
Create a constraint uniquer with a unique prefix derived from the record keeper with an optional tag.
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.
void emitOpConstraints()
Collect and unique all compatible type, attribute, successor, and region constraints from the operati...
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
std::string buildErrorStreamingString(StringRef message, const FmtContext &ctx, ErrorStreamType errorStreamType=ErrorStreamType::InString)
Helper to generate a C++ streaming error message from a given message.
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.
ErrorStreamType
This class represents how an error stream string being constructed will be consumed.
Include the generated interface declarations.