LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
CommonCAPIGen.cpp
Go to the documentation of this file.
1//===- CommonCAPIGen.cpp - Common utilities for C API generation ----------===//
2//
3// Part of the LLZK Project, under the Apache License v2.0.
4// See LICENSE.txt for license information.
5// Copyright 2025 Veridise Inc.
6// SPDX-License-Identifier: Apache-2.0
7//
8//===----------------------------------------------------------------------===//
9//
10// Shared command-line options for all CAPI generators (ops, attrs, types)
11//
12//===----------------------------------------------------------------------===//
13
14#include "CommonCAPIGen.h"
15
16#include "CAPIGenRegistration.h"
17
18#include <llvm/ADT/StringMap.h>
19
20#include <clang/Basic/FileManager.h>
21#include <clang/Basic/LangOptions.h>
22#include <clang/Basic/SourceManager.h>
23#include <clang/Lex/Lexer.h>
24#include <optional>
25
26using namespace mlir;
27using namespace clang;
28
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"
33 );
34 return option;
35}
36
37llvm::cl::opt<std::string> &getDialectName() {
38 static llvm::cl::opt<std::string> option(
39 "dialect",
40 llvm::cl::desc(
41 "The dialect name to use for this group of ops. "
42 "Must match across header, implementation, and test generation."
43 ),
44 llvm::cl::cat(getOpGenCat())
45 );
46 return option;
47}
48
49llvm::cl::opt<std::string> &getFunctionPrefix() {
50 static llvm::cl::opt<std::string> option(
51 "prefix",
52 llvm::cl::desc(
53 "The prefix to use for generated C API function names. "
54 "Default is 'mlir'. Must match across header, implementation, and test generation."
55 ),
56 llvm::cl::init("mlir"), llvm::cl::cat(getOpGenCat())
57 );
58 return option;
59}
60
61llvm::cl::opt<bool> &getGenIsA() {
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())
65 );
66 return option;
67}
68
69llvm::cl::opt<bool> &getGenOpBuild() {
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())
73 );
74 return option;
75}
76
77llvm::cl::opt<bool> &getGenOpOperandGetters() {
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())
81 );
82 return option;
83}
84
85llvm::cl::opt<bool> &getGenOpOperandSetters() {
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())
89 );
90 return option;
91}
92
93llvm::cl::opt<bool> &getGenOpAttributeGetters() {
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())
97 );
98 return option;
99}
100
101llvm::cl::opt<bool> &getGenOpAttributeSetters() {
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())
105 );
106 return option;
107}
108
109llvm::cl::opt<bool> &getGenOpRegionGetters() {
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())
113 );
114 return option;
115}
116
117llvm::cl::opt<bool> &getGenOpResultGetters() {
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())
121 );
122 return option;
123}
124
125llvm::cl::opt<bool> &getGenTypeOrAttrGet() {
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())
129 );
130 return option;
131}
132
133llvm::cl::opt<bool> &getGenTypeOrAttrParamGetters() {
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())
138 );
139 return option;
140}
141
142llvm::cl::opt<bool> &getGenExtraClassMethods() {
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())
147 );
148 return option;
149}
150
152 (void)getDialectName();
153 (void)getFunctionPrefix();
154 (void)getGenIsA();
155 (void)getGenOpBuild();
160 (void)getGenOpRegionGetters();
161 (void)getGenOpResultGetters();
162 (void)getGenTypeOrAttrGet();
165}
166
167//===----------------------------------------------------------------------===//
168// ClangLexerContext Implementation
169//===----------------------------------------------------------------------===//
172 LangOptions langOpts;
174 IntrusiveRefCntPtr<FileManager> fileMgr;
176 IntrusiveRefCntPtr<DiagnosticIDs> diagIDs;
178 std::unique_ptr<DiagnosticOptions> diagOpts;
180 std::unique_ptr<DiagnosticsEngine> diags;
182 std::unique_ptr<SourceManager> sourceMgr;
184 std::unique_ptr<Lexer> lexer;
185
186 Impl() : diagIDs(new DiagnosticIDs()), diagOpts(std::make_unique<DiagnosticOptions>()) {
187 // Enable C++ language features for lexing
188 langOpts.CPlusPlus = true;
189 langOpts.CPlusPlus11 = true;
190
191 FileSystemOptions fileSystemOpts;
192 fileMgr = new FileManager(fileSystemOpts);
193 diags = std::make_unique<DiagnosticsEngine>(diagIDs, *diagOpts);
194 sourceMgr = std::make_unique<SourceManager>(*diags, *fileMgr);
195 }
196};
197
198ClangLexerContext::ClangLexerContext(StringRef source, StringRef bufferName)
199 : impl(std::make_unique<Impl>()) {
200 if (source.empty()) {
201 llvm::errs() << "Warning: ClangLexerContext created with empty source\n";
202 return;
203 }
204
205 // Create a memory buffer for the input
206 std::unique_ptr<llvm::MemoryBuffer> buffer = llvm::MemoryBuffer::getMemBuffer(source, bufferName);
207 if (!buffer) {
208 llvm::errs() << "Error: Failed to create memory buffer for ClangLexerContext\n";
209 return;
210 }
211
212 FileID fileID = impl->sourceMgr->createFileID(std::move(buffer), SrcMgr::C_User);
213 llvm::MemoryBufferRef bufferRef = impl->sourceMgr->getBufferOrFake(fileID);
214
215 if (bufferRef.getBufferSize() == 0 && !source.empty()) {
216 llvm::errs() << "Error: Failed to get buffer from source manager in ClangLexerContext\n";
217 return;
218 }
219
220 // Create the lexer
221 impl->lexer = std::make_unique<Lexer>(fileID, bufferRef, *impl->sourceMgr, impl->langOpts);
222 // Enable comment parsing for extraClassDeclaration method extraction
223 impl->lexer->SetCommentRetentionState(true);
224 lexer = impl->lexer.get();
225}
226
228 assert(lexer && "Lexer not initialized - check isValid() before calling getLexer()");
229 return *lexer;
230}
231
233 assert(
234 impl && impl->sourceMgr &&
235 "SourceManager not initialized - check isValid() before calling getSourceManager()"
236 );
237 return *impl->sourceMgr;
238}
239
240namespace {
241
242static inline bool isAccessModifier(StringRef tokenText) {
243 return tokenText == "private" || tokenText == "public" || tokenText == "protected";
244}
245
249static inline std::vector<Token> tokenize(const ClangLexerContext &lexerCtx) {
250 Lexer &lexer = lexerCtx.getLexer();
251 std::vector<Token> tokens;
252 tokens.reserve(128); // Reasonable default
253 for (Token tok; !lexer.LexFromRawLexer(tok);) {
254 if (tok.is(tok::eof)) {
255 break;
256 }
257 tokens.push_back(tok);
258 }
259 return tokens;
260}
261
263static inline std::string getDocumentation(
264 size_t returnTypeStart, const std::vector<Token> &tokens, const SourceManager &sourceMgr
265) {
266 std::string documentation;
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("*/");
275 }
276 comment = comment.trim();
277
278 // Trim whitespace
279 if (!comment.empty()) {
280 std::string newDoc("/// ");
281 newDoc += comment;
282 if (!documentation.empty()) {
283 newDoc += '\n';
284 newDoc += documentation;
285 }
286 documentation = std::move(newDoc);
287 }
288 } else if (!curr.is(tok::unknown)) {
289 // Stop looking backwards when we hit a non-comment, non-whitespace token
290 // that could be part of another declaration
291 if (curr.isOneOf(tok::semi, tok::r_brace, tok::l_brace)) {
292 break;
293 }
294 if (curr.is(tok::raw_identifier) && isAccessModifier(curr.getRawIdentifier())) {
295 break;
296 }
297 }
298 }
299 return documentation;
300}
301
302} // namespace
303
304//===----------------------------------------------------------------------===//
305// Method Parsing Implementation
306//===----------------------------------------------------------------------===//
307
309enum class AccessLevel : std::uint8_t { Public, Private, Protected };
310
318static bool
319updateAccessLevel(size_t &i, const std::vector<Token> &tokens, AccessLevel &currentAccess) {
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") {
324 currentAccess = AccessLevel::Private;
325 i++; // extra skip for the colon
326 return true;
327 } else if (name == "public") {
328 currentAccess = AccessLevel::Public;
329 i++; // extra skip for the colon
330 return true;
331 } else if (name == "protected") {
332 currentAccess = AccessLevel::Protected;
333 i++; // extra skip for the colon
334 return true;
335 }
336 }
337 }
338 return false;
339}
340
351static std::string extractReturnType(
352 size_t i, const std::vector<Token> &tokens, const SourceManager &sourceMgr,
353 size_t &returnTypeStart
354) {
355 std::string returnType;
356 returnTypeStart = 0;
357 bool isStaticMethod = false;
358
359 // Look backwards for return type start, stopping at declaration boundaries
360 for (size_t j = i; j > 0; --j) {
361 Token curr = tokens[j - 1];
362 // Semicolon or right brace indicates lookback has reached the end of a prior declaration.
363 if (curr.isOneOf(tok::semi, tok::r_brace)) {
364 returnTypeStart = j;
365 break;
366 }
367 // Check for "static" or access modifiers in the return type lookback (both appear as
368 // `raw_identifier` in raw token stream).
369 if (curr.is(tok::raw_identifier)) {
370 StringRef text = curr.getRawIdentifier();
371 if (text == "static") {
372 isStaticMethod = true;
373 break;
374 }
375 if (tokens[j].is(tok::colon) && isAccessModifier(text)) {
376 // In this case, `returnTypeStart` must be after the colon.
377 returnTypeStart = j + 1;
378 assert(returnTypeStart < tokens.size());
379 break;
380 }
381 }
382 }
383
384 // Skip static methods (for now)
385 if (isStaticMethod) {
386 return "";
387 }
388
389 // Adjust `returnTypeStart` for potential comment tokens. Skip as many
390 // sequential comments as needed.
391 while (tokens[returnTypeStart].is(tok::comment)) {
392 returnTypeStart++;
393 assert(returnTypeStart <= i);
394 }
395
396 // Build return type from tokens, skipping modifiers and comments.
397 returnType.reserve(32); // Reasonable default for most type names
398 llvm::raw_string_ostream returnTypeStream(returnType);
399
400 for (size_t j = returnTypeStart; j < i; ++j) {
401 // Skip comments - they should be extracted as documentation, not part of the return type
402 if (tokens[j].is(tok::comment)) {
403 continue;
404 }
405
406 StringRef tokenText(sourceMgr.getCharacterData(tokens[j].getLocation()), tokens[j].getLength());
407
408 // Skip access specifiers (e.g., "private", "public", "protected").
409 if (tokens[j].is(tok::raw_identifier) && isAccessModifier(tokenText)) {
410 // If followed by a colon, skip that too
411 if (j + 1 < i && tokens[j + 1].is(tok::colon)) {
412 j++; // Skip the colon too
413 }
414 continue;
415 }
416
417 // Skip common implementation keywords that indicate we're in code, not a declaration.
418 if (tokens[j].is(tok::raw_identifier) && tokenText == "return") {
419 // This indicates we've hit implementation code, stop parsing
420 returnType.clear();
421 break;
422 }
423
424 // Skip modifiers and language keywords that shouldn't be in the return type
425 if (tokens[j].is(tok::raw_identifier) && isCppModifierKeyword(tokenText)) {
426 continue;
427 }
428
429 // Skip standalone colons (from lookback to access specifiers)
430 if (tokens[j].is(tok::colon)) {
431 // Only skip if it's not part of ::
432 if (j == 0 || j + 1 >= i || !tokens[j - 1].is(tok::colon)) {
433 continue;
434 }
435 }
436
437 // Add spacing between tokens (but not around ::)
438 if (!returnType.empty() && !returnType.ends_with("::") && tokenText != "::" &&
439 !tokenText.starts_with("::")) {
440 returnTypeStream << ' ';
441 }
442 returnTypeStream << tokenText;
443 }
444
445 // Trim possible whitespace
446 return StringRef(returnType).trim().str();
447}
448
459static bool parseMethodParameters(
460 size_t i, const std::vector<Token> &tokens, const SourceManager &sourceMgr,
461 size_t &closeParenIdx, bool &hasParameters, std::vector<MethodParameter> &parameters
462) {
463 const size_t tokenCount = tokens.size();
464 closeParenIdx = tokenCount;
465 hasParameters = false;
466 parameters.clear();
467
468 // Initialize parenDepth to 1 to account for the opening '(' at tokens[i+1]
469 // Start scanning from i+2 (the first token after the opening paren)
470 size_t parenDepth = 1;
471 for (size_t j = i + 2; j < tokenCount; ++j) {
472 if (tokens[j].is(tok::l_paren)) {
473 parenDepth++;
474 } else if (tokens[j].is(tok::r_paren)) {
475 parenDepth--;
476 if (parenDepth == 0) {
477 closeParenIdx = j;
478
479 // Parse parameters between '(' and ')'
480 // Parameters follow the pattern: type name [, type name ...]
481 std::vector<Token> paramTokens;
482 for (size_t k = i + 2; k < j; ++k) {
483 if (k >= tokenCount) {
484 break;
485 }
486 if (!tokens[k].is(tok::comment)) {
487 paramTokens.push_back(tokens[k]);
488 }
489 }
490 const size_t paramTokenCount = paramTokens.size();
491
492 // Check if we have actual parameters (excluding just "void")
493 if (paramTokenCount == 1) {
494 StringRef paramToken(
495 sourceMgr.getCharacterData(paramTokens[0].getLocation()), paramTokens[0].getLength()
496 );
497 if (paramToken != "void") {
498 hasParameters = true;
499 }
500 } else if (paramTokenCount > 1) {
501 hasParameters = true;
502 }
503
504 // Parse individual parameters
505 if (hasParameters) {
506 std::string currentParamType;
507 std::string currentParamName;
508 bool inDefaultValue = false;
509
510 for (size_t k = 0; k < paramTokenCount; ++k) {
511 // Check for end of current parameter
512 if (paramTokens[k].is(tok::comma)) {
513 // Add the current parameter if valid
514 if (!currentParamType.empty() && !currentParamName.empty()) {
515 parameters.push_back(MethodParameter(currentParamType, currentParamName));
516 }
517 currentParamType.clear();
518 currentParamName.clear();
519 inDefaultValue = false;
520 continue;
521 }
522 // Skip tokens that are part of the default value
523 if (inDefaultValue) {
524 continue;
525 }
526 // Check for '=' which indicates start of default value
527 if (paramTokens[k].is(tok::equal)) {
528 inDefaultValue = true;
529 continue;
530 }
531
532 StringRef tokenText(
533 sourceMgr.getCharacterData(paramTokens[k].getLocation()), paramTokens[k].getLength()
534 );
535
536 // Identifier token could be part of the type or the parameter name.
537 // Simple heuristic: last identifier before comma, equal, or end is the name
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();
542 continue;
543 }
544 }
545
546 // Other identifiers and other tokens (keywords, ::, *, &, etc.) are part of type.
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 << ' ';
552 }
553 paramTypeStream << tokenText;
554 currentParamType = paramTypeStream.str();
555 }
556
557 // Add the last parameter if valid
558 if (!currentParamType.empty() && !currentParamName.empty()) {
559 parameters.push_back(MethodParameter(currentParamType, currentParamName));
560 }
561 }
562
563 return true;
564 }
565 }
566 }
567
568 // Couldn't find closing paren
569 return false;
570}
571
579static bool
580checkConstAndFindEnd(size_t closeParenIdx, const std::vector<Token> &tokens, size_t &endIdx) {
581 bool isConst = false;
582 endIdx = closeParenIdx + 1;
583
584 while (endIdx < tokens.size()) {
585 Token curr = tokens[endIdx];
586 if (curr.isOneOf(tok::semi, tok::l_brace)) {
587 break;
588 }
589 if (curr.is(tok::raw_identifier) && curr.getRawIdentifier() == "const") {
590 isConst = true;
591 }
592 endIdx++;
593 }
594
595 return isConst;
596}
597
616SmallVector<ExtraMethod> parseExtraMethods(StringRef extraDecl) {
617 if (extraDecl.empty()) {
618 return {};
619 }
620
621 // Use ClangLexerContext for simplified setup
622 const ClangLexerContext lexerCtx(extraDecl, "extraClassDecl");
623 if (!lexerCtx.isValid()) {
624 llvm::errs() << "Error: Failed to create lexer context for parseExtraMethods\n";
625 return {};
626 }
627
628 // Store methods uniqued by name to detect and skip overloads (duplicate method names).
629 llvm::StringMap<std::optional<ExtraMethod>> methods;
630
631 // Parse tokens to find method declarations
632 const std::vector<Token> tokens = tokenize(lexerCtx);
633 const size_t tokenCount = tokens.size();
634 const SourceManager &sourceMgr = lexerCtx.getSourceManager();
635
636 // Track current access level to avoid generating C API wrappers for private functions. Code
637 // generated by `mlir-tblgen` puts the extra declarations in the public section by default.
638 AccessLevel currentAccess = AccessLevel::Public;
639
640 for (size_t i = 0; i < tokenCount; ++i) {
641 // Skip comments (they'll be extracted separately)
642 if (tokens[i].is(tok::comment)) {
643 continue;
644 }
645
646 // Check for access specifier changes (e.g., "private:", "public:", "protected:").
647 if (updateAccessLevel(i, tokens, currentAccess)) {
648 continue;
649 }
650
651 // Skip private and protected methods - no need to generate C API wrappers
652 if (currentAccess != AccessLevel::Public) {
653 continue;
654 }
655
656 // Look for pattern: [modifiers] <return_type> <identifier> '(' [params] ')' [const] ';'
657 // Look for an identifier followed by '('
658 if (i + 1 < tokenCount && tokens[i + 1].is(tok::l_paren) && tokens[i].is(tok::raw_identifier)) {
659 StringRef methodName = tokens[i].getRawIdentifier();
660
661 // Skip control flow keywords and other language constructs that use parentheses
662 if (isCppLanguageConstruct(methodName)) {
663 continue;
664 }
665
666 // Extract return type (everything before method name)
667 size_t returnTypeStart = 0;
668 std::string returnType = extractReturnType(i, tokens, sourceMgr, returnTypeStart);
669
670 // Skip static methods (return type is empty if static)
671 if (returnType.empty()) {
672 continue;
673 }
674
675 // Parse method parameters
676 size_t closeParenIdx = tokenCount;
677 bool hasParameters = false;
678 std::vector<MethodParameter> parameters;
679 if (!parseMethodParameters(i, tokens, sourceMgr, closeParenIdx, hasParameters, parameters)) {
680 // Couldn't find closing paren, skip this method
681 continue;
682 }
683
684 // Check for 'const' and find declaration end
685 size_t endIdx;
686 bool isConst = checkConstAndFindEnd(closeParenIdx, tokens, endIdx);
687
688 // Create method struct
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;
693 } else {
694 ExtraMethod method;
695 method.returnType = returnType;
696 method.methodName = methodName;
697 method.documentation = getDocumentation(returnTypeStart, tokens, sourceMgr);
698 method.isConst = isConst;
699 method.hasParameters = hasParameters;
700 method.parameters = parameters;
701 methods[methodName] = std::make_optional(method);
702 }
703 }
704
705 // Skip to end of this declaration for the next iteration.
706 i = endIdx;
707 }
708 }
709
710 // Return valid methods, skipping overloaded names (nullopt entries).
711 return llvm::to_vector(
712 llvm::map_range(
713 llvm::make_filter_range(methods, [](const auto &p) { return p.second.has_value(); }),
714 [](const auto &p) { return p.second.value(); }
715 )
716 );
717}
718
720bool matchesMLIRClass(StringRef cppType, StringRef typeName) {
721 if (cppType == typeName) {
722 return true;
723 }
724
725 // Check for "::mlir::" or "mlir::" prefix
726 StringRef prefix = cppType;
727 prefix.consume_front("::");
728 if (prefix.consume_front("mlir::")) {
729 return prefix == typeName;
730 }
731
732 return false;
733}
734
736std::optional<std::string> tryCppTypeToCapiType(StringRef cppType) {
737 cppType = cppType.trim();
738
739 // Primitive types are unchanged
740 if (isPrimitiveType(cppType)) {
741 return std::make_optional(cppType.str());
742 }
743
744 if (isDynamicAPIntType(cppType)) {
745 return std::make_optional("MlirStringRef");
746 }
747
748 // Pointer type conversions happen via the `unwrap()` function generated
749 // by `DEFINE_C_API_PTR_METHODS()` in `mlir/CAPI/IR.h`
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();
754 if (matchesMLIRClass(baseType, "AsmState")) {
755 return std::make_optional("MlirAsmState");
756 }
757 if (matchesMLIRClass(baseType, "BytecodeWriterConfig")) {
758 return std::make_optional("MlirBytecodeWriterConfig");
759 }
760 if (matchesMLIRClass(baseType, "MLIRContext")) {
761 return std::make_optional("MlirContext");
762 }
763 if (matchesMLIRClass(baseType, "Dialect")) {
764 return std::make_optional("MlirDialect");
765 }
766 if (matchesMLIRClass(baseType, "DialectRegistry")) {
767 return std::make_optional("MlirDialectRegistry");
768 }
769 if (matchesMLIRClass(baseType, "Operation")) {
770 return std::make_optional("MlirOperation");
771 }
772 if (matchesMLIRClass(baseType, "Block")) {
773 return std::make_optional("MlirBlock");
774 }
775 if (matchesMLIRClass(baseType, "OpOperand")) {
776 return std::make_optional("MlirOpOperand");
777 }
778 if (matchesMLIRClass(baseType, "OpPrintingFlags")) {
779 return std::make_optional("MlirOpPrintingFlags");
780 }
781 if (matchesMLIRClass(baseType, "Region")) {
782 return std::make_optional("MlirRegion");
783 }
784 if (matchesMLIRClass(baseType, "SymbolTable")) {
785 return std::make_optional("MlirSymbolTable");
786 }
787 } else {
788 llvm::errs() << "Error: Failed to parse pointer type: " << cppType << '\n';
789 }
790 }
791
792 // These have `wrap()`/`unwrap()` generated by `DEFINE_C_API_METHODS()` in...
793 // ... `mlir/CAPI/IR.h`
794 if (matchesMLIRClass(cppType, "Attribute")) {
795 return std::make_optional("MlirAttribute");
796 }
797 if (matchesMLIRClass(cppType, "StringAttr")) {
798 return std::make_optional("MlirIdentifier");
799 }
800 if (matchesMLIRClass(cppType, "Location")) {
801 return std::make_optional("MlirLocation");
802 }
803 if (matchesMLIRClass(cppType, "ModuleOp")) {
804 return std::make_optional("MlirModule");
805 }
806 if (matchesMLIRClass(cppType, "Type")) {
807 return std::make_optional("MlirType");
808 }
809 if (matchesMLIRClass(cppType, "Value")) {
810 return std::make_optional("MlirValue");
811 }
812 // ... `mlir/CAPI/AffineExpr.h`
813 if (matchesMLIRClass(cppType, "AffineExpr")) {
814 return std::make_optional("MlirAffineExpr");
815 }
816 // ... `mlir/CAPI/AffineMap.h`
817 if (matchesMLIRClass(cppType, "AffineMap")) {
818 return std::make_optional("MlirAffineMap");
819 }
820 // ... `mlir/CAPI/IntegerSet.h`
821 if (matchesMLIRClass(cppType, "IntegerSet")) {
822 return std::make_optional("MlirIntegerSet");
823 }
824 // ... `mlir/CAPI/Support.h`
825 if (matchesMLIRClass(cppType, "TypeID")) {
826 return std::make_optional("MlirTypeID");
827 }
828
829 // These have `wrap()`/`unwrap()` manually defined in `mlir/CAPI/Support.h`
830 if (matchesMLIRClass(cppType, "StringRef")) {
831 return std::make_optional("MlirStringRef");
832 }
833 if (matchesMLIRClass(cppType, "LogicalResult")) {
834 return std::make_optional("MlirLogicalResult");
835 }
836
837 // Heuristically map custom dialect classes to their C API equivalents
838 if (cppType.ends_with("Type")) {
839 return std::make_optional("MlirType");
840 }
841 if (cppType.ends_with("Attr")) {
842 return std::make_optional("MlirAttribute");
843 }
844 if (cppType.ends_with("Op")) {
845 return std::make_optional("MlirOperation");
846 }
847
848 // Otherwise, not sure how to convert it
849 return std::nullopt;
850}
851
852// Map C++ type to corresponding C API type
853std::string mapCppTypeToCapiType(StringRef cppType) {
854 assert(!isArrayRefType(cppType) && "must check `isArrayRefType()` outside");
855
856 std::optional<std::string> capiTypeOpt = tryCppTypeToCapiType(cppType);
857 if (capiTypeOpt.has_value()) {
858 return capiTypeOpt.value();
859 }
860
861 // Otherwise assume it's a type where the C name is a direct translation from the C++ name.
862 return toPascalCase(cppType);
863}
864
865// Map C API type to corresponding basic (not dialect-defined) C++ type (for `unwrapList()` calls)
866std::optional<std::string> mapCapiTypeToBasicCppType(StringRef capiType) {
867 if (capiType == "MlirValue") {
868 return "Value";
869 } else if (capiType == "MlirType") {
870 return "Type";
871 } else if (capiType == "MlirAttribute") {
872 return "Attribute";
873 } else if (capiType == "MlirNamedAttribute") {
874 return "NamedAttribute";
875 } else if (capiType == "MlirStringRef") {
876 return "StringRef";
877 }
878 return std::nullopt;
879}
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
Definition LICENSE.txt:28
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
Definition LICENSE.txt:32
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 parsed method signature from an extraClassDeclaration.
bool isConst
Whether the method is const-qualified.
bool hasParameters
Whether the method has parameters (unsupported for now).
std::vector< MethodParameter > parameters
The parameters of the method.
std::string returnType
The C++ return type of the method.
std::string methodName
The name of the method.
std::string documentation
Properly escaped documentation comment (if any).
Structure to represent a parameter in a parsed method signature from an extraClassDeclaration.