1 //===- Class.cpp - Helper classes for Op C++ code emission --------------===//
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 #include "mlir/TableGen/Class.h"
10 #include "mlir/TableGen/Format.h"
11 #include "llvm/ADT/Sequence.h"
12 #include "llvm/ADT/Twine.h"
13 #include "llvm/Support/Debug.h"
14
15 using namespace mlir;
16 using namespace mlir::tblgen;
17
18 /// Returns space to be emitted after the given C++ `type`. return "" if the
19 /// ends with '&' or '*', or is empty, else returns " ".
getSpaceAfterType(StringRef type)20 static StringRef getSpaceAfterType(StringRef type) {
21 return (type.empty() || type.endswith("&") || type.endswith("*")) ? "" : " ";
22 }
23
24 //===----------------------------------------------------------------------===//
25 // MethodParameter definitions
26 //===----------------------------------------------------------------------===//
27
writeDeclTo(raw_indented_ostream & os) const28 void MethodParameter::writeDeclTo(raw_indented_ostream &os) const {
29 if (optional)
30 os << "/*optional*/";
31 os << type << getSpaceAfterType(type) << name;
32 if (hasDefaultValue())
33 os << " = " << defaultValue;
34 }
35
writeDefTo(raw_indented_ostream & os) const36 void MethodParameter::writeDefTo(raw_indented_ostream &os) const {
37 if (optional)
38 os << "/*optional*/";
39 os << type << getSpaceAfterType(type) << name;
40 }
41
42 //===----------------------------------------------------------------------===//
43 // MethodParameters definitions
44 //===----------------------------------------------------------------------===//
45
writeDeclTo(raw_indented_ostream & os) const46 void MethodParameters::writeDeclTo(raw_indented_ostream &os) const {
47 llvm::interleaveComma(parameters, os,
48 [&os](auto ¶m) { param.writeDeclTo(os); });
49 }
writeDefTo(raw_indented_ostream & os) const50 void MethodParameters::writeDefTo(raw_indented_ostream &os) const {
51 llvm::interleaveComma(parameters, os,
52 [&os](auto ¶m) { param.writeDefTo(os); });
53 }
54
subsumes(const MethodParameters & other) const55 bool MethodParameters::subsumes(const MethodParameters &other) const {
56 // These parameters do not subsume the others if there are fewer parameters
57 // or their types do not match.
58 if (parameters.size() < other.parameters.size())
59 return false;
60 if (!std::equal(
61 other.parameters.begin(), other.parameters.end(), parameters.begin(),
62 [](auto &lhs, auto &rhs) { return lhs.getType() == rhs.getType(); }))
63 return false;
64
65 // If all the common parameters have the same type, we can elide the other
66 // method if this method has the same number of parameters as other or if the
67 // first paramater after the common parameters has a default value (and, as
68 // required by C++, subsequent parameters will have default values too).
69 return parameters.size() == other.parameters.size() ||
70 parameters[other.parameters.size()].hasDefaultValue();
71 }
72
73 //===----------------------------------------------------------------------===//
74 // MethodSignature definitions
75 //===----------------------------------------------------------------------===//
76
makesRedundant(const MethodSignature & other) const77 bool MethodSignature::makesRedundant(const MethodSignature &other) const {
78 return methodName == other.methodName &&
79 parameters.subsumes(other.parameters);
80 }
81
writeDeclTo(raw_indented_ostream & os) const82 void MethodSignature::writeDeclTo(raw_indented_ostream &os) const {
83 os << returnType << getSpaceAfterType(returnType) << methodName << "(";
84 parameters.writeDeclTo(os);
85 os << ")";
86 }
87
writeDefTo(raw_indented_ostream & os,StringRef namePrefix) const88 void MethodSignature::writeDefTo(raw_indented_ostream &os,
89 StringRef namePrefix) const {
90 os << returnType << getSpaceAfterType(returnType) << namePrefix
91 << (namePrefix.empty() ? "" : "::") << methodName << "(";
92 parameters.writeDefTo(os);
93 os << ")";
94 }
95
96 //===----------------------------------------------------------------------===//
97 // MethodBody definitions
98 //===----------------------------------------------------------------------===//
99
MethodBody(bool declOnly)100 MethodBody::MethodBody(bool declOnly)
101 : declOnly(declOnly), stringOs(body), os(stringOs) {}
102
writeTo(raw_indented_ostream & os) const103 void MethodBody::writeTo(raw_indented_ostream &os) const {
104 auto bodyRef = StringRef(body).drop_while([](char c) { return c == '\n'; });
105 os << bodyRef;
106 if (bodyRef.empty())
107 return;
108 if (bodyRef.back() != '\n')
109 os << "\n";
110 }
111
112 //===----------------------------------------------------------------------===//
113 // Method definitions
114 //===----------------------------------------------------------------------===//
115
writeDeclTo(raw_indented_ostream & os) const116 void Method::writeDeclTo(raw_indented_ostream &os) const {
117 if (isStatic())
118 os << "static ";
119 if (properties & ConstexprValue)
120 os << "constexpr ";
121 methodSignature.writeDeclTo(os);
122 if (isConst())
123 os << " const";
124 if (!isInline()) {
125 os << ";\n";
126 return;
127 }
128 os << " {\n";
129 methodBody.writeTo(os);
130 os << "}\n\n";
131 }
132
writeDefTo(raw_indented_ostream & os,StringRef namePrefix) const133 void Method::writeDefTo(raw_indented_ostream &os, StringRef namePrefix) const {
134 // The method has no definition to write if it is declaration only or inline.
135 if (properties & Declaration || isInline())
136 return;
137
138 methodSignature.writeDefTo(os, namePrefix);
139 if (isConst())
140 os << " const";
141 os << " {\n";
142 methodBody.writeTo(os);
143 os << "}\n\n";
144 }
145
146 //===----------------------------------------------------------------------===//
147 // Constructor definitions
148 //===----------------------------------------------------------------------===//
149
writeDeclTo(raw_indented_ostream & os) const150 void Constructor::writeDeclTo(raw_indented_ostream &os) const {
151 if (properties & ConstexprValue)
152 os << "constexpr ";
153 methodSignature.writeDeclTo(os);
154 if (!isInline()) {
155 os << ";\n\n";
156 return;
157 }
158 os << ' ';
159 if (!initializers.empty())
160 os << ": ";
161 llvm::interleaveComma(initializers, os,
162 [&](auto &initializer) { initializer.writeTo(os); });
163 if (!initializers.empty())
164 os << ' ';
165 os << "{";
166 methodBody.writeTo(os);
167 os << "}\n\n";
168 }
169
writeDefTo(raw_indented_ostream & os,StringRef namePrefix) const170 void Constructor::writeDefTo(raw_indented_ostream &os,
171 StringRef namePrefix) const {
172 // The method has no definition to write if it is declaration only or inline.
173 if (properties & Declaration || isInline())
174 return;
175
176 methodSignature.writeDefTo(os, namePrefix);
177 os << ' ';
178 if (!initializers.empty())
179 os << ": ";
180 llvm::interleaveComma(initializers, os,
181 [&](auto &initializer) { initializer.writeTo(os); });
182 if (!initializers.empty())
183 os << ' ';
184 os << "{";
185 methodBody.writeTo(os);
186 os << "}\n\n";
187 }
188
writeTo(raw_indented_ostream & os) const189 void Constructor::MemberInitializer::writeTo(raw_indented_ostream &os) const {
190 os << name << '(' << value << ')';
191 }
192
193 //===----------------------------------------------------------------------===//
194 // Visibility definitions
195 //===----------------------------------------------------------------------===//
196
197 namespace mlir {
198 namespace tblgen {
operator <<(raw_ostream & os,Visibility visibility)199 raw_ostream &operator<<(raw_ostream &os, Visibility visibility) {
200 switch (visibility) {
201 case Visibility::Public:
202 return os << "public";
203 case Visibility::Protected:
204 return os << "protected";
205 case Visibility::Private:
206 return os << "private";
207 }
208 return os;
209 }
210 } // namespace tblgen
211 } // namespace mlir
212
213 //===----------------------------------------------------------------------===//
214 // ParentClass definitions
215 //===----------------------------------------------------------------------===//
216
writeTo(raw_indented_ostream & os) const217 void ParentClass::writeTo(raw_indented_ostream &os) const {
218 os << visibility << ' ' << name;
219 if (!templateParams.empty()) {
220 auto scope = os.scope("<", ">", /*indent=*/false);
221 llvm::interleaveComma(templateParams, os,
222 [&](auto ¶m) { os << param; });
223 }
224 }
225
226 //===----------------------------------------------------------------------===//
227 // UsingDeclaration definitions
228 //===----------------------------------------------------------------------===//
229
writeDeclTo(raw_indented_ostream & os) const230 void UsingDeclaration::writeDeclTo(raw_indented_ostream &os) const {
231 os << "using " << name;
232 if (!value.empty())
233 os << " = " << value;
234 os << ";\n";
235 }
236
237 //===----------------------------------------------------------------------===//
238 // Field definitions
239 //===----------------------------------------------------------------------===//
240
writeDeclTo(raw_indented_ostream & os) const241 void Field::writeDeclTo(raw_indented_ostream &os) const {
242 os << type << ' ' << name << ";\n";
243 }
244
245 //===----------------------------------------------------------------------===//
246 // VisibilityDeclaration definitions
247 //===----------------------------------------------------------------------===//
248
writeDeclTo(raw_indented_ostream & os) const249 void VisibilityDeclaration::writeDeclTo(raw_indented_ostream &os) const {
250 os.unindent();
251 os << visibility << ":\n";
252 os.indent();
253 }
254
255 //===----------------------------------------------------------------------===//
256 // ExtraClassDeclaration definitions
257 //===----------------------------------------------------------------------===//
258
writeDeclTo(raw_indented_ostream & os) const259 void ExtraClassDeclaration::writeDeclTo(raw_indented_ostream &os) const {
260 os.printReindented(extraClassDeclaration);
261 }
262
writeDefTo(raw_indented_ostream & os,StringRef namePrefix) const263 void ExtraClassDeclaration::writeDefTo(raw_indented_ostream &os,
264 StringRef namePrefix) const {
265 os.printReindented(extraClassDefinition);
266 }
267
268 //===----------------------------------------------------------------------===//
269 // Class definitions
270 //===----------------------------------------------------------------------===//
271
addParent(ParentClass parent)272 ParentClass &Class::addParent(ParentClass parent) {
273 parents.push_back(std::move(parent));
274 return parents.back();
275 }
276
writeDeclTo(raw_indented_ostream & os) const277 void Class::writeDeclTo(raw_indented_ostream &os) const {
278 // Declare the class.
279 os << (isStruct ? "struct" : "class") << ' ' << className << ' ';
280
281 // Declare the parent classes, if any.
282 if (!parents.empty()) {
283 os << ": ";
284 llvm::interleaveComma(parents, os,
285 [&](auto &parent) { parent.writeTo(os); });
286 os << ' ';
287 }
288 auto classScope = os.scope("{\n", "};\n", /*indent=*/true);
289
290 // Print all the class declarations.
291 for (auto &decl : declarations)
292 decl->writeDeclTo(os);
293 }
294
writeDefTo(raw_indented_ostream & os) const295 void Class::writeDefTo(raw_indented_ostream &os) const {
296 // Print all the definitions.
297 for (auto &decl : declarations)
298 decl->writeDefTo(os, className);
299 }
300
finalize()301 void Class::finalize() {
302 // Sort the methods by public and private. Remove them from the pending list
303 // of methods.
304 SmallVector<std::unique_ptr<Method>> publicMethods, privateMethods;
305 for (auto &method : methods) {
306 if (method->isPrivate())
307 privateMethods.push_back(std::move(method));
308 else
309 publicMethods.push_back(std::move(method));
310 }
311 methods.clear();
312
313 // If the last visibility declaration wasn't `public`, add one that is. Then,
314 // declare the public methods.
315 if (!publicMethods.empty() && getLastVisibilityDecl() != Visibility::Public)
316 declare<VisibilityDeclaration>(Visibility::Public);
317 for (auto &method : publicMethods)
318 declarations.push_back(std::move(method));
319
320 // If the last visibility declaration wasn't `private`, add one that is. Then,
321 // declare the private methods.
322 if (!privateMethods.empty() && getLastVisibilityDecl() != Visibility::Private)
323 declare<VisibilityDeclaration>(Visibility::Private);
324 for (auto &method : privateMethods)
325 declarations.push_back(std::move(method));
326
327 // All fields added to the pending list are private and declared at the bottom
328 // of the class. If the last visibility declaration wasn't `private`, add one
329 // that is, then declare the fields.
330 if (!fields.empty() && getLastVisibilityDecl() != Visibility::Private)
331 declare<VisibilityDeclaration>(Visibility::Private);
332 for (auto &field : fields)
333 declare<Field>(std::move(field));
334 fields.clear();
335 }
336
getLastVisibilityDecl() const337 Visibility Class::getLastVisibilityDecl() const {
338 auto reverseDecls = llvm::reverse(declarations);
339 auto it = llvm::find_if(reverseDecls, [](auto &decl) {
340 return isa<VisibilityDeclaration>(decl);
341 });
342 return it == reverseDecls.end()
343 ? (isStruct ? Visibility::Public : Visibility::Private)
344 : cast<VisibilityDeclaration>(*it).getVisibility();
345 }
346
insertAndPruneMethods(std::vector<std::unique_ptr<Method>> & methods,std::unique_ptr<Method> newMethod)347 Method *insertAndPruneMethods(std::vector<std::unique_ptr<Method>> &methods,
348 std::unique_ptr<Method> newMethod) {
349 if (llvm::any_of(methods, [&](auto &method) {
350 return method->makesRedundant(*newMethod);
351 }))
352 return nullptr;
353
354 llvm::erase_if(methods, [&](auto &method) {
355 return newMethod->makesRedundant(*method);
356 });
357 methods.push_back(std::move(newMethod));
358 return methods.back().get();
359 }
360
addMethodAndPrune(Method && newMethod)361 Method *Class::addMethodAndPrune(Method &&newMethod) {
362 return insertAndPruneMethods(methods,
363 std::make_unique<Method>(std::move(newMethod)));
364 }
365
addConstructorAndPrune(Constructor && newCtor)366 Constructor *Class::addConstructorAndPrune(Constructor &&newCtor) {
367 return dyn_cast_or_null<Constructor>(insertAndPruneMethods(
368 methods, std::make_unique<Constructor>(std::move(newCtor))));
369 }
370