18#include <llvm/ADT/StringMap.h>
20#include <clang/Basic/FileManager.h>
21#include <clang/Basic/LangOptions.h>
22#include <clang/Basic/SourceManager.h>
23#include <clang/Lex/Lexer.h>
30static llvm::cl::OptionCategory &getOpGenCat() {
31 static llvm::cl::OptionCategory option(
32 "Options for -gen-op-capi-header, -gen-op-capi-impl, and -gen-op-capi-tests"
38 static llvm::cl::opt<std::string> option(
41 "The dialect name to use for this group of ops. "
42 "Must match across header, implementation, and test generation."
44 llvm::cl::cat(getOpGenCat())
50 static llvm::cl::opt<std::string> option(
53 "The prefix to use for generated C API function names. "
54 "Default is 'mlir'. Must match across header, implementation, and test generation."
56 llvm::cl::init(
"mlir"), llvm::cl::cat(getOpGenCat())
62 static llvm::cl::opt<bool> option(
63 "gen-isa", llvm::cl::desc(
"Generate IsA checks"), llvm::cl::init(
true),
64 llvm::cl::cat(getOpGenCat())
70 static llvm::cl::opt<bool> option(
71 "gen-op-build", llvm::cl::desc(
"Generate operation build(..) functions"),
72 llvm::cl::init(
true), llvm::cl::cat(getOpGenCat())
78 static llvm::cl::opt<bool> option(
79 "gen-operand-getters", llvm::cl::desc(
"Generate operand getters for operations"),
80 llvm::cl::init(
true), llvm::cl::cat(getOpGenCat())
86 static llvm::cl::opt<bool> option(
87 "gen-operand-setters", llvm::cl::desc(
"Generate operand setters for operations"),
88 llvm::cl::init(
true), llvm::cl::cat(getOpGenCat())
94 static llvm::cl::opt<bool> option(
95 "gen-attribute-getters", llvm::cl::desc(
"Generate attribute getters for operations"),
96 llvm::cl::init(
true), llvm::cl::cat(getOpGenCat())
102 static llvm::cl::opt<bool> option(
103 "gen-attribute-setters", llvm::cl::desc(
"Generate attribute setters for operations"),
104 llvm::cl::init(
true), llvm::cl::cat(getOpGenCat())
110 static llvm::cl::opt<bool> option(
111 "gen-region-getters", llvm::cl::desc(
"Generate region getters for operations"),
112 llvm::cl::init(
true), llvm::cl::cat(getOpGenCat())
118 static llvm::cl::opt<bool> option(
119 "gen-result-getters", llvm::cl::desc(
"Generate result getters for operations"),
120 llvm::cl::init(
true), llvm::cl::cat(getOpGenCat())
126 static llvm::cl::opt<bool> option(
127 "gen-type-attr-get", llvm::cl::desc(
"Generate get functions for types and attributes"),
128 llvm::cl::init(
true), llvm::cl::cat(getOpGenCat())
134 static llvm::cl::opt<bool> option(
135 "gen-parameter-getters",
136 llvm::cl::desc(
"Generate parameter getters for types and attributes"), llvm::cl::init(
true),
137 llvm::cl::cat(getOpGenCat())
143 static llvm::cl::opt<bool> option(
144 "gen-extra-class-methods",
145 llvm::cl::desc(
"Generate C API wrappers for methods in `extraClassDeclaration`"),
146 llvm::cl::init(
true), llvm::cl::cat(getOpGenCat())
180 std::unique_ptr<DiagnosticsEngine>
diags;
191 FileSystemOptions fileSystemOpts;
192 fileMgr =
new FileManager(fileSystemOpts);
199 : impl(std::make_unique<
Impl>()) {
201 llvm::errs() <<
"Warning: ClangLexerContext created with empty source\n";
206 std::unique_ptr<llvm::MemoryBuffer> buffer = llvm::MemoryBuffer::getMemBuffer(
source, bufferName);
208 llvm::errs() <<
"Error: Failed to create memory buffer for ClangLexerContext\n";
212 FileID fileID = impl->sourceMgr->createFileID(std::move(buffer), SrcMgr::C_User);
213 llvm::MemoryBufferRef bufferRef = impl->sourceMgr->getBufferOrFake(fileID);
215 if (bufferRef.getBufferSize() == 0 && !
source.empty()) {
216 llvm::errs() <<
"Error: Failed to get buffer from source manager in ClangLexerContext\n";
221 impl->lexer = std::make_unique<Lexer>(fileID, bufferRef, *impl->sourceMgr, impl->langOpts);
223 impl->lexer->SetCommentRetentionState(
true);
224 lexer = impl->lexer.get();
228 assert(lexer &&
"Lexer not initialized - check isValid() before calling getLexer()");
234 impl && impl->sourceMgr &&
235 "SourceManager not initialized - check isValid() before calling getSourceManager()"
237 return *impl->sourceMgr;
242static inline bool isAccessModifier(StringRef tokenText) {
243 return tokenText ==
"private" || tokenText ==
"public" || tokenText ==
"protected";
251 std::vector<Token> tokens;
253 for (Token tok; !lexer.LexFromRawLexer(tok);) {
254 if (tok.is(tok::eof)) {
257 tokens.push_back(tok);
263static inline std::string getDocumentation(
264 size_t returnTypeStart,
const std::vector<Token> &tokens,
const SourceManager &sourceMgr
267 for (
size_t j = returnTypeStart; j > 0; --j) {
268 Token curr = tokens[j - 1];
269 if (curr.is(tok::comment)) {
270 StringRef comment(sourceMgr.getCharacterData(curr.getLocation()), curr.getLength());
271 comment.consume_front(
"///");
272 comment.consume_front(
"//");
273 if (comment.consume_front(
"/*")) {
274 comment.consume_back(
"*/");
276 comment = comment.trim();
279 if (!comment.empty()) {
280 std::string newDoc(
"/// ");
288 }
else if (!curr.is(tok::unknown)) {
291 if (curr.isOneOf(tok::semi, tok::r_brace, tok::l_brace)) {
294 if (curr.is(tok::raw_identifier) && isAccessModifier(curr.getRawIdentifier())) {
319updateAccessLevel(
size_t &i,
const std::vector<Token> &tokens,
AccessLevel ¤tAccess) {
320 if (i + 1 < tokens.size() && tokens[i + 1].is(tok::colon)) {
321 if (tokens[i].is(tok::raw_identifier)) {
322 StringRef name = tokens[i].getRawIdentifier();
323 if (name ==
"private") {
327 }
else if (name ==
"public") {
331 }
else if (name ==
"protected") {
351static std::string extractReturnType(
352 size_t i,
const std::vector<Token> &tokens,
const SourceManager &sourceMgr,
353 size_t &returnTypeStart
355 std::string returnType;
357 bool isStaticMethod =
false;
360 for (
size_t j = i; j > 0; --j) {
361 Token curr = tokens[j - 1];
363 if (curr.isOneOf(tok::semi, tok::r_brace)) {
369 if (curr.is(tok::raw_identifier)) {
370 StringRef text = curr.getRawIdentifier();
371 if (text ==
"static") {
372 isStaticMethod =
true;
375 if (tokens[j].is(tok::colon) && isAccessModifier(text)) {
377 returnTypeStart = j + 1;
378 assert(returnTypeStart < tokens.size());
385 if (isStaticMethod) {
391 while (tokens[returnTypeStart].is(tok::comment)) {
393 assert(returnTypeStart <= i);
397 returnType.reserve(32);
398 llvm::raw_string_ostream returnTypeStream(returnType);
400 for (
size_t j = returnTypeStart; j < i; ++j) {
402 if (tokens[j].is(tok::comment)) {
406 StringRef tokenText(sourceMgr.getCharacterData(tokens[j].getLocation()), tokens[j].getLength());
409 if (tokens[j].is(tok::raw_identifier) && isAccessModifier(tokenText)) {
411 if (j + 1 < i && tokens[j + 1].is(tok::colon)) {
418 if (tokens[j].is(tok::raw_identifier) && tokenText ==
"return") {
430 if (tokens[j].is(tok::colon)) {
432 if (j == 0 || j + 1 >= i || !tokens[j - 1].is(tok::colon)) {
438 if (!returnType.empty() && !returnType.ends_with(
"::") && tokenText !=
"::" &&
439 !tokenText.starts_with(
"::")) {
440 returnTypeStream <<
' ';
442 returnTypeStream << tokenText;
446 return StringRef(returnType).trim().str();
459static bool parseMethodParameters(
460 size_t i,
const std::vector<Token> &tokens,
const SourceManager &sourceMgr,
461 size_t &closeParenIdx,
bool &hasParameters, std::vector<MethodParameter> ¶meters
463 const size_t tokenCount = tokens.size();
464 closeParenIdx = tokenCount;
465 hasParameters =
false;
470 size_t parenDepth = 1;
471 for (
size_t j = i + 2; j < tokenCount; ++j) {
472 if (tokens[j].is(tok::l_paren)) {
474 }
else if (tokens[j].is(tok::r_paren)) {
476 if (parenDepth == 0) {
481 std::vector<Token> paramTokens;
482 for (
size_t k = i + 2; k < j; ++k) {
483 if (k >= tokenCount) {
486 if (!tokens[k].is(tok::comment)) {
487 paramTokens.push_back(tokens[k]);
490 const size_t paramTokenCount = paramTokens.size();
493 if (paramTokenCount == 1) {
494 StringRef paramToken(
495 sourceMgr.getCharacterData(paramTokens[0].getLocation()), paramTokens[0].getLength()
497 if (paramToken !=
"void") {
498 hasParameters =
true;
500 }
else if (paramTokenCount > 1) {
501 hasParameters =
true;
506 std::string currentParamType;
507 std::string currentParamName;
508 bool inDefaultValue =
false;
510 for (
size_t k = 0; k < paramTokenCount; ++k) {
512 if (paramTokens[k].is(tok::comma)) {
514 if (!currentParamType.empty() && !currentParamName.empty()) {
515 parameters.push_back(
MethodParameter(currentParamType, currentParamName));
517 currentParamType.clear();
518 currentParamName.clear();
519 inDefaultValue =
false;
523 if (inDefaultValue) {
527 if (paramTokens[k].is(tok::equal)) {
528 inDefaultValue =
true;
533 sourceMgr.getCharacterData(paramTokens[k].getLocation()), paramTokens[k].getLength()
538 if (paramTokens[k].is(tok::raw_identifier)) {
539 if (k + 1 == paramTokenCount ||
540 (k + 1 < paramTokenCount && paramTokens[k + 1].isOneOf(tok::comma, tok::equal))) {
541 currentParamName = tokenText.str();
547 llvm::raw_string_ostream paramTypeStream(currentParamType);
548 if (!currentParamType.empty() && tokenText !=
"*" && tokenText !=
"&" &&
549 tokenText !=
"::" && !tokenText.starts_with(
"::") &&
550 !StringRef(currentParamType).ends_with(
"::")) {
551 paramTypeStream <<
' ';
553 paramTypeStream << tokenText;
554 currentParamType = paramTypeStream.str();
558 if (!currentParamType.empty() && !currentParamName.empty()) {
559 parameters.push_back(
MethodParameter(currentParamType, currentParamName));
580checkConstAndFindEnd(
size_t closeParenIdx,
const std::vector<Token> &tokens,
size_t &endIdx) {
581 bool isConst =
false;
582 endIdx = closeParenIdx + 1;
584 while (endIdx < tokens.size()) {
585 Token curr = tokens[endIdx];
586 if (curr.isOneOf(tok::semi, tok::l_brace)) {
589 if (curr.is(tok::raw_identifier) && curr.getRawIdentifier() ==
"const") {
617 if (extraDecl.empty()) {
624 llvm::errs() <<
"Error: Failed to create lexer context for parseExtraMethods\n";
629 llvm::StringMap<std::optional<ExtraMethod>> methods;
632 const std::vector<Token> tokens = tokenize(lexerCtx);
633 const size_t tokenCount = tokens.size();
640 for (
size_t i = 0; i < tokenCount; ++i) {
642 if (tokens[i].is(tok::comment)) {
647 if (updateAccessLevel(i, tokens, currentAccess)) {
658 if (i + 1 < tokenCount && tokens[i + 1].is(tok::l_paren) && tokens[i].is(tok::raw_identifier)) {
659 StringRef methodName = tokens[i].getRawIdentifier();
667 size_t returnTypeStart = 0;
668 std::string returnType = extractReturnType(i, tokens, sourceMgr, returnTypeStart);
671 if (returnType.empty()) {
676 size_t closeParenIdx = tokenCount;
677 bool hasParameters =
false;
678 std::vector<MethodParameter> parameters;
679 if (!parseMethodParameters(i, tokens, sourceMgr, closeParenIdx, hasParameters, parameters)) {
686 bool isConst = checkConstAndFindEnd(closeParenIdx, tokens, endIdx);
689 if (!returnType.empty() && !methodName.empty()) {
690 if (methods.contains(methodName)) {
691 warnSkipped(methodName,
"C API does not support method overloading");
692 methods[methodName] = std::nullopt;
697 method.
documentation = getDocumentation(returnTypeStart, tokens, sourceMgr);
701 methods[methodName] = std::make_optional(method);
711 return llvm::to_vector(
713 llvm::make_filter_range(methods, [](
const auto &p) {
return p.second.has_value(); }),
714 [](
const auto &p) {
return p.second.value(); }
721 if (cppType == typeName) {
726 StringRef prefix = cppType;
727 prefix.consume_front(
"::");
728 if (prefix.consume_front(
"mlir::")) {
729 return prefix == typeName;
737 cppType = cppType.trim();
741 return std::make_optional(cppType.str());
745 return std::make_optional(
"MlirStringRef");
750 if (cppType.ends_with(
" *") || cppType.ends_with(
"*")) {
751 size_t starPos = cppType.rfind(
'*');
752 if (starPos != StringRef::npos) {
753 StringRef baseType = cppType.substr(0, starPos).trim();
755 return std::make_optional(
"MlirAsmState");
758 return std::make_optional(
"MlirBytecodeWriterConfig");
761 return std::make_optional(
"MlirContext");
764 return std::make_optional(
"MlirDialect");
767 return std::make_optional(
"MlirDialectRegistry");
770 return std::make_optional(
"MlirOperation");
773 return std::make_optional(
"MlirBlock");
776 return std::make_optional(
"MlirOpOperand");
779 return std::make_optional(
"MlirOpPrintingFlags");
782 return std::make_optional(
"MlirRegion");
785 return std::make_optional(
"MlirSymbolTable");
788 llvm::errs() <<
"Error: Failed to parse pointer type: " << cppType <<
'\n';
795 return std::make_optional(
"MlirAttribute");
798 return std::make_optional(
"MlirIdentifier");
801 return std::make_optional(
"MlirLocation");
804 return std::make_optional(
"MlirModule");
807 return std::make_optional(
"MlirType");
810 return std::make_optional(
"MlirValue");
814 return std::make_optional(
"MlirAffineExpr");
818 return std::make_optional(
"MlirAffineMap");
822 return std::make_optional(
"MlirIntegerSet");
826 return std::make_optional(
"MlirTypeID");
831 return std::make_optional(
"MlirStringRef");
834 return std::make_optional(
"MlirLogicalResult");
838 if (cppType.ends_with(
"Type")) {
839 return std::make_optional(
"MlirType");
841 if (cppType.ends_with(
"Attr")) {
842 return std::make_optional(
"MlirAttribute");
844 if (cppType.ends_with(
"Op")) {
845 return std::make_optional(
"MlirOperation");
854 assert(!
isArrayRefType(cppType) &&
"must check `isArrayRefType()` outside");
857 if (capiTypeOpt.has_value()) {
858 return capiTypeOpt.value();
867 if (capiType ==
"MlirValue") {
869 }
else if (capiType ==
"MlirType") {
871 }
else if (capiType ==
"MlirAttribute") {
873 }
else if (capiType ==
"MlirNamedAttribute") {
874 return "NamedAttribute";
875 }
else if (capiType ==
"MlirStringRef") {
llvm::cl::opt< bool > & getGenOpOperandGetters()
Return the GenOpOperandGetters option, registering it on first access.
std::optional< std::string > tryCppTypeToCapiType(StringRef cppType)
Convert C++ type to MLIR C API type.
llvm::cl::opt< std::string > & getFunctionPrefix()
Return the FunctionPrefix option, registering it on first access.
llvm::cl::opt< bool > & getGenOpRegionGetters()
Return the GenOpRegionGetters option, registering it on first access.
std::string mapCppTypeToCapiType(StringRef cppType)
llvm::cl::opt< bool > & getGenOpResultGetters()
Return the GenOpResultGetters option, registering it on first access.
llvm::cl::opt< std::string > & getDialectName()
Return the DialectName option, registering it on first access.
AccessLevel
Access level tracking for C++ class declarations.
std::optional< std::string > mapCapiTypeToBasicCppType(StringRef capiType)
llvm::cl::opt< bool > & getGenOpAttributeSetters()
Return the GenOpAttributeSetters option, registering it on first access.
llvm::cl::opt< bool > & getGenOpBuild()
Return the GenOpBuild option, registering it on first access.
llvm::cl::opt< bool > & getGenOpAttributeGetters()
Return the GenOpAttributeGetters option, registering it on first access.
SmallVector< ExtraMethod > parseExtraMethods(StringRef extraDecl)
Parse method declarations from extraClassDeclaration using Clang's Lexer.
llvm::cl::opt< bool > & getGenExtraClassMethods()
Return the GenExtraClassMethods option, registering it on first access.
bool matchesMLIRClass(StringRef cppType, StringRef typeName)
Check if a C++ type matches an MLIR type pattern.
llvm::cl::opt< bool > & getGenTypeOrAttrParamGetters()
Return the GenTypeOrAttrParamGetters option, registering it on first access.
llvm::cl::opt< bool > & getGenIsA()
Return the GenIsA option, registering it on first access.
llvm::cl::opt< bool > & getGenOpOperandSetters()
Return the GenOpOperandSetters option, registering it on first access.
llvm::cl::opt< bool > & getGenTypeOrAttrGet()
Return the GenTypeOrAttrGet option, registering it on first access.
bool isPrimitiveType(mlir::StringRef cppType)
Check if a C++ type is a known primitive type.
bool isDynamicAPIntType(mlir::StringRef cppType)
Whether a parameter represents an arbitrary-precision mathematical integer.
bool isCppModifierKeyword(mlir::StringRef tokenText)
Check if a token text represents a C++ modifier/specifier keyword.
bool isArrayRefType(mlir::StringRef cppType)
Check if a C++ type is an ArrayRef type.
std::string toPascalCase(mlir::StringRef str)
Convert names separated by underscore or colon to PascalCase.
void warnSkipped(const S &methodName, const std::string &message)
Print warning about skipping a function.
bool isCppLanguageConstruct(mlir::StringRef methodName)
Check if a method name represents a C++ control flow keyword or language construct.
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for and distribution as defined by Sections through of this document Licensor shall mean the copyright owner or entity authorized by the copyright owner that is granting the License Legal Entity shall mean the union of the acting entity and all other entities that control are controlled by or are under common control with that entity For the purposes of this definition control direct or to cause the direction or management of such whether by contract or including but not limited to software source documentation source
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for and distribution as defined by Sections through of this document Licensor shall mean the copyright owner or entity authorized by the copyright owner that is granting the License Legal Entity shall mean the union of the acting entity and all other entities that control are controlled by or are under common control with that entity For the purposes of this definition control direct or to cause the direction or management of such whether by contract or including but not limited to software source documentation and configuration files Object form shall mean any form resulting from mechanical transformation or translation of a Source including but not limited to compiled object generated documentation
RAII wrapper for Clang lexer infrastructure.
clang::SourceManager & getSourceManager() const
Get the source manager instance.
bool isValid() const
Check if the lexer was successfully created.
ClangLexerContext(mlir::StringRef source, mlir::StringRef bufferName="input")
Construct a lexer context for the given source code.
clang::Lexer & getLexer() const
Get the lexer instance.
void registerCAPIOptions()
Register shared C API options before command-line parsing.
std::unique_ptr< DiagnosticOptions > diagOpts
Diagnostic options for configuring diagnostics.
IntrusiveRefCntPtr< FileManager > fileMgr
File manager for handling virtual files.
IntrusiveRefCntPtr< DiagnosticIDs > diagIDs
Diagnostic IDs for error reporting.
LangOptions langOpts
C++ language options for lexer configuration.
std::unique_ptr< SourceManager > sourceMgr
Source manager for tracking file locations.
std::unique_ptr< Lexer > lexer
The actual lexer instance.
std::unique_ptr< DiagnosticsEngine > diags
Diagnostics engine for handling errors and warnings.
Structure to represent a parameter in a parsed method signature from an extraClassDeclaration.