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
14#include "mlir/TableGen/CodeGenHelpers.h"
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
21using namespace llvm;
22using namespace mlir;
23using 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.
27static 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(path: inputFilename);
34 nameRef.consume_back(Suffix: ".td");
35
36 // Sanitize any invalid characters.
37 std::string uniqueName(tag);
38 for (char c : nameRef) {
39 if (isAlnum(C: c) || c == '_')
40 uniqueName.push_back(c: c);
41 else
42 uniqueName.append(str: utohexstr(X: (unsigned char)c));
43 }
44 return uniqueName;
45}
46
47StaticVerifierFunctionEmitter::StaticVerifierFunctionEmitter(
48 raw_ostream &os, const RecordKeeper &records, StringRef tag)
49 : os(os), uniqueOutputLabel(getUniqueOutputLabel(records, tag)) {}
50
51void StaticVerifierFunctionEmitter::emitOpConstraints(
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
61void StaticVerifierFunctionEmitter::emitPatternConstraints(
62 const ArrayRef<DagLeaf> constraints) {
63 collectPatternConstraints(constraints);
64 emitPatternConstraints();
65}
66
67//===----------------------------------------------------------------------===//
68// Constraint Getters
69//===----------------------------------------------------------------------===//
70
71StringRef StaticVerifierFunctionEmitter::getTypeConstraintFn(
72 const Constraint &constraint) const {
73 const auto *it = typeConstraints.find(Key: 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.
80std::optional<StringRef> StaticVerifierFunctionEmitter::getAttrConstraintFn(
81 const Constraint &constraint) const {
82 const auto *it = attrConstraints.find(Key: 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.
89std::optional<StringRef> StaticVerifierFunctionEmitter::getPropConstraintFn(
90 const Constraint &constraint) const {
91 const auto *it = propConstraints.find(Key: constraint);
92 return it == propConstraints.end() ? std::optional<StringRef>()
93 : StringRef(it->second);
94}
95
96StringRef StaticVerifierFunctionEmitter::getSuccessorConstraintFn(
97 const Constraint &constraint) const {
98 const auto *it = successorConstraints.find(Key: constraint);
99 assert(it != successorConstraints.end() &&
100 "expected to find a sucessor constraint");
101 return it->second;
102}
103
104StringRef StaticVerifierFunctionEmitter::getRegionConstraintFn(
105 const Constraint &constraint) const {
106 const auto *it = regionConstraints.find(Key: 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.
125static const char *const typeConstraintCode = R"(
126static ::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.
143static const char *const attrConstraintCode = R"(
144static ::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}
151static ::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.
162static 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.
179static const char *const successorConstraintCode = R"(
180static ::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.
193static const char *const regionConstraintCode = R"(
194static ::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.
213static const char *const patternConstraintCode = R"(
214static ::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
226void StaticVerifierFunctionEmitter::emitConstraints(
227 const ConstraintMap &constraints, StringRef selfName,
228 const char *const codeTemplate) {
229 FmtContext ctx;
230 ctx.addSubst(placeholder: "_op", subst: "*op").withSelf(subst: selfName);
231 for (auto &it : constraints) {
232 os << formatv(Fmt: codeTemplate, Vals: it.second,
233 Vals: tgfmt(fmt: it.first.getConditionTemplate(), ctx: &ctx),
234 Vals: escapeString(value: it.first.getSummary()));
235 }
236}
237
238void StaticVerifierFunctionEmitter::emitTypeConstraints() {
239 emitConstraints(constraints: typeConstraints, selfName: "type", codeTemplate: typeConstraintCode);
240}
241
242void StaticVerifierFunctionEmitter::emitAttrConstraints() {
243 emitConstraints(constraints: attrConstraints, selfName: "attr", codeTemplate: 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.
248void StaticVerifierFunctionEmitter::emitPropConstraints() {
249 FmtContext ctx;
250 ctx.addSubst(placeholder: "_op", subst: "*op").withSelf(subst: "prop");
251 for (auto &it : propConstraints) {
252 auto propConstraint = cast<PropConstraint>(Val&: it.first);
253 os << formatv(Fmt: propConstraintCode, Vals&: it.second,
254 Vals: tgfmt(fmt: propConstraint.getConditionTemplate(), ctx: &ctx),
255 Vals: escapeString(value: it.first.getSummary()),
256 Vals: propConstraint.getInterfaceType());
257 }
258}
259
260void StaticVerifierFunctionEmitter::emitSuccessorConstraints() {
261 emitConstraints(constraints: successorConstraints, selfName: "successor", codeTemplate: successorConstraintCode);
262}
263
264void StaticVerifierFunctionEmitter::emitRegionConstraints() {
265 emitConstraints(constraints: regionConstraints, selfName: "region", codeTemplate: regionConstraintCode);
266}
267
268void StaticVerifierFunctionEmitter::emitPatternConstraints() {
269 FmtContext ctx;
270 ctx.addSubst(placeholder: "_op", subst: "*op").withBuilder(subst: "rewriter").withSelf(subst: "type");
271 for (auto &it : typeConstraints) {
272 os << formatv(Fmt: patternConstraintCode, Vals&: it.second,
273 Vals: tgfmt(fmt: it.first.getConditionTemplate(), ctx: &ctx),
274 Vals: escapeString(value: it.first.getSummary()), Vals: "::mlir::Type type");
275 }
276 ctx.withSelf(subst: "attr");
277 for (auto &it : attrConstraints) {
278 os << formatv(Fmt: patternConstraintCode, Vals&: it.second,
279 Vals: tgfmt(fmt: it.first.getConditionTemplate(), ctx: &ctx),
280 Vals: escapeString(value: it.first.getSummary()),
281 Vals: "::mlir::Attribute attr");
282 }
283 ctx.withSelf(subst: "prop");
284 for (auto &it : propConstraints) {
285 PropConstraint propConstraint = cast<PropConstraint>(Val&: 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(Fmt: patternConstraintCode, Vals&: it.second,
294 Vals: tgfmt(fmt: propConstraint.getConditionTemplate(), ctx: &ctx),
295 Vals: escapeString(value: propConstraint.getSummary()),
296 Vals: 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.
309static bool canUniqueAttrConstraint(Attribute attr) {
310 FmtContext ctx;
311 auto test = tgfmt(fmt: attr.getConditionTemplate(),
312 ctx: &ctx.withSelf(subst: "attr").addSubst(placeholder: "_op", subst: "*op"))
313 .str();
314 return !StringRef(test).contains(Other: "<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.
322static bool canUniquePropConstraint(Property prop) {
323 FmtContext ctx;
324 auto test = tgfmt(fmt: prop.getConditionTemplate(),
325 ctx: &ctx.withSelf(subst: "prop").addSubst(placeholder: "_op", subst: "*op"))
326 .str();
327 return !StringRef(test).contains(Other: "<no-subst-found>") && test != "true" &&
328 !prop.getInterfaceType().empty();
329}
330
331std::string StaticVerifierFunctionEmitter::getUniqueName(StringRef kind,
332 unsigned index) {
333 return ("__mlir_ods_local_" + kind + "_constraint_" + uniqueOutputLabel +
334 Twine(index))
335 .str();
336}
337
338void StaticVerifierFunctionEmitter::collectConstraint(ConstraintMap &map,
339 StringRef kind,
340 Constraint constraint) {
341 auto [it, inserted] = map.try_emplace(Key: constraint);
342 if (inserted)
343 it->second = getUniqueName(kind, index: map.size());
344}
345
346void StaticVerifierFunctionEmitter::collectOpConstraints(
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(map&: typeConstraints, kind: "type", constraint: 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(attr: namedAttr.attr))
364 collectConstraint(map&: attrConstraints, kind: "attr", constraint: namedAttr.attr);
365 }
366 /// Collect non-trivial property constraints.
367 for (const NamedProperty &namedProp : op.getProperties()) {
368 if (!namedProp.prop.getPredicate().isNull() &&
369 canUniquePropConstraint(prop: namedProp.prop)) {
370 collectConstraint(map&: propConstraints, kind: "prop", constraint: namedProp.prop);
371 }
372 }
373 /// Collect successor constraints.
374 for (const NamedSuccessor &successor : op.getSuccessors()) {
375 if (!successor.constraint.getPredicate().isNull()) {
376 collectConstraint(map&: successorConstraints, kind: "successor",
377 constraint: successor.constraint);
378 }
379 }
380 /// Collect region constraints.
381 for (const NamedRegion &region : op.getRegions())
382 if (!region.constraint.getPredicate().isNull())
383 collectConstraint(map&: regionConstraints, kind: "region", constraint: region.constraint);
384 }
385}
386
387void 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(map&: typeConstraints, kind: "type", constraint);
395 else if (leaf.isAttrMatcher())
396 collectConstraint(map&: attrConstraints, kind: "attr", constraint);
397 else if (leaf.isPropMatcher())
398 collectConstraint(map&: propConstraints, kind: "prop", constraint);
399 }
400}
401
402//===----------------------------------------------------------------------===//
403// Public Utility Functions
404//===----------------------------------------------------------------------===//
405
406std::string mlir::tblgen::escapeString(StringRef value) {
407 std::string ret;
408 raw_string_ostream os(ret);
409 os.write_escaped(Str: value);
410 return ret;
411}
412

source code of mlir/lib/TableGen/CodeGenHelpers.cpp