LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
CommonAttrOrTypeCAPIGen.h
Go to the documentation of this file.
1//===- CommonAttrOrTypeCAPIGen.h ------------------------------------------===//
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// Common utilities shared between Attr and Type CAPI generators
11//
12//===----------------------------------------------------------------------===//
13
14#pragma once
15
16#include "CommonCAPIGen.h"
17
18#include <mlir/TableGen/AttrOrTypeDef.h>
19
25 using HeaderGenerator::HeaderGenerator;
26 ~AttrOrTypeHeaderGenerator() override = default;
27
30 void setParamName(mlir::StringRef name) {
31 this->paramName = name;
33 }
34
36 virtual void genParameterGetterDecl(mlir::StringRef cppType) const {
37 if (isDynamicAPIntType(cppType)) {
38 os << llvm::formatv(
39 R"(
41MLIR_CAPI_EXPORTED void {0}{2}_{3}Get{4}(Mlir{1}, MlirStringCallback callback, void *userData);
42)",
45 );
46 return;
47 }
48 static constexpr char fmt[] = R"(
50MLIR_CAPI_EXPORTED {7} {0}{2}_{3}Get{4}(Mlir{1});
51)";
52 assert(!dialectNamespace.empty() && "Dialect must be set");
53 assert(!paramName.empty() && "paramName must be set");
54 os << llvm::formatv(
55 fmt,
56 getFunctionPrefix(), // {0}
57 kind, // {1}
59 className, // {3}
61 paramName, // {5}
63 mapCppTypeToCapiType(cppType) // {7}
64 );
65 }
66
68 virtual void genArrayRefParameterGetterDecls(mlir::StringRef cppType) const {
69 static constexpr char fmt[] = R"(
71MLIR_CAPI_EXPORTED intptr_t {0}{2}_{3}Get{4}Count(Mlir{1});
72
74MLIR_CAPI_EXPORTED {7} {0}{2}_{3}Get{4}At(Mlir{1}, intptr_t pos);
75)";
76 assert(!dialectNamespace.empty() && "Dialect must be set");
77 assert(!paramName.empty() && "paramName must be set");
78 mlir::StringRef cppElemType = extractArrayRefElementType(cppType);
79 os << llvm::formatv(
80 fmt,
82 kind, // {1}
84 className, // {3}
86 paramName, // {5}
87 dialectNamespace, // {6}
88 mapCppTypeToCapiType(cppElemType) // {7}
89 );
90 }
91
93 virtual void genDefaultGetBuilderDecl(const mlir::tblgen::AttrOrTypeDef &def) const {
94 static constexpr char fmt[] = R"(
96MLIR_CAPI_EXPORTED Mlir{1} {0}{2}_{3}Get(MlirContext ctx{4});
97)";
98 assert(!dialectNamespace.empty() && "Dialect must be set");
99
100 // Use raw_string_ostream for efficient string building of parameter list
101 std::string paramListBuffer;
102 llvm::raw_string_ostream paramListStream(paramListBuffer);
103 for (const auto &param : def.getParameters()) {
104 mlir::StringRef cppType = param.getCppType();
105 if (isArrayRefType(cppType)) {
106 // For ArrayRef parameters, use intptr_t for count and pointer to element type
107 mlir::StringRef cppElemType = extractArrayRefElementType(cppType);
108 paramListStream << ", intptr_t " << param.getName() << "Count, "
109 << mapCppTypeToCapiType(cppElemType) << " const *" << param.getName();
110 } else {
111 paramListStream << ", " << mapCppTypeToCapiType(cppType) << ' ' << param.getName();
112 }
113 }
114
115 os << llvm::formatv(
116 fmt,
117 getFunctionPrefix(), // {0}
118 kind, // {1}
120 className, // {3}
121 paramListBuffer, // {4}
122 dialectNamespace // {5}
123 );
124 }
125
126 void genCompleteRecord(const mlir::tblgen::AttrOrTypeDef &def) {
127 mlir::tblgen::Dialect defDialect = def.getDialect();
128
129 // Generate for the selected dialect only
130 if (defDialect.getName() != getDialectName()) {
131 return;
132 }
133
134 this->setNamespaceAndClassName(defDialect, def.getCppClassName());
135
136 // Generate IsA check
137 if (getGenIsA()) {
138 this->genIsADecl();
139 }
140
141 // Generate default Get builder if not skipped
142 if (getGenTypeOrAttrGet() && !def.skipDefaultBuilders()) {
143 this->genDefaultGetBuilderDecl(def);
144 }
145
146 // Generate parameter getters
148 for (const auto &param : def.getParameters()) {
149 this->setParamName(param.getName());
150 mlir::StringRef cppType = param.getCppType();
151 if (isArrayRefType(cppType)) {
152 this->genArrayRefParameterGetterDecls(cppType);
153 } else {
156 }
157 }
158
159 // Generate extra class method declarations
161 std::optional<mlir::StringRef> extraDecls = def.getExtraDecls();
162 if (extraDecls.has_value()) {
163 this->genExtraMethods(extraDecls.value());
164 }
165 }
166 }
167
168protected:
169 mlir::StringRef paramName;
170 std::string paramNameCapitalized;
171};
172
178 using ImplementationGenerator::ImplementationGenerator;
180
183 void setParamName(mlir::StringRef name) {
184 this->paramName = name;
186 }
187
188 void genPrologue() const override {
189 os << R"(
190#include <mlir/IR/Diagnostics.h>
191#include <mlir/IR/BuiltinAttributes.h>
192#include <mlir/CAPI/IR.h>
193#include <mlir/CAPI/Support.h>
194#include <llvm/ADT/TypeSwitch.h>
195#include <utility>
196
197using namespace mlir;
198using namespace llvm;
199)";
200 }
201
202 virtual void genArrayRefParameterImpls(mlir::StringRef cppType) const {
203 static constexpr char fmt[] = R"(
204intptr_t {0}{2}_{3}Get{4}Count(Mlir{1} inp) {{
205 auto size = llvm::cast<{3}>(unwrap(inp)).get{4}().size();
206 assert(std::in_range<intptr_t>(size) && "lossy conversion");
207 return static_cast<intptr_t>(size);
208}
210{5} {0}{2}_{3}Get{4}At(Mlir{1} inp, intptr_t pos) {{
211 return {6}(llvm::cast<{3}>(unwrap(inp)).get{4}()[pos]);
212}
213 )";
214 assert(!className.empty() && "className must be set");
215 assert(!paramName.empty() && "paramName must be set");
216 mlir::StringRef cppElemType = extractArrayRefElementType(cppType);
217 os << llvm::formatv(
218 fmt,
219 getFunctionPrefix(), // {0}
220 kind, // {1}
222 className, // {3}
224 mapCppTypeToCapiType(cppElemType), // {5}
225 isPrimitiveType(cppElemType) ? "" : "wrap" // {6}
226 );
227 }
228
229 virtual void genParameterGetterImpl(mlir::StringRef cppType) const {
230 if (isDynamicAPIntType(cppType)) {
231 os << llvm::formatv(
232 R"(
233void {0}{2}_{3}Get{4}(Mlir{1} inp, MlirStringCallback callback, void *userData) {{
234 std::string text;
235 llvm::raw_string_ostream(text) << llvm::cast<{3}>(unwrap(inp)).get{4}();
236 callback(wrap(llvm::StringRef(text)), userData);
237}
238)",
240 );
241 return;
242 }
243 static constexpr char fmt[] = R"(
244{5} {0}{2}_{3}Get{4}(Mlir{1} inp) {{
245 return {6}(llvm::cast<{3}>(unwrap(inp)).get{4}());
246}
247 )";
248 assert(!className.empty() && "className must be set");
249 assert(!paramName.empty() && "paramName must be set");
250 os << llvm::formatv(
251 fmt,
252 getFunctionPrefix(), // {0}
253 kind, // {1}
255 className, // {3}
257 mapCppTypeToCapiType(cppType), // {5}
258 isPrimitiveType(cppType) ? "" : "wrap" // {6}
259 );
260 }
261
263 virtual void genDefaultGetBuilderImpl(const mlir::tblgen::AttrOrTypeDef &def) const {
264 static constexpr char fmt[] = R"(
265Mlir{1} {0}{2}_{3}Get(MlirContext ctx{4}) {{
266 {6}
267 return wrap({3}::{7}unwrap(ctx){5}));
268}
269 )";
270 assert(!className.empty() && "className must be set");
271
272 // Use raw_string_ostream for efficient string building
273 std::string paramListBuffer;
274 std::string argListBuffer;
275 std::string prefixBuffer;
276 llvm::raw_string_ostream paramListStream(paramListBuffer);
277 llvm::raw_string_ostream argListStream(argListBuffer);
278 llvm::raw_string_ostream prefixStream(prefixBuffer);
279
280 for (const auto &param : def.getParameters()) {
281 mlir::StringRef pName = param.getName();
282 mlir::StringRef cppType = param.getCppType();
283 if (isArrayRefType(cppType)) {
284 // For ArrayRef parameters, convert from pointer + count to ArrayRef
285 mlir::StringRef cppElemType = extractArrayRefElementType(cppType);
286 std::string capiElemType = mapCppTypeToCapiType(cppElemType);
287 paramListStream << ", intptr_t " << pName << "Count, " << capiElemType << " const *"
288 << pName;
289
290 // In the call, we need to convert back to ArrayRef. Check if elements need unwrapping.
291 if (isPrimitiveType(cppElemType)) {
292 argListStream << ", ::llvm::ArrayRef<" << cppElemType << ">(" << pName << ", " << pName
293 << "Count)";
294 } else {
295 std::optional<std::string> storageCppType = mapCapiTypeToBasicCppType(capiElemType);
296 if (storageCppType.has_value()) {
297 prefixStream << "SmallVector<" << storageCppType.value() << "> storage;";
298 argListStream << ", llvm::map_to_vector(unwrapList(" << pName << "Count, " << pName
299 << ", storage), [](auto a) {return llvm::cast<" << cppElemType
300 << ">(a);})";
301 } else {
302 prefixStream << "SmallVector<" << cppElemType << "> storage;";
303 argListStream << ", unwrapList(" << pName << "Count, " << pName << ", storage)";
304 }
305 }
306 } else {
307 std::string capiType = mapCppTypeToCapiType(cppType);
308 paramListStream << ", " << capiType << ' ' << pName;
309
310 // Add unwrapping if needed
311 argListStream << ", ";
312 if (isDynamicAPIntType(cppType)) {
313 prefixStream << "auto " << pName << "Integer = llzk::parseDynamicAPInt(unwrap(" << pName
314 << "));\n";
315 prefixStream
316 << "if (!" << pName
317 << "Integer) { mlir::emitError(mlir::UnknownLoc::get(unwrap(ctx)), llvm::toString("
318 << pName << "Integer.takeError())); return {nullptr}; }\n";
319 argListStream << "*" << pName << "Integer";
320 } else if (isPrimitiveType(cppType)) {
321 argListStream << pName;
322 } else if (capiType == "MlirAttribute" || capiType == "MlirType") {
323 // Needs additional cast to the specific attribute/type class
324 argListStream << "::llvm::cast<" << cppType << ">(unwrap(" << pName << "))";
325 } else {
326 // Any other cases, just use an "unwrap" function
327 argListStream << "unwrap(" << pName << ')';
328 }
329 }
330 }
331
332 os << llvm::formatv(
333 fmt,
334 getFunctionPrefix(), // {0}
335 kind, // {1}
337 className, // {3}
338 paramListBuffer, // {4}
339 argListBuffer, // {5}
340 prefixBuffer, // {6}
341 def.genVerifyDecl() && llvm::any_of(
342 def.getParameters(),
343 [](const auto &p) { return isDynamicAPIntType(p.getCppType()); }
344 )
345 ? "getChecked(mlir::UnknownLoc::get(unwrap(ctx)), "
346 : "get(" // {7}
347 );
348 }
349
350 void genCompleteRecord(const mlir::tblgen::AttrOrTypeDef &def) {
351 mlir::tblgen::Dialect defDialect = def.getDialect();
352
353 // Generate for the selected dialect only
354 if (defDialect.getName() != getDialectName()) {
355 return;
356 }
357
358 this->setNamespaceAndClassName(defDialect, def.getCppClassName());
360 // Generate IsA check implementation
361 if (getGenIsA()) {
362 this->genIsAImpl();
363 }
364
365 // Generate default Get builder implementation if not skipped
366 if (getGenTypeOrAttrGet() && !def.skipDefaultBuilders()) {
367 this->genDefaultGetBuilderImpl(def);
368 }
369
370 // Generate parameter getter implementations
372 for (const auto &param : def.getParameters()) {
373 this->setParamName(param.getName());
374 mlir::StringRef cppType = param.getCppType();
375 if (isArrayRefType(cppType)) {
376 this->genArrayRefParameterImpls(cppType);
377 } else {
378 this->genParameterGetterImpl(cppType);
379 }
380 }
381 }
382
383 // Generate extra class method implementations
385 std::optional<mlir::StringRef> extraDecls = def.getExtraDecls();
386 if (extraDecls.has_value()) {
387 this->genExtraMethods(extraDecls.value());
388 }
389 }
390 }
391
392protected:
393 mlir::StringRef paramName;
394 std::string paramNameCapitalized;
395};
llvm::cl::opt< std::string > & getFunctionPrefix()
Return the FunctionPrefix option, registering it on first access.
std::string mapCppTypeToCapiType(StringRef cppType)
llvm::cl::opt< std::string > & getDialectName()
Return the DialectName option, registering it on first access.
std::optional< std::string > mapCapiTypeToBasicCppType(StringRef capiType)
llvm::cl::opt< bool > & getGenExtraClassMethods()
Return the GenExtraClassMethods option, registering it on first access.
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 > & getGenTypeOrAttrGet()
Return the GenTypeOrAttrGet option, registering it on first access.
mlir::StringRef extractArrayRefElementType(mlir::StringRef cppType)
Extract element type from ArrayRef<...>.
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 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.
Generator for attribute/type C header files.
virtual void genArrayRefParameterGetterDecls(mlir::StringRef cppType) const
Generate accessor function for ArrayRef parameter elements.
void setParamName(mlir::StringRef name)
Set the parameter name for code generation.
void genCompleteRecord(const mlir::tblgen::AttrOrTypeDef &def)
virtual void genDefaultGetBuilderDecl(const mlir::tblgen::AttrOrTypeDef &def) const
Generate default Get builder declaration.
~AttrOrTypeHeaderGenerator() override=default
virtual void genParameterGetterDecl(mlir::StringRef cppType) const
Generate regular getter for non-ArrayRef type parameter.
Generator for attribute/type C implementation files.
void genCompleteRecord(const mlir::tblgen::AttrOrTypeDef &def)
void setParamName(mlir::StringRef name)
Set the parameter name for code generation.
virtual void genDefaultGetBuilderImpl(const mlir::tblgen::AttrOrTypeDef &def) const
Generate default Get builder implementation.
virtual void genArrayRefParameterImpls(mlir::StringRef cppType) const
~AttrOrTypeImplementationGenerator() override=default
virtual void genParameterGetterImpl(mlir::StringRef cppType) const
mlir::StringRef className
mlir::StringRef dialectNamespace
virtual void genExtraMethods(mlir::StringRef extraDecl) const
Generate code for extra methods from an extraClassDeclaration.
virtual void setNamespaceAndClassName(const mlir::tblgen::Dialect &d, mlir::StringRef cppClassName)
Set the dialect and class name for code generation.
std::string dialectNameCapitalized
llvm::raw_ostream & os
std::string kind
Generator for common C header file elements.
virtual void genIsADecl() const
Generator for common C implementation file elements.
virtual void genIsAImpl() const