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 "llvm/ADT/SetVector.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 StaticVerifierFunctionEmitter::StaticVerifierFunctionEmitter(
26     const llvm::RecordKeeper &records)
27     : uniqueOutputLabel(getUniqueName(records)) {}
28 
29 StaticVerifierFunctionEmitter &
30 StaticVerifierFunctionEmitter::setSelf(StringRef str) {
31   fctx.withSelf(str);
32   return *this;
33 }
34 
35 StaticVerifierFunctionEmitter &
36 StaticVerifierFunctionEmitter::setBuilder(StringRef str) {
37   fctx.withBuilder(str);
38   return *this;
39 }
40 
41 void StaticVerifierFunctionEmitter::emitConstraintMethodsInNamespace(
42     StringRef signatureFormat, StringRef errorHandlerFormat,
43     StringRef cppNamespace, ArrayRef<const void *> constraints, raw_ostream &os,
44     bool emitDecl) {
45   llvm::Optional<NamespaceEmitter> namespaceEmitter;
46   if (!emitDecl)
47     namespaceEmitter.emplace(os, cppNamespace);
48 
49   emitConstraintMethods(signatureFormat, errorHandlerFormat, constraints, os,
50                         emitDecl);
51 }
52 
53 StringRef StaticVerifierFunctionEmitter::getConstraintFn(
54     const Constraint &constraint) const {
55   auto it = localTypeConstraints.find(constraint.getAsOpaquePointer());
56   assert(it != localTypeConstraints.end() && "expected valid constraint fn");
57   return it->second;
58 }
59 
60 std::string StaticVerifierFunctionEmitter::getUniqueName(
61     const llvm::RecordKeeper &records) {
62   // Use the input file name when generating a unique name.
63   std::string inputFilename = records.getInputFilename();
64 
65   // Drop all but the base filename.
66   StringRef nameRef = llvm::sys::path::filename(inputFilename);
67   nameRef.consume_back(".td");
68 
69   // Sanitize any invalid characters.
70   std::string uniqueName;
71   for (char c : nameRef) {
72     if (llvm::isAlnum(c) || c == '_')
73       uniqueName.push_back(c);
74     else
75       uniqueName.append(llvm::utohexstr((unsigned char)c));
76   }
77   return uniqueName;
78 }
79 
80 void StaticVerifierFunctionEmitter::emitConstraintMethods(
81     StringRef signatureFormat, StringRef errorHandlerFormat,
82     ArrayRef<const void *> constraints, raw_ostream &rawOs, bool emitDecl) {
83   raw_indented_ostream os(rawOs);
84 
85   // Record the mapping from predicate to constraint. If two constraints has the
86   // same predicate and constraint summary, they can share the same verification
87   // function.
88   llvm::DenseMap<Pred, const void *> predToConstraint;
89   for (auto it : llvm::enumerate(constraints)) {
90     std::string name;
91     Constraint constraint = Constraint::getFromOpaquePointer(it.value());
92     Pred pred = constraint.getPredicate();
93     auto iter = predToConstraint.find(pred);
94     if (iter != predToConstraint.end()) {
95       do {
96         Constraint built = Constraint::getFromOpaquePointer(iter->second);
97         // We may have the different constraints but have the same predicate,
98         // for example, ConstraintA and Variadic<ConstraintA>, note that
99         // Variadic<> doesn't introduce new predicate. In this case, we can
100         // share the same predicate function if they also have consistent
101         // summary, otherwise we may report the wrong message while verification
102         // fails.
103         if (constraint.getSummary() == built.getSummary()) {
104           name = getConstraintFn(built).str();
105           break;
106         }
107         ++iter;
108       } while (iter != predToConstraint.end() && iter->first == pred);
109     }
110 
111     if (!name.empty()) {
112       localTypeConstraints.try_emplace(it.value(), name);
113       continue;
114     }
115 
116     // Generate an obscure and unique name for this type constraint.
117     name = (Twine("__mlir_ods_local_type_constraint_") + uniqueOutputLabel +
118             Twine(it.index()))
119                .str();
120     predToConstraint.insert(
121         std::make_pair(constraint.getPredicate(), it.value()));
122     localTypeConstraints.try_emplace(it.value(), name);
123 
124     // Only generate the methods if we are generating definitions.
125     if (emitDecl)
126       continue;
127 
128     os << formatv(signatureFormat.data(), name) << " {\n";
129     os.indent() << "if (!(" << tgfmt(constraint.getConditionTemplate(), &fctx)
130                 << ")) {\n";
131     os.indent() << "return "
132                 << formatv(errorHandlerFormat.data(),
133                            escapeString(constraint.getSummary()))
134                 << ";\n";
135     os.unindent() << "}\nreturn ::mlir::success();\n";
136     os.unindent() << "}\n\n";
137   }
138 }
139 
140 std::string mlir::tblgen::escapeString(StringRef value) {
141   std::string ret;
142   llvm::raw_string_ostream os(ret);
143   os.write_escaped(value);
144   return os.str();
145 }
146