MLIR 24.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 (errorStreamType == ErrorStreamType::InsideOpError) {
161 // To enable having part of string post, this adds a parenthesis before
162 // the last string segment to match the existing one.
163 os << " << (\"";
164 } else {
165 os << " << \"";
166 }
167
168 split = rest.split("{{");
169 os << split.first;
170 msg = split.second;
171 }
172
173 return os.str();
174}
175
176/// Code templates for emitting type, attribute, successor, and region
177/// constraints. Each of these templates require the following arguments:
178///
179/// {0}: The unique constraint name.
180/// {1}: The constraint code.
181/// {2}: The constraint description.
182
183/// Code for a type constraint. These may be called on the type of either
184/// operands or results.
185static const char *const typeConstraintCode = R"(
186static ::llvm::LogicalResult {0}(
187 ::mlir::Operation *op, ::mlir::Type type, ::llvm::StringRef valueKind,
188 unsigned valueIndex) {
189 if (!({1})) {
190 return op->emitOpError(valueKind) << " #" << valueIndex
191 << " must be {2}, but got " << type;
192 }
193 return ::mlir::success();
194}
195)";
196
197/// Code for an attribute constraint. These may be called from ops only.
198/// Attribute constraints cannot reference anything other than `$_self` and
199/// `$_op`.
200///
201/// TODO: Unique constraints for adaptors. However, most Adaptor::verify
202/// functions are stripped anyways.
203static const char *const attrConstraintCode = R"(
204static ::llvm::LogicalResult {0}(
205 ::mlir::Attribute attr, ::llvm::StringRef attrName, llvm::function_ref<::mlir::InFlightDiagnostic()> emitError) {{
206 if (attr && !({1}))
207 return emitError() << "attribute '" << attrName
208 << "' failed to satisfy constraint: {2}";
209 return ::mlir::success();
210}
211static ::llvm::LogicalResult {0}(
212 ::mlir::Operation *op, ::mlir::Attribute attr, ::llvm::StringRef attrName) {{
213 return {0}(attr, attrName, [op]() {{
214 return op->emitOpError();
215 });
216}
217)";
218
219/// Code for a property constraint. These may be called from ops only.
220/// Property constraints cannot reference anything other than `$_self` and
221/// `$_op`. {3} is the interface type of the property.
222static const char *const propConstraintCode = R"(
223 static ::llvm::LogicalResult {0}(
224 {3} prop, ::llvm::StringRef propName, llvm::function_ref<::mlir::InFlightDiagnostic()> emitError) {{
225 if (!({1}))
226 return emitError() << "property '" << propName
227 << "' failed to satisfy constraint: {2}";
228 return ::mlir::success();
229 }
230 static ::llvm::LogicalResult {0}(
231 ::mlir::Operation *op, {3} prop, ::llvm::StringRef propName) {{
232 return {0}(prop, propName, [op]() {{
233 return op->emitOpError();
234 });
235 }
236 )";
237
238/// Code for a successor constraint.
239static const char *const successorConstraintCode = R"(
240static ::llvm::LogicalResult {0}(
241 ::mlir::Operation *op, ::mlir::Block *successor,
242 ::llvm::StringRef successorName, unsigned successorIndex) {
243 if (!({1})) {
244 return op->emitOpError("successor #") << successorIndex << " ('"
245 << successorName << "') failed to verify constraint: {2}";
246 }
247 return ::mlir::success();
248}
249)";
250
251/// Code for a region constraint. Callers will need to pass in the region's name
252/// for emitting an error message.
253static const char *const regionConstraintCode = R"(
254static ::llvm::LogicalResult {0}(
255 ::mlir::Operation *op, ::mlir::Region &region, ::llvm::StringRef regionName,
256 unsigned regionIndex) {
257 if (!({1})) {
258 return op->emitOpError("region #") << regionIndex
259 << (regionName.empty() ? " " : " ('" + regionName + "') ")
260 << "failed to verify constraint: {2}";
261 }
262 return ::mlir::success();
263}
264)";
265
266/// Code for a pattern type or attribute constraint.
267///
268/// {0}: name of function
269/// {1}: Condition template
270/// {2}: Constraint summary
271/// {3}: "::mlir::Type type" or "::mlirAttribute attr" or "propType prop".
272/// Can be "T prop" for generic property constraints.
273static const char *const patternConstraintCode = R"(
274static ::llvm::LogicalResult {0}(
275 ::mlir::PatternRewriter &rewriter, ::mlir::Operation *op, {3},
276 ::llvm::StringRef failureStr) {
277 if (!({1})) {
278 return rewriter.notifyMatchFailure(op, [&](::mlir::Diagnostic &diag) {
279 diag << failureStr << ": {2}";
280 });
281 }
282 return ::mlir::success();
283}
284)";
285
286void StaticVerifierFunctionEmitter::emitConstraints(
287 const ConstraintMap &constraints, StringRef selfName,
288 const char *const codeTemplate, ErrorStreamType errorStreamType) {
289 FmtContext ctx;
290 ctx.addSubst("_op", "*op").withSelf(selfName);
291
292 for (auto &it : constraints) {
293 os << formatv(codeTemplate, it.second,
294 tgfmt(it.first.getConditionTemplate(), &ctx),
295 buildErrorStreamingString(it.first.getSummary(), ctx));
296 }
297}
298void StaticVerifierFunctionEmitter::emitTypeConstraints() {
299 emitConstraints(typeConstraints, "type", typeConstraintCode,
301}
302
303void StaticVerifierFunctionEmitter::emitAttrConstraints() {
304 emitConstraints(attrConstraints, "attr", attrConstraintCode,
306}
307
308/// Unlike with the other helpers, this one has to substitute in the interface
309/// type of the property, so we can't just use the generic function.
310void StaticVerifierFunctionEmitter::emitPropConstraints() {
311 FmtContext ctx;
312 ctx.addSubst("_op", "*op").withSelf("prop");
313 for (auto &it : propConstraints) {
314 auto propConstraint = cast<PropConstraint>(it.first);
315 os << formatv(propConstraintCode, it.second,
316 tgfmt(propConstraint.getConditionTemplate(), &ctx),
317 buildErrorStreamingString(it.first.getSummary(), ctx),
318 propConstraint.getInterfaceType());
319 }
320}
321
322void StaticVerifierFunctionEmitter::emitSuccessorConstraints() {
323 emitConstraints(successorConstraints, "successor", successorConstraintCode,
325}
326
327void StaticVerifierFunctionEmitter::emitRegionConstraints() {
328 emitConstraints(regionConstraints, "region", regionConstraintCode,
330}
331
333 FmtContext ctx;
334 ctx.addSubst("_op", "*op").withBuilder("rewriter").withSelf("type");
335 for (auto &it : typeConstraints) {
336 os << formatv(patternConstraintCode, it.second,
337 tgfmt(it.first.getConditionTemplate(), &ctx),
338 buildErrorStreamingString(it.first.getSummary(), ctx),
339 "::mlir::Type type");
340 }
341 ctx.withSelf("attr");
342 for (auto &it : attrConstraints) {
343 os << formatv(patternConstraintCode, it.second,
344 tgfmt(it.first.getConditionTemplate(), &ctx),
345 buildErrorStreamingString(it.first.getSummary(), ctx),
346 "::mlir::Attribute attr");
347 }
348 ctx.withSelf("prop");
349 for (auto &it : propConstraints) {
350 PropConstraint propConstraint = cast<PropConstraint>(it.first);
351 StringRef interfaceType = propConstraint.getInterfaceType();
352 // Constraints that are generic over multiple interface types are
353 // templatized under the assumption that they'll be used correctly.
354 if (interfaceType.empty()) {
355 interfaceType = "T";
356 os << "template <typename T>";
357 }
358 os << formatv(patternConstraintCode, it.second,
359 tgfmt(propConstraint.getConditionTemplate(), &ctx),
360 buildErrorStreamingString(propConstraint.getSummary(), ctx),
361 Twine(interfaceType) + " prop");
362 }
363}
364
365//===----------------------------------------------------------------------===//
366// Constraint Uniquing
367//===----------------------------------------------------------------------===//
368
369/// An attribute constraint that references anything other than itself and the
370/// current op cannot be generically extracted into a function. Most
371/// prohibitive are operands and results, which require calls to
372/// `getODSOperands` or `getODSResults`. Attribute references are tricky too
373/// because ops use cached identifiers.
375 FmtContext ctx;
376 auto test = tgfmt(attr.getConditionTemplate(),
377 &ctx.withSelf("attr").addSubst("_op", "*op"))
378 .str();
379 return !StringRef(test).contains("<no-subst-found>");
380}
381
382/// A property constraint that references anything other than itself and the
383/// current op cannot be generically extracted into a function, just as with
384/// canUnequePropConstraint(). Additionally, property constraints without
385/// an interface type specified can't be uniqued, and ones that are a literal
386/// "true" shouldn't be constrained.
388 FmtContext ctx;
389 auto test = tgfmt(prop.getConditionTemplate(),
390 &ctx.withSelf("prop").addSubst("_op", "*op"))
391 .str();
392 return !StringRef(test).contains("<no-subst-found>") && test != "true" &&
393 !prop.getInterfaceType().empty();
394}
395
396std::string StaticVerifierFunctionEmitter::getUniqueName(StringRef kind,
397 unsigned index) {
398 return ("__mlir_ods_local_" + kind + "_constraint_" + uniqueOutputLabel +
399 Twine(index))
400 .str();
401}
402
403void StaticVerifierFunctionEmitter::collectConstraint(ConstraintMap &map,
404 StringRef kind,
405 Constraint constraint) {
406 auto [it, inserted] = map.try_emplace(constraint);
407 if (inserted)
408 it->second = getUniqueName(kind, map.size());
409}
410
413 const auto collectTypeConstraints = [&](Operator::const_value_range values) {
414 for (const NamedTypeConstraint &value : values)
415 if (value.hasPredicate())
416 collectConstraint(typeConstraints, "type", value.constraint);
417 };
418
419 for (const Record *def : opDefs) {
420 Operator op(*def);
421 /// Collect type constraints.
422 collectTypeConstraints(op.getOperands());
423 collectTypeConstraints(op.getResults());
424 /// Collect attribute constraints.
425 for (const NamedAttribute &namedAttr : op.getAttributes()) {
426 if (!namedAttr.attr.getPredicate().isNull() &&
427 !namedAttr.attr.isDerivedAttr() &&
428 canUniqueAttrConstraint(namedAttr.attr))
429 collectConstraint(attrConstraints, "attr", namedAttr.attr);
430 }
431 /// Collect non-trivial property constraints.
432 for (const NamedProperty &namedProp : op.getProperties()) {
433 if (!namedProp.prop.getPredicate().isNull() &&
434 canUniquePropConstraint(namedProp.prop)) {
435 collectConstraint(propConstraints, "prop", namedProp.prop);
436 }
437 }
438 /// Collect successor constraints.
439 for (const NamedSuccessor &successor : op.getSuccessors()) {
440 if (!successor.constraint.getPredicate().isNull()) {
441 collectConstraint(successorConstraints, "successor",
442 successor.constraint);
443 }
444 }
445 /// Collect region constraints.
446 for (const NamedRegion &region : op.getRegions())
447 if (!region.constraint.getPredicate().isNull())
448 collectConstraint(regionConstraints, "region", region.constraint);
449 }
450}
451
452void StaticVerifierFunctionEmitter::collectPatternConstraints(
453 const ArrayRef<DagLeaf> constraints) {
454 for (auto &leaf : constraints) {
455 assert(leaf.isOperandMatcher() || leaf.isAttrMatcher() ||
456 leaf.isPropMatcher());
457 Constraint constraint = leaf.getAsConstraint();
458 if (leaf.isOperandMatcher())
459 collectConstraint(typeConstraints, "type", constraint);
460 else if (leaf.isAttrMatcher())
461 collectConstraint(attrConstraints, "attr", constraint);
462 else if (leaf.isPropMatcher())
463 collectConstraint(propConstraints, "prop", constraint);
464 }
465}
466
467//===----------------------------------------------------------------------===//
468// Public Utility Functions
469//===----------------------------------------------------------------------===//
470
471std::string mlir::tblgen::escapeString(StringRef value) {
472 std::string ret;
473 raw_string_ostream os(ret);
474 os.write_escaped(value);
475 return ret;
476}
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:227
std::string buildErrorStreamingString(StringRef message, const FmtContext &ctx, ErrorStreamType errorStreamType=ErrorStreamType::InString)
Helper to generate a C++ streaming error message from a given message.
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.
auto tgfmt(StringRef fmt, const FmtContext *ctx, Ts &&...vals) -> FmtObject< decltype(std::make_tuple(llvm::support::detail::FormatFunctor< Ts >(std::forward< Ts >(vals))...))>
Formats text by substituting placeholders in format string with replacement parameters.
Definition Format.h:255
Include the generated interface declarations.