mirror of https://github.com/llvm/circt.git
[AIG][CAPI] Add C API for LongestPathAnalysis (#8652)
This commit introduces C API bindings for AIG LongestPathAnalysis and LongestPathCollection, enabling longest path analysis of AIG circuits from C and other languages. The API uses JSON serialization for path data exchange, providing a stable interface while the underlying data structures evolve. Paths are automatically sorted by delay in descending order for efficient critical path analysis.
This commit is contained in:
parent
796f83ba94
commit
4bb54cdf9d
|
@ -18,6 +18,56 @@ extern "C" {
|
|||
MLIR_DECLARE_CAPI_DIALECT_REGISTRATION(AIG, aig);
|
||||
MLIR_CAPI_EXPORTED void registerAIGPasses(void);
|
||||
|
||||
#define DEFINE_C_API_STRUCT(name, storage) \
|
||||
struct name { \
|
||||
storage *ptr; \
|
||||
}; \
|
||||
typedef struct name name
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// LongestPathAnalysis
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Opaque handle to LongestPathAnalysis
|
||||
DEFINE_C_API_STRUCT(AIGLongestPathAnalysis, void);
|
||||
|
||||
// Opaque handle to LongestPathCollection
|
||||
DEFINE_C_API_STRUCT(AIGLongestPathCollection, void);
|
||||
|
||||
#undef DEFINE_C_API_STRUCT
|
||||
|
||||
// Create a LongestPathAnalysis for the given module
|
||||
MLIR_CAPI_EXPORTED AIGLongestPathAnalysis
|
||||
aigLongestPathAnalysisCreate(MlirOperation module, bool traceDebugPoints);
|
||||
|
||||
// Destroy a LongestPathAnalysis
|
||||
MLIR_CAPI_EXPORTED void
|
||||
aigLongestPathAnalysisDestroy(AIGLongestPathAnalysis analysis);
|
||||
|
||||
MLIR_CAPI_EXPORTED AIGLongestPathCollection aigLongestPathAnalysisGetAllPaths(
|
||||
AIGLongestPathAnalysis analysis, MlirStringRef moduleName,
|
||||
bool elaboratePaths);
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// LongestPathCollection
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Check if the collection is valid
|
||||
MLIR_CAPI_EXPORTED bool
|
||||
aigLongestPathCollectionIsNull(AIGLongestPathCollection collection);
|
||||
|
||||
// Destroy a LongestPathCollection
|
||||
MLIR_CAPI_EXPORTED void
|
||||
aigLongestPathCollectionDestroy(AIGLongestPathCollection collection);
|
||||
|
||||
// Get the number of paths in the collection
|
||||
MLIR_CAPI_EXPORTED size_t
|
||||
aigLongestPathCollectionGetSize(AIGLongestPathCollection collection);
|
||||
|
||||
// Get a specific path from the collection as JSON
|
||||
MLIR_CAPI_EXPORTED MlirStringRef aigLongestPathCollectionGetPath(
|
||||
AIGLongestPathCollection collection, int pathIndex);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -9,10 +9,113 @@
|
|||
#include "circt-c/Dialect/AIG.h"
|
||||
#include "circt/Dialect/AIG/AIGDialect.h"
|
||||
#include "circt/Dialect/AIG/AIGPasses.h"
|
||||
#include "circt/Dialect/AIG/Analysis/LongestPathAnalysis.h"
|
||||
#include "mlir-c/BuiltinAttributes.h"
|
||||
#include "mlir-c/IR.h"
|
||||
#include "mlir-c/Support.h"
|
||||
#include "mlir/CAPI/IR.h"
|
||||
#include "mlir/CAPI/Registration.h"
|
||||
#include "mlir/CAPI/Support.h"
|
||||
#include "mlir/Pass/AnalysisManager.h"
|
||||
#include "llvm/Support/JSON.h"
|
||||
|
||||
using namespace circt;
|
||||
using namespace circt::aig;
|
||||
|
||||
MLIR_DEFINE_CAPI_DIALECT_REGISTRATION(AIG, aig, circt::aig::AIGDialect)
|
||||
|
||||
void registerAIGPasses() { circt::aig::registerPasses(); }
|
||||
|
||||
// Wrapper struct to hold both the analysis and the analysis manager
|
||||
struct LongestPathAnalysisWrapper {
|
||||
std::unique_ptr<mlir::ModuleAnalysisManager> analysisManager;
|
||||
std::unique_ptr<LongestPathAnalysis> analysis;
|
||||
};
|
||||
|
||||
DEFINE_C_API_PTR_METHODS(AIGLongestPathAnalysis, LongestPathAnalysisWrapper)
|
||||
DEFINE_C_API_PTR_METHODS(AIGLongestPathCollection, LongestPathCollection)
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// LongestPathAnalysis C API
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
AIGLongestPathAnalysis aigLongestPathAnalysisCreate(MlirOperation module,
|
||||
bool traceDebugPoints) {
|
||||
auto *op = unwrap(module);
|
||||
auto *wrapper = new LongestPathAnalysisWrapper();
|
||||
wrapper->analysisManager =
|
||||
std::make_unique<mlir::ModuleAnalysisManager>(op, nullptr);
|
||||
mlir::AnalysisManager am = *wrapper->analysisManager;
|
||||
if (traceDebugPoints)
|
||||
wrapper->analysis = std::make_unique<LongestPathAnalysisWithTrace>(op, am);
|
||||
else
|
||||
wrapper->analysis = std::make_unique<LongestPathAnalysis>(op, am);
|
||||
return wrap(wrapper);
|
||||
}
|
||||
|
||||
void aigLongestPathAnalysisDestroy(AIGLongestPathAnalysis analysis) {
|
||||
delete unwrap(analysis);
|
||||
}
|
||||
|
||||
AIGLongestPathCollection
|
||||
aigLongestPathAnalysisGetAllPaths(AIGLongestPathAnalysis analysis,
|
||||
MlirStringRef moduleName,
|
||||
bool elaboratePaths) {
|
||||
auto *wrapper = unwrap(analysis);
|
||||
auto *lpa = wrapper->analysis.get();
|
||||
auto moduleNameAttr = StringAttr::get(lpa->getContext(), unwrap(moduleName));
|
||||
|
||||
auto *collection = new LongestPathCollection(lpa->getContext());
|
||||
if (!lpa->isAnalysisAvailable(moduleNameAttr) ||
|
||||
failed(
|
||||
lpa->getAllPaths(moduleNameAttr, collection->paths, elaboratePaths)))
|
||||
return {nullptr};
|
||||
|
||||
collection->sortInDescendingOrder();
|
||||
return wrap(collection);
|
||||
}
|
||||
|
||||
// ===----------------------------------------------------------------------===//
|
||||
// LongestPathCollection
|
||||
// ===----------------------------------------------------------------------===//
|
||||
|
||||
bool aigLongestPathCollectionIsNull(AIGLongestPathCollection collection) {
|
||||
return !collection.ptr;
|
||||
}
|
||||
|
||||
void aigLongestPathCollectionDestroy(AIGLongestPathCollection collection) {
|
||||
delete unwrap(collection);
|
||||
}
|
||||
|
||||
size_t aigLongestPathCollectionGetSize(AIGLongestPathCollection collection) {
|
||||
auto *wrapper = unwrap(collection);
|
||||
return wrapper->paths.size();
|
||||
}
|
||||
|
||||
MlirStringRef
|
||||
aigLongestPathCollectionGetPath(AIGLongestPathCollection collection,
|
||||
int pathIndex) {
|
||||
auto *wrapper = unwrap(collection);
|
||||
|
||||
// Check if pathIndex is valid
|
||||
if (pathIndex < 0 || pathIndex >= static_cast<int>(wrapper->paths.size()))
|
||||
return wrap(llvm::StringRef(""));
|
||||
|
||||
// Convert the specific path to JSON
|
||||
// FIXME: Avoid converting to JSON and then back to string. Use native
|
||||
// CAPI instead once data structure is stabilized.
|
||||
llvm::json::Value pathJson = toJSON(wrapper->paths[pathIndex]);
|
||||
|
||||
std::string jsonStr;
|
||||
llvm::raw_string_ostream os(jsonStr);
|
||||
os << pathJson;
|
||||
|
||||
auto ctx = wrap(wrapper->getContext());
|
||||
|
||||
// Use MLIR StringAttr to manage the string lifetime.
|
||||
// FIXME: This is safe but expensive. Consider manually managing the string
|
||||
// lifetime.
|
||||
MlirAttribute strAttr =
|
||||
mlirStringAttrGet(ctx, mlirStringRefCreateFromCString(os.str().c_str()));
|
||||
return mlirStringAttrGetValue(strAttr);
|
||||
}
|
||||
|
|
|
@ -110,3 +110,18 @@ target_link_libraries(circt-capi-rtgtest-test
|
|||
MLIRCAPIIR
|
||||
CIRCTCAPIRTGTest
|
||||
)
|
||||
|
||||
add_llvm_executable(circt-capi-aig-test
|
||||
PARTIAL_SOURCES_INTENDED
|
||||
aig.c
|
||||
)
|
||||
llvm_update_compile_flags(circt-capi-aig-test)
|
||||
|
||||
target_link_libraries(circt-capi-aig-test
|
||||
PRIVATE
|
||||
|
||||
MLIRCAPIIR
|
||||
CIRCTCAPIAIG
|
||||
CIRCTCAPIHW
|
||||
CIRCTCAPISeq
|
||||
)
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/* RUN: circt-capi-aig-test 2>&1 | FileCheck %s
|
||||
*/
|
||||
|
||||
#include "circt-c/Dialect/AIG.h"
|
||||
#include "circt-c/Dialect/HW.h"
|
||||
#include "circt-c/Dialect/Seq.h"
|
||||
#include "mlir-c/BuiltinAttributes.h"
|
||||
#include "mlir-c/BuiltinTypes.h"
|
||||
#include "mlir-c/IR.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void testAIGDialectRegistration(void) {
|
||||
MlirContext ctx = mlirContextCreate();
|
||||
mlirDialectHandleRegisterDialect(mlirGetDialectHandle__aig__(), ctx);
|
||||
|
||||
MlirDialect aig =
|
||||
mlirContextGetOrLoadDialect(ctx, mlirStringRefCreateFromCString("aig"));
|
||||
|
||||
// CHECK: AIG dialect registration: PASS
|
||||
if (!mlirDialectIsNull(aig))
|
||||
printf("AIG dialect registration: PASS\n");
|
||||
|
||||
mlirContextDestroy(ctx);
|
||||
}
|
||||
|
||||
void testLongestPathAnalysis(void) {
|
||||
MlirContext ctx = mlirContextCreate();
|
||||
mlirDialectHandleLoadDialect(mlirGetDialectHandle__aig__(), ctx);
|
||||
mlirDialectHandleLoadDialect(mlirGetDialectHandle__hw__(), ctx);
|
||||
mlirDialectHandleLoadDialect(mlirGetDialectHandle__seq__(), ctx);
|
||||
|
||||
// clang-format off
|
||||
const char *moduleStr =
|
||||
"hw.module private @ch(in %c : !seq.clock, in %a: i1, out x: i1) {\n"
|
||||
" %p = seq.compreg %q, %c : i1\n"
|
||||
" %q = aig.and_inv %p, %p {sv.namehint = \"q\"}: i1\n"
|
||||
" hw.output %p: i1\n"
|
||||
"}\n"
|
||||
"hw.module private @top(in %c : !seq.clock, in %a: i1) {\n"
|
||||
" %0 = hw.instance \"i1\" @ch(c: %c: !seq.clock, a: %a: i1) -> (x: i1)\n"
|
||||
" %1 = hw.instance \"i2\" @ch(c: %c: !seq.clock, a: %a: i1) -> (x: i1)\n"
|
||||
"}\n";
|
||||
// clang-format on
|
||||
|
||||
MlirModule module =
|
||||
mlirModuleCreateParse(ctx, mlirStringRefCreateFromCString(moduleStr));
|
||||
|
||||
MlirOperation moduleOp = mlirModuleGetOperation(module);
|
||||
|
||||
// Test with debug points enabled
|
||||
{
|
||||
AIGLongestPathAnalysis analysis =
|
||||
aigLongestPathAnalysisCreate(moduleOp, true);
|
||||
|
||||
MlirStringRef moduleName = mlirStringRefCreateFromCString("top");
|
||||
AIGLongestPathCollection collection1 =
|
||||
aigLongestPathAnalysisGetAllPaths(analysis, moduleName, true);
|
||||
AIGLongestPathCollection collection2 =
|
||||
aigLongestPathAnalysisGetAllPaths(analysis, moduleName, false);
|
||||
|
||||
size_t pathCount = aigLongestPathCollectionGetSize(collection1);
|
||||
printf("Path count with elaboration: %zu\n", pathCount);
|
||||
// CHECK: Path count with elaboration: 2
|
||||
|
||||
pathCount = aigLongestPathCollectionGetSize(collection2);
|
||||
printf("Path count without elaboration: %zu\n", pathCount);
|
||||
// CHECK: Path count without elaboration: 1
|
||||
|
||||
// Test getting individual paths
|
||||
|
||||
// Test multiple path access
|
||||
MlirStringRef pathJson = aigLongestPathCollectionGetPath(collection1, 1);
|
||||
printf("Path: %.*s\n", (int)pathJson.length, pathJson.data);
|
||||
// CHECK: Path: {"fan_out":{"bit_pos":
|
||||
// CHECK-SAME: "history":[{
|
||||
|
||||
// Cleanup
|
||||
aigLongestPathCollectionDestroy(collection1);
|
||||
aigLongestPathCollectionDestroy(collection2);
|
||||
aigLongestPathAnalysisDestroy(analysis);
|
||||
|
||||
printf("LongestPathAnalysis with debug points: PASS\n");
|
||||
// CHECK: LongestPathAnalysis with debug points: PASS
|
||||
}
|
||||
|
||||
// Test without debug points
|
||||
{
|
||||
AIGLongestPathAnalysis analysis =
|
||||
aigLongestPathAnalysisCreate(moduleOp, false);
|
||||
|
||||
MlirStringRef moduleName = mlirStringRefCreateFromCString("top");
|
||||
AIGLongestPathCollection collection =
|
||||
aigLongestPathAnalysisGetAllPaths(analysis, moduleName, true);
|
||||
|
||||
MlirStringRef pathJson = aigLongestPathCollectionGetPath(collection, 1);
|
||||
printf("Path: %.*s\n", (int)pathJson.length, pathJson.data);
|
||||
// CHECK: "history":[]
|
||||
|
||||
// Cleanup
|
||||
aigLongestPathCollectionDestroy(collection);
|
||||
aigLongestPathAnalysisDestroy(analysis);
|
||||
|
||||
printf("LongestPathAnalysis without debug points: PASS\n");
|
||||
// CHECK: LongestPathAnalysis without debug points: PASS
|
||||
}
|
||||
|
||||
mlirModuleDestroy(module);
|
||||
mlirContextDestroy(ctx);
|
||||
}
|
||||
|
||||
void testErrorHandling(void) {
|
||||
MlirContext ctx = mlirContextCreate();
|
||||
mlirDialectHandleLoadDialect(mlirGetDialectHandle__aig__(), ctx);
|
||||
mlirDialectHandleLoadDialect(mlirGetDialectHandle__hw__(), ctx);
|
||||
|
||||
const char *moduleStr =
|
||||
"hw.module @test(in %a: i1, in %b: i1, out out: i1) {\n"
|
||||
" hw.output %a : i1\n"
|
||||
"}\n";
|
||||
|
||||
MlirModule module =
|
||||
mlirModuleCreateParse(ctx, mlirStringRefCreateFromCString(moduleStr));
|
||||
|
||||
MlirOperation moduleOp = mlirModuleGetOperation(module);
|
||||
|
||||
AIGLongestPathAnalysis analysis =
|
||||
aigLongestPathAnalysisCreate(moduleOp, true);
|
||||
|
||||
MlirStringRef invalidModuleName = mlirStringRefCreateFromCString("unknown");
|
||||
AIGLongestPathCollection invalidCollection =
|
||||
aigLongestPathAnalysisGetAllPaths(analysis, invalidModuleName, true);
|
||||
|
||||
if (aigLongestPathCollectionIsNull(invalidCollection)) {
|
||||
// CHECK: Invalid module name handling: PASS
|
||||
printf("Invalid module name handling: PASS\n");
|
||||
}
|
||||
|
||||
MlirStringRef moduleName = mlirStringRefCreateFromCString("test");
|
||||
AIGLongestPathCollection collection =
|
||||
aigLongestPathAnalysisGetAllPaths(analysis, moduleName, true);
|
||||
MlirStringRef invalidPath = aigLongestPathCollectionGetPath(collection, 100);
|
||||
if (invalidPath.length == 0) {
|
||||
printf("Invalid path index handling: PASS\n");
|
||||
// CHECK: Invalid path index handling: PASS
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
aigLongestPathCollectionDestroy(collection);
|
||||
aigLongestPathAnalysisDestroy(analysis);
|
||||
mlirModuleDestroy(module);
|
||||
mlirContextDestroy(ctx);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
testAIGDialectRegistration();
|
||||
testLongestPathAnalysis();
|
||||
testErrorHandling();
|
||||
|
||||
printf("=== All tests completed ===\n");
|
||||
// CHECK: === All tests completed ===
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -18,6 +18,7 @@ set(CIRCT_TEST_DEPENDS
|
|||
FileCheck count not
|
||||
split-file
|
||||
arcilator
|
||||
circt-capi-aig-test
|
||||
circt-capi-ir-test
|
||||
circt-capi-om-test
|
||||
circt-capi-firrtl-test
|
||||
|
|
|
@ -58,12 +58,12 @@ tool_dirs = [
|
|||
config.circt_tools_dir, config.mlir_tools_dir, config.llvm_tools_dir
|
||||
]
|
||||
tools = [
|
||||
'arcilator', 'circt-as', 'circt-capi-ir-test', 'circt-capi-om-test',
|
||||
'circt-capi-firrtl-test', 'circt-capi-firtool-test',
|
||||
'circt-capi-rtg-pipelines-test', 'circt-capi-rtg-test',
|
||||
'circt-capi-rtgtest-test', 'circt-dis', 'circt-lec', 'circt-reduce',
|
||||
'circt-synth', 'circt-test', 'circt-translate', 'firld', 'firtool',
|
||||
'hlstool', 'om-linker', 'kanagawatool'
|
||||
'arcilator', 'circt-as', 'circt-bmc', 'circt-capi-aig-test',
|
||||
'circt-capi-ir-test', 'circt-capi-om-test', 'circt-capi-firrtl-test',
|
||||
'circt-capi-firtool-test', 'circt-capi-rtg-pipelines-test',
|
||||
'circt-capi-rtg-test', 'circt-capi-rtgtest-test', 'circt-dis', 'circt-lec',
|
||||
'circt-reduce', 'circt-synth', 'circt-test', 'circt-translate', 'firld',
|
||||
'firtool', 'hlstool', 'om-linker', 'kanagawatool'
|
||||
]
|
||||
|
||||
if "CIRCT_OPT_CHECK_IR_ROUNDTRIP" in os.environ:
|
||||
|
|
Loading…
Reference in New Issue