xref: /llvm-project/mlir/lib/TableGen/CodeGenHelpers.cpp (revision 659192b1843c4af180700783caca4cdc7afa3eab)
1f570cc17SMarkus Böck //===- CodeGenHelpers.cpp - MLIR op definitions generator ---------------===//
2f570cc17SMarkus Böck //
3f570cc17SMarkus Böck // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4f570cc17SMarkus Böck // See https://llvm.org/LICENSE.txt for license information.
5f570cc17SMarkus Böck // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6f570cc17SMarkus Böck //
7f570cc17SMarkus Böck //===----------------------------------------------------------------------===//
8f570cc17SMarkus Böck //
9f570cc17SMarkus Böck // OpDefinitionsGen uses the description of operations to generate C++
10f570cc17SMarkus Böck // definitions for ops.
11f570cc17SMarkus Böck //
12f570cc17SMarkus Böck //===----------------------------------------------------------------------===//
13f570cc17SMarkus Böck 
14f570cc17SMarkus Böck #include "mlir/TableGen/CodeGenHelpers.h"
15f570cc17SMarkus Böck #include "mlir/TableGen/Operator.h"
16f570cc17SMarkus Böck #include "mlir/TableGen/Pattern.h"
17f570cc17SMarkus Böck #include "llvm/Support/FormatVariadic.h"
18f570cc17SMarkus Böck #include "llvm/Support/Path.h"
19f570cc17SMarkus Böck #include "llvm/TableGen/Record.h"
20f570cc17SMarkus Böck 
21f570cc17SMarkus Böck using namespace llvm;
22f570cc17SMarkus Böck using namespace mlir;
23f570cc17SMarkus Böck using namespace mlir::tblgen;
24f570cc17SMarkus Böck 
25f570cc17SMarkus Böck /// Generate a unique label based on the current file name to prevent name
26f570cc17SMarkus Böck /// collisions if multiple generated files are included at once.
27*659192b1SRahul Joshi static std::string getUniqueOutputLabel(const RecordKeeper &records,
281b232fa0SJeff Niu                                         StringRef tag) {
29f570cc17SMarkus Böck   // Use the input file name when generating a unique name.
30f570cc17SMarkus Böck   std::string inputFilename = records.getInputFilename();
31f570cc17SMarkus Böck 
32f570cc17SMarkus Böck   // Drop all but the base filename.
33*659192b1SRahul Joshi   StringRef nameRef = sys::path::filename(inputFilename);
34f570cc17SMarkus Böck   nameRef.consume_back(".td");
35f570cc17SMarkus Böck 
36f570cc17SMarkus Böck   // Sanitize any invalid characters.
371b232fa0SJeff Niu   std::string uniqueName(tag);
38f570cc17SMarkus Böck   for (char c : nameRef) {
39*659192b1SRahul Joshi     if (isAlnum(c) || c == '_')
40f570cc17SMarkus Böck       uniqueName.push_back(c);
41f570cc17SMarkus Böck     else
42*659192b1SRahul Joshi       uniqueName.append(utohexstr((unsigned char)c));
43f570cc17SMarkus Böck   }
44f570cc17SMarkus Böck   return uniqueName;
45f570cc17SMarkus Böck }
46f570cc17SMarkus Böck 
47f570cc17SMarkus Böck StaticVerifierFunctionEmitter::StaticVerifierFunctionEmitter(
48*659192b1SRahul Joshi     raw_ostream &os, const RecordKeeper &records, StringRef tag)
491b232fa0SJeff Niu     : os(os), uniqueOutputLabel(getUniqueOutputLabel(records, tag)) {}
50f570cc17SMarkus Böck 
51f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::emitOpConstraints(
52*659192b1SRahul Joshi     ArrayRef<const Record *> opDefs) {
53f570cc17SMarkus Böck   NamespaceEmitter namespaceEmitter(os, Operator(*opDefs[0]).getCppNamespace());
54f570cc17SMarkus Böck   emitTypeConstraints();
55f570cc17SMarkus Böck   emitAttrConstraints();
56f570cc17SMarkus Böck   emitSuccessorConstraints();
57f570cc17SMarkus Böck   emitRegionConstraints();
58f570cc17SMarkus Böck }
59f570cc17SMarkus Böck 
60f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::emitPatternConstraints(
61*659192b1SRahul Joshi     const ArrayRef<DagLeaf> constraints) {
62f570cc17SMarkus Böck   collectPatternConstraints(constraints);
63f570cc17SMarkus Böck   emitPatternConstraints();
64f570cc17SMarkus Böck }
65f570cc17SMarkus Böck 
66f570cc17SMarkus Böck //===----------------------------------------------------------------------===//
67f570cc17SMarkus Böck // Constraint Getters
68f570cc17SMarkus Böck 
69f570cc17SMarkus Böck StringRef StaticVerifierFunctionEmitter::getTypeConstraintFn(
70f570cc17SMarkus Böck     const Constraint &constraint) const {
71dfe17596SMehdi Amini   const auto *it = typeConstraints.find(constraint);
72f570cc17SMarkus Böck   assert(it != typeConstraints.end() && "expected to find a type constraint");
73f570cc17SMarkus Böck   return it->second;
74f570cc17SMarkus Böck }
75f570cc17SMarkus Böck 
76f570cc17SMarkus Böck // Find a uniqued attribute constraint. Since not all attribute constraints can
7770c73d1bSKazu Hirata // be uniqued, return std::nullopt if one was not found.
783cfe412eSFangrui Song std::optional<StringRef> StaticVerifierFunctionEmitter::getAttrConstraintFn(
79f570cc17SMarkus Böck     const Constraint &constraint) const {
80dfe17596SMehdi Amini   const auto *it = attrConstraints.find(constraint);
813cfe412eSFangrui Song   return it == attrConstraints.end() ? std::optional<StringRef>()
82f570cc17SMarkus Böck                                      : StringRef(it->second);
83f570cc17SMarkus Böck }
84f570cc17SMarkus Böck 
85f570cc17SMarkus Böck StringRef StaticVerifierFunctionEmitter::getSuccessorConstraintFn(
86f570cc17SMarkus Böck     const Constraint &constraint) const {
87dfe17596SMehdi Amini   const auto *it = successorConstraints.find(constraint);
88f570cc17SMarkus Böck   assert(it != successorConstraints.end() &&
89f570cc17SMarkus Böck          "expected to find a sucessor constraint");
90f570cc17SMarkus Böck   return it->second;
91f570cc17SMarkus Böck }
92f570cc17SMarkus Böck 
93f570cc17SMarkus Böck StringRef StaticVerifierFunctionEmitter::getRegionConstraintFn(
94f570cc17SMarkus Böck     const Constraint &constraint) const {
95dfe17596SMehdi Amini   const auto *it = regionConstraints.find(constraint);
96f570cc17SMarkus Böck   assert(it != regionConstraints.end() &&
97f570cc17SMarkus Böck          "expected to find a region constraint");
98f570cc17SMarkus Böck   return it->second;
99f570cc17SMarkus Böck }
100f570cc17SMarkus Böck 
101f570cc17SMarkus Böck //===----------------------------------------------------------------------===//
102f570cc17SMarkus Böck // Constraint Emission
103f570cc17SMarkus Böck 
104f570cc17SMarkus Böck /// Code templates for emitting type, attribute, successor, and region
105f570cc17SMarkus Böck /// constraints. Each of these templates require the following arguments:
106f570cc17SMarkus Böck ///
107f570cc17SMarkus Böck /// {0}: The unique constraint name.
108f570cc17SMarkus Böck /// {1}: The constraint code.
109f570cc17SMarkus Böck /// {2}: The constraint description.
110f570cc17SMarkus Böck 
111f570cc17SMarkus Böck /// Code for a type constraint. These may be called on the type of either
112f570cc17SMarkus Böck /// operands or results.
113f570cc17SMarkus Böck static const char *const typeConstraintCode = R"(
114db791b27SRamkumar Ramachandra static ::llvm::LogicalResult {0}(
115f570cc17SMarkus Böck     ::mlir::Operation *op, ::mlir::Type type, ::llvm::StringRef valueKind,
116f570cc17SMarkus Böck     unsigned valueIndex) {
117f570cc17SMarkus Böck   if (!({1})) {
118f570cc17SMarkus Böck     return op->emitOpError(valueKind) << " #" << valueIndex
119f570cc17SMarkus Böck         << " must be {2}, but got " << type;
120f570cc17SMarkus Böck   }
121f570cc17SMarkus Böck   return ::mlir::success();
122f570cc17SMarkus Böck }
123f570cc17SMarkus Böck )";
124f570cc17SMarkus Böck 
125f570cc17SMarkus Böck /// Code for an attribute constraint. These may be called from ops only.
126f570cc17SMarkus Böck /// Attribute constraints cannot reference anything other than `$_self` and
127f570cc17SMarkus Böck /// `$_op`.
128f570cc17SMarkus Böck ///
129f570cc17SMarkus Böck /// TODO: Unique constraints for adaptors. However, most Adaptor::verify
130f570cc17SMarkus Böck /// functions are stripped anyways.
131f570cc17SMarkus Böck static const char *const attrConstraintCode = R"(
132db791b27SRamkumar Ramachandra static ::llvm::LogicalResult {0}(
133c50617daSMehdi Amini     ::mlir::Attribute attr, ::llvm::StringRef attrName, llvm::function_ref<::mlir::InFlightDiagnostic()> emitError) {{
1345e118f93SMehdi Amini   if (attr && !({1}))
135c50617daSMehdi Amini     return emitError() << "attribute '" << attrName
136f570cc17SMarkus Böck         << "' failed to satisfy constraint: {2}";
1371e853421SMehdi Amini   return ::mlir::success();
138d572cd1bSMehdi Amini }
139db791b27SRamkumar Ramachandra static ::llvm::LogicalResult {0}(
1405e118f93SMehdi Amini     ::mlir::Operation *op, ::mlir::Attribute attr, ::llvm::StringRef attrName) {{
1415e118f93SMehdi Amini   return {0}(attr, attrName, [op]() {{
1425e118f93SMehdi Amini     return op->emitOpError();
1435e118f93SMehdi Amini   });
1445e118f93SMehdi Amini }
145f570cc17SMarkus Böck )";
146f570cc17SMarkus Böck 
147f570cc17SMarkus Böck /// Code for a successor constraint.
148f570cc17SMarkus Böck static const char *const successorConstraintCode = R"(
149db791b27SRamkumar Ramachandra static ::llvm::LogicalResult {0}(
150f570cc17SMarkus Böck     ::mlir::Operation *op, ::mlir::Block *successor,
151f570cc17SMarkus Böck     ::llvm::StringRef successorName, unsigned successorIndex) {
152f570cc17SMarkus Böck   if (!({1})) {
153f570cc17SMarkus Böck     return op->emitOpError("successor #") << successorIndex << " ('"
154f570cc17SMarkus Böck         << successorName << ")' failed to verify constraint: {2}";
155f570cc17SMarkus Böck   }
156f570cc17SMarkus Böck   return ::mlir::success();
157f570cc17SMarkus Böck }
158f570cc17SMarkus Böck )";
159f570cc17SMarkus Böck 
160f570cc17SMarkus Böck /// Code for a region constraint. Callers will need to pass in the region's name
161f570cc17SMarkus Böck /// for emitting an error message.
162f570cc17SMarkus Böck static const char *const regionConstraintCode = R"(
163db791b27SRamkumar Ramachandra static ::llvm::LogicalResult {0}(
164f570cc17SMarkus Böck     ::mlir::Operation *op, ::mlir::Region &region, ::llvm::StringRef regionName,
165f570cc17SMarkus Böck     unsigned regionIndex) {
166f570cc17SMarkus Böck   if (!({1})) {
167f570cc17SMarkus Böck     return op->emitOpError("region #") << regionIndex
168f570cc17SMarkus Böck         << (regionName.empty() ? " " : " ('" + regionName + "') ")
169f570cc17SMarkus Böck         << "failed to verify constraint: {2}";
170f570cc17SMarkus Böck   }
171f570cc17SMarkus Böck   return ::mlir::success();
172f570cc17SMarkus Böck }
173f570cc17SMarkus Böck )";
174f570cc17SMarkus Böck 
175f570cc17SMarkus Böck /// Code for a pattern type or attribute constraint.
176f570cc17SMarkus Böck ///
177f570cc17SMarkus Böck /// {3}: "Type type" or "Attribute attr".
178f570cc17SMarkus Böck static const char *const patternAttrOrTypeConstraintCode = R"(
179db791b27SRamkumar Ramachandra static ::llvm::LogicalResult {0}(
180f570cc17SMarkus Böck     ::mlir::PatternRewriter &rewriter, ::mlir::Operation *op, ::mlir::{3},
181f570cc17SMarkus Böck     ::llvm::StringRef failureStr) {
182f570cc17SMarkus Böck   if (!({1})) {
183f570cc17SMarkus Böck     return rewriter.notifyMatchFailure(op, [&](::mlir::Diagnostic &diag) {
184f570cc17SMarkus Böck       diag << failureStr << ": {2}";
185f570cc17SMarkus Böck     });
186f570cc17SMarkus Böck   }
187f570cc17SMarkus Böck   return ::mlir::success();
188f570cc17SMarkus Böck }
189f570cc17SMarkus Böck )";
190f570cc17SMarkus Böck 
191f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::emitConstraints(
192f570cc17SMarkus Böck     const ConstraintMap &constraints, StringRef selfName,
193f570cc17SMarkus Böck     const char *const codeTemplate) {
194f570cc17SMarkus Böck   FmtContext ctx;
1955cdc2bbcSRiver Riddle   ctx.addSubst("_op", "*op").withSelf(selfName);
196f570cc17SMarkus Böck   for (auto &it : constraints) {
197f570cc17SMarkus Böck     os << formatv(codeTemplate, it.second,
198f570cc17SMarkus Böck                   tgfmt(it.first.getConditionTemplate(), &ctx),
199f570cc17SMarkus Böck                   escapeString(it.first.getSummary()));
200f570cc17SMarkus Böck   }
201f570cc17SMarkus Böck }
202f570cc17SMarkus Böck 
203f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::emitTypeConstraints() {
204f570cc17SMarkus Böck   emitConstraints(typeConstraints, "type", typeConstraintCode);
205f570cc17SMarkus Böck }
206f570cc17SMarkus Böck 
207f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::emitAttrConstraints() {
208f570cc17SMarkus Böck   emitConstraints(attrConstraints, "attr", attrConstraintCode);
209f570cc17SMarkus Böck }
210f570cc17SMarkus Böck 
211f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::emitSuccessorConstraints() {
212f570cc17SMarkus Böck   emitConstraints(successorConstraints, "successor", successorConstraintCode);
213f570cc17SMarkus Böck }
214f570cc17SMarkus Böck 
215f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::emitRegionConstraints() {
216f570cc17SMarkus Böck   emitConstraints(regionConstraints, "region", regionConstraintCode);
217f570cc17SMarkus Böck }
218f570cc17SMarkus Böck 
219f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::emitPatternConstraints() {
220f570cc17SMarkus Böck   FmtContext ctx;
2215cdc2bbcSRiver Riddle   ctx.addSubst("_op", "*op").withBuilder("rewriter").withSelf("type");
222f570cc17SMarkus Böck   for (auto &it : typeConstraints) {
223f570cc17SMarkus Böck     os << formatv(patternAttrOrTypeConstraintCode, it.second,
224f570cc17SMarkus Böck                   tgfmt(it.first.getConditionTemplate(), &ctx),
225f570cc17SMarkus Böck                   escapeString(it.first.getSummary()), "Type type");
226f570cc17SMarkus Böck   }
227f570cc17SMarkus Böck   ctx.withSelf("attr");
228f570cc17SMarkus Böck   for (auto &it : attrConstraints) {
229f570cc17SMarkus Böck     os << formatv(patternAttrOrTypeConstraintCode, it.second,
230f570cc17SMarkus Böck                   tgfmt(it.first.getConditionTemplate(), &ctx),
231f570cc17SMarkus Böck                   escapeString(it.first.getSummary()), "Attribute attr");
232f570cc17SMarkus Böck   }
233f570cc17SMarkus Böck }
234f570cc17SMarkus Böck 
235f570cc17SMarkus Böck //===----------------------------------------------------------------------===//
236f570cc17SMarkus Böck // Constraint Uniquing
237f570cc17SMarkus Böck 
238f570cc17SMarkus Böck /// An attribute constraint that references anything other than itself and the
239f570cc17SMarkus Böck /// current op cannot be generically extracted into a function. Most
240f570cc17SMarkus Böck /// prohibitive are operands and results, which require calls to
241f570cc17SMarkus Böck /// `getODSOperands` or `getODSResults`. Attribute references are tricky too
242f570cc17SMarkus Böck /// because ops use cached identifiers.
243f570cc17SMarkus Böck static bool canUniqueAttrConstraint(Attribute attr) {
244f570cc17SMarkus Böck   FmtContext ctx;
2455cdc2bbcSRiver Riddle   auto test = tgfmt(attr.getConditionTemplate(),
2465cdc2bbcSRiver Riddle                     &ctx.withSelf("attr").addSubst("_op", "*op"))
247f570cc17SMarkus Böck                   .str();
248f570cc17SMarkus Böck   return !StringRef(test).contains("<no-subst-found>");
249f570cc17SMarkus Böck }
250f570cc17SMarkus Böck 
251f570cc17SMarkus Böck std::string StaticVerifierFunctionEmitter::getUniqueName(StringRef kind,
252f570cc17SMarkus Böck                                                          unsigned index) {
253f570cc17SMarkus Böck   return ("__mlir_ods_local_" + kind + "_constraint_" + uniqueOutputLabel +
254f570cc17SMarkus Böck           Twine(index))
255f570cc17SMarkus Böck       .str();
256f570cc17SMarkus Böck }
257f570cc17SMarkus Böck 
258f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::collectConstraint(ConstraintMap &map,
259f570cc17SMarkus Böck                                                       StringRef kind,
260f570cc17SMarkus Böck                                                       Constraint constraint) {
26118b39497SKazu Hirata   auto [it, inserted] = map.try_emplace(constraint);
26218b39497SKazu Hirata   if (inserted)
26318b39497SKazu Hirata     it->second = getUniqueName(kind, map.size());
264f570cc17SMarkus Böck }
265f570cc17SMarkus Böck 
266f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::collectOpConstraints(
267b60c6cbcSRahul Joshi     ArrayRef<const Record *> opDefs) {
268f570cc17SMarkus Böck   const auto collectTypeConstraints = [&](Operator::const_value_range values) {
269f570cc17SMarkus Böck     for (const NamedTypeConstraint &value : values)
270f570cc17SMarkus Böck       if (value.hasPredicate())
271f570cc17SMarkus Böck         collectConstraint(typeConstraints, "type", value.constraint);
272f570cc17SMarkus Böck   };
273f570cc17SMarkus Böck 
274b60c6cbcSRahul Joshi   for (const Record *def : opDefs) {
275f570cc17SMarkus Böck     Operator op(*def);
276f570cc17SMarkus Böck     /// Collect type constraints.
277f570cc17SMarkus Böck     collectTypeConstraints(op.getOperands());
278f570cc17SMarkus Böck     collectTypeConstraints(op.getResults());
279f570cc17SMarkus Böck     /// Collect attribute constraints.
280f570cc17SMarkus Böck     for (const NamedAttribute &namedAttr : op.getAttributes()) {
281f570cc17SMarkus Böck       if (!namedAttr.attr.getPredicate().isNull() &&
282f570cc17SMarkus Böck           !namedAttr.attr.isDerivedAttr() &&
283f570cc17SMarkus Böck           canUniqueAttrConstraint(namedAttr.attr))
284f570cc17SMarkus Böck         collectConstraint(attrConstraints, "attr", namedAttr.attr);
285f570cc17SMarkus Böck     }
286f570cc17SMarkus Böck     /// Collect successor constraints.
287f570cc17SMarkus Böck     for (const NamedSuccessor &successor : op.getSuccessors()) {
288f570cc17SMarkus Böck       if (!successor.constraint.getPredicate().isNull()) {
289f570cc17SMarkus Böck         collectConstraint(successorConstraints, "successor",
290f570cc17SMarkus Böck                           successor.constraint);
291f570cc17SMarkus Böck       }
292f570cc17SMarkus Böck     }
293f570cc17SMarkus Böck     /// Collect region constraints.
294f570cc17SMarkus Böck     for (const NamedRegion &region : op.getRegions())
295f570cc17SMarkus Böck       if (!region.constraint.getPredicate().isNull())
296f570cc17SMarkus Böck         collectConstraint(regionConstraints, "region", region.constraint);
297f570cc17SMarkus Böck   }
298f570cc17SMarkus Böck }
299f570cc17SMarkus Böck 
300f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::collectPatternConstraints(
301*659192b1SRahul Joshi     const ArrayRef<DagLeaf> constraints) {
302f570cc17SMarkus Böck   for (auto &leaf : constraints) {
303f570cc17SMarkus Böck     assert(leaf.isOperandMatcher() || leaf.isAttrMatcher());
304f570cc17SMarkus Böck     collectConstraint(
305f570cc17SMarkus Böck         leaf.isOperandMatcher() ? typeConstraints : attrConstraints,
306f570cc17SMarkus Böck         leaf.isOperandMatcher() ? "type" : "attr", leaf.getAsConstraint());
307f570cc17SMarkus Böck   }
308f570cc17SMarkus Böck }
309f570cc17SMarkus Böck 
310f570cc17SMarkus Böck //===----------------------------------------------------------------------===//
311f570cc17SMarkus Böck // Public Utility Functions
312f570cc17SMarkus Böck //===----------------------------------------------------------------------===//
313f570cc17SMarkus Böck 
314f570cc17SMarkus Böck std::string mlir::tblgen::escapeString(StringRef value) {
315f570cc17SMarkus Böck   std::string ret;
316*659192b1SRahul Joshi   raw_string_ostream os(ret);
317f570cc17SMarkus Böck   os.write_escaped(value);
318095b41c6SJOE1994   return ret;
319f570cc17SMarkus Böck }
320