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 " ". 20 static StringRef getSpaceAfterType(StringRef type) { 21 return (type.empty() || type.endswith("&") || type.endswith("*")) ? "" : " "; 22 } 23 24 //===----------------------------------------------------------------------===// 25 // MethodParameter definitions 26 //===----------------------------------------------------------------------===// 27 28 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 36 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 46 void MethodParameters::writeDeclTo(raw_indented_ostream &os) const { 47 llvm::interleaveComma(parameters, os, 48 [&os](auto ¶m) { param.writeDeclTo(os); }); 49 } 50 void MethodParameters::writeDefTo(raw_indented_ostream &os) const { 51 llvm::interleaveComma(parameters, os, 52 [&os](auto ¶m) { param.writeDefTo(os); }); 53 } 54 55 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 77 bool MethodSignature::makesRedundant(const MethodSignature &other) const { 78 return methodName == other.methodName && 79 parameters.subsumes(other.parameters); 80 } 81 82 void MethodSignature::writeDeclTo(raw_indented_ostream &os) const { 83 os << returnType << getSpaceAfterType(returnType) << methodName << "("; 84 parameters.writeDeclTo(os); 85 os << ")"; 86 } 87 88 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 100 MethodBody::MethodBody(bool declOnly) 101 : declOnly(declOnly), stringOs(body), os(stringOs) {} 102 103 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 116 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 133 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 150 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 170 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 189 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 { 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 } // end namespace tblgen 211 } // end namespace mlir 212 213 //===----------------------------------------------------------------------===// 214 // ParentClass definitions 215 //===----------------------------------------------------------------------===// 216 217 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 230 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 241 void Field::writeDeclTo(raw_indented_ostream &os) const { 242 os << type << ' ' << name << ";\n"; 243 } 244 245 //===----------------------------------------------------------------------===// 246 // VisibilityDeclaration definitions 247 //===----------------------------------------------------------------------===// 248 249 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 259 void ExtraClassDeclaration::writeDeclTo(raw_indented_ostream &os) const { 260 os.printReindented(extraClassDeclaration); 261 } 262 263 //===----------------------------------------------------------------------===// 264 // Class definitions 265 //===----------------------------------------------------------------------===// 266 267 ParentClass &Class::addParent(ParentClass parent) { 268 parents.push_back(std::move(parent)); 269 return parents.back(); 270 } 271 272 void Class::writeDeclTo(raw_indented_ostream &os) const { 273 // Declare the class. 274 os << (isStruct ? "struct" : "class") << ' ' << className << ' '; 275 276 // Declare the parent classes, if any. 277 if (!parents.empty()) { 278 os << ": "; 279 llvm::interleaveComma(parents, os, 280 [&](auto &parent) { parent.writeTo(os); }); 281 os << ' '; 282 } 283 auto classScope = os.scope("{\n", "};\n", /*indent=*/true); 284 285 // Print all the class declarations. 286 for (auto &decl : declarations) 287 decl->writeDeclTo(os); 288 } 289 290 void Class::writeDefTo(raw_indented_ostream &os) const { 291 // Print all the definitions. 292 for (auto &decl : declarations) 293 decl->writeDefTo(os, className); 294 } 295 296 void Class::finalize() { 297 // Sort the methods by public and private. Remove them from the pending list 298 // of methods. 299 SmallVector<std::unique_ptr<Method>> publicMethods, privateMethods; 300 for (auto &method : methods) { 301 if (method->isPrivate()) 302 privateMethods.push_back(std::move(method)); 303 else 304 publicMethods.push_back(std::move(method)); 305 } 306 methods.clear(); 307 308 // If the last visibility declaration wasn't `public`, add one that is. Then, 309 // declare the public methods. 310 if (!publicMethods.empty() && getLastVisibilityDecl() != Visibility::Public) 311 declare<VisibilityDeclaration>(Visibility::Public); 312 for (auto &method : publicMethods) 313 declarations.push_back(std::move(method)); 314 315 // If the last visibility declaration wasn't `private`, add one that is. Then, 316 // declare the private methods. 317 if (!privateMethods.empty() && getLastVisibilityDecl() != Visibility::Private) 318 declare<VisibilityDeclaration>(Visibility::Private); 319 for (auto &method : privateMethods) 320 declarations.push_back(std::move(method)); 321 322 // All fields added to the pending list are private and declared at the bottom 323 // of the class. If the last visibility declaration wasn't `private`, add one 324 // that is, then declare the fields. 325 if (!fields.empty() && getLastVisibilityDecl() != Visibility::Private) 326 declare<VisibilityDeclaration>(Visibility::Private); 327 for (auto &field : fields) 328 declare<Field>(std::move(field)); 329 fields.clear(); 330 } 331 332 Visibility Class::getLastVisibilityDecl() const { 333 auto reverseDecls = llvm::reverse(declarations); 334 auto it = llvm::find_if(reverseDecls, [](auto &decl) { 335 return isa<VisibilityDeclaration>(decl); 336 }); 337 return it == reverseDecls.end() 338 ? (isStruct ? Visibility::Public : Visibility::Private) 339 : cast<VisibilityDeclaration>(*it).getVisibility(); 340 } 341 342 Method *insertAndPruneMethods(std::vector<std::unique_ptr<Method>> &methods, 343 std::unique_ptr<Method> newMethod) { 344 if (llvm::any_of(methods, [&](auto &method) { 345 return method->makesRedundant(*newMethod); 346 })) 347 return nullptr; 348 349 llvm::erase_if(methods, [&](auto &method) { 350 return newMethod->makesRedundant(*method); 351 }); 352 methods.push_back(std::move(newMethod)); 353 return methods.back().get(); 354 } 355 356 Method *Class::addMethodAndPrune(Method &&newMethod) { 357 return insertAndPruneMethods(methods, 358 std::make_unique<Method>(std::move(newMethod))); 359 } 360 361 Constructor *Class::addConstructorAndPrune(Constructor &&newCtor) { 362 return dyn_cast_or_null<Constructor>(insertAndPruneMethods( 363 methods, std::make_unique<Constructor>(std::move(newCtor)))); 364 } 365