LLZK 2.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
EnumCAPIGen.cpp
Go to the documentation of this file.
1//===- EnumCAPIGen.cpp - C API generator for enum attributes --------------===//
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// EnumCAPIGen generates C API enum declarations from EnumAttr definitions
11// in TableGen files. This allows exposing MLIR enum attributes to C APIs.
12//
13//===----------------------------------------------------------------------===//
14
15#include "CommonCAPIGen.h"
16
17#include <mlir/TableGen/Attribute.h>
18#include <mlir/TableGen/GenInfo.h>
19
20#include <llvm/Support/CommandLine.h>
21#include <llvm/Support/FormatVariadic.h>
22#include <llvm/TableGen/Record.h>
23#include <llvm/TableGen/TableGenBackend.h>
24
25using namespace mlir;
26using namespace mlir::tblgen;
27
28namespace {
29
31struct EnumHeaderGenerator : public HeaderGenerator {
32 using HeaderGenerator::HeaderGenerator;
33 ~EnumHeaderGenerator() override = default;
34
35 void setEnumName(StringRef enumCppNamespace, StringRef enumClassName) {
36 std::string classNamePascal = toPascalCase(enumClassName);
37 this->cEnumName = toPascalCase(FunctionPrefix) + toPascalCase(DialectName) + classNamePascal;
38 this->cEnumAltName = toPascalCase(enumCppNamespace) + classNamePascal;
39 this->cppQualifiedPrefix = enumCppNamespace.str() + "::" + enumClassName.str() + "::";
40 // Reset case labels for next enum
41 caseLabels.clear();
42 }
43
44 void genCaseLabel(const EnumAttrCase &enumCase) {
45 static constexpr char fmt[] = " /// `{0}{2}`\n {1}_{2} = {3}";
46 assert(!cEnumName.empty() && "cEnumName must be set");
47 assert(!cppQualifiedPrefix.empty() && "cppQualifiedPrefix must be set");
48 this->caseLabels.push_back(
49 llvm::formatv(fmt, cppQualifiedPrefix, cEnumName, enumCase.getSymbol(), enumCase.getValue())
50 .str()
51 );
52 }
53
54 void genEnumDeclaration() const {
55 static constexpr char fmt[] = R"(
56enum {0} {{
57{2}
58};
59// type name based on prefix + dialect + enum class name
60typedef enum {0} {0};
61// type name based on cpp namespace + enum class name
62typedef enum {0} {1};
63)";
64 assert(!cEnumName.empty() && "cEnumName must be set");
65 os << llvm::formatv(fmt, cEnumName, cEnumAltName, llvm::join(caseLabels, ",\n"));
66 }
67
68protected:
69 std::string cEnumName;
73 std::string cEnumAltName;
75 std::string cppQualifiedPrefix;
77 SmallVector<std::string> caseLabels;
78};
79
81struct EnumImplementationGenerator : public ImplementationGenerator {
82 using ImplementationGenerator::ImplementationGenerator;
83 ~EnumImplementationGenerator() override = default;
84
85 void setEnumName(StringRef /*enumCppNamespace*/, StringRef enumClassName) {
86 std::string classNamePascal = toPascalCase(enumClassName);
87 this->cEnumName = toPascalCase(FunctionPrefix) + toPascalCase(DialectName) + classNamePascal;
88 }
89
92 void genWrapAndUnwrapDeclaration(StringRef enumCppNamespace, StringRef enumClassName) const {
93 static constexpr char fmt[] = R"(
94 static inline {0} wrap({1} cpp) {
95 return {0}(cpp);
96 }
97 static inline {1} unwrap({0} c) {
98 return {1}(c);
99 }
100 )";
101 assert(!cEnumName.empty() && "cEnumName must be set");
102 os << llvm::formatv(fmt, cEnumName, enumCppNamespace + "::" + enumClassName);
103 }
104
105protected:
106 std::string cEnumName;
107};
108
109} // namespace
110
112static bool emitEnumCAPIHeader(const llvm::RecordKeeper &records, raw_ostream &os) {
113 emitSourceFileHeader("Enum C API Declarations", os, records);
114
115 EnumHeaderGenerator generator("Enum", os);
116 generator.genPrologue();
117
118 // Find all EnumAttr definitions
119 for (const auto *def : records.getAllDerivedDefinitionsIfDefined("EnumAttrInfo")) {
120 const EnumAttr enumInfo(def);
121 StringRef enumCppNamespace = enumInfo.getCppNamespace();
122
123 // Generate for the selected dialect only
124 // EnumAttr does not contain a Dialect reference, so filter by C++ namespace instead.
125 if (!DialectName.empty() && !enumCppNamespace.contains_insensitive(DialectName)) {
126 continue;
127 }
128
129 std::vector<EnumAttrCase> enumCases = enumInfo.getAllCases();
130 if (enumCases.empty()) {
131 continue;
132 }
133
134 generator.setEnumName(enumCppNamespace, enumInfo.getEnumClassName());
135 for (EnumAttrCase &enumCase : enumCases) {
136 generator.genCaseLabel(enumCase);
137 }
138 generator.genEnumDeclaration();
139 }
140
141 generator.genEpilogue();
142 return false;
143}
144
146static bool emitEnumCAPIImpl(const llvm::RecordKeeper &records, raw_ostream &os) {
147 emitSourceFileHeader("Enum C API Implementations", os, records);
148
149 EnumImplementationGenerator generator("Enum", os);
150
151 // Find all EnumAttr definitions
152 for (const auto *def : records.getAllDerivedDefinitionsIfDefined("EnumAttrInfo")) {
153 const EnumAttr enumInfo(def);
154 StringRef enumCppNamespace = enumInfo.getCppNamespace();
155
156 // Generate for the selected dialect only
157 // EnumAttr does not contain a Dialect reference, so filter by C++ namespace instead.
158 if (!DialectName.empty() && !enumCppNamespace.contains_insensitive(DialectName)) {
159 continue;
160 }
161
162 std::vector<EnumAttrCase> enumCases = enumInfo.getAllCases();
163 if (enumCases.empty()) {
164 continue;
165 }
166
167 generator.setEnumName(enumCppNamespace, enumInfo.getEnumClassName());
168 generator.genWrapAndUnwrapDeclaration(enumCppNamespace, enumInfo.getEnumClassName());
169 }
170 return false;
171}
172
173static mlir::GenRegistration genEnumCAPIHeader(
174 "gen-enum-capi-header", "Generate C API enum declarations from EnumAttr definitions",
175 &emitEnumCAPIHeader
176);
177
178static mlir::GenRegistration genEnumCAPIImpl(
179 "gen-enum-capi-impl", "Generate enum C API wrap/unwrap implementations", &emitEnumCAPIImpl
180);
llvm::cl::opt< std::string > DialectName
llvm::cl::opt< std::string > FunctionPrefix
std::string toPascalCase(mlir::StringRef str)
Convert names separated by underscore or colon to PascalCase.
Generator for common C header file elements.
Generator for common C implementation file elements.