mirror of https://github.com/llvm/circt.git
[HW][AIG] Add InstancePath CAPI and use native structures for AIG longest path analysis (#8760)
This commit refactors the AIG longest path analysis C API to use native C structures instead of JSON strings, providing better performance and type safety. The API changes replace `aigLongestPathCollectionGetPath` returning JSON with `aigLongestPathCollectionGetDataflowPath` returning native objects. New opaque handle types are added including `AIGLongestPathObject`, `AIGLongestPathHistory`, and `AIGLongestPathDataflowPath`. Comprehensive APIs are provided for accessing path data, history, and object properties. InstancePath C API support is introduced in `circt-c/Support/InstanceGraph.h`. Currently `InstancePathCache` itself is not provided, as the use of LongestPathAnalysis is read-only and there is no need to mutate/construct InstancePath. Unfortunately due to that testing of CAPI of InstancePath got a bit tricky. For now AIGLongestPathAnalysis is used to produce InstancePath in CAPI. The Python binding updates refactor Object, DataflowPath, and LongestPathHistory classes to use the native C API. JSON parsing dependencies and from_json_string() methods are removed. Proper property accessors using the new C API are added while maintaining backward compatibility for existing Python interfaces. So the existing integration tests cover most of the APIs. Testing updates include comprehensive coverage in the existing C API tests in `test/CAPI/aig.c`. A new `test/CAPI/support.c` is added for InstancePath API testing. Python integration tests are updated to work with the new API. This change improves performance by eliminating JSON serialization/deserialization overhead and provides a more robust, type-safe interface for accessing longest path analysis results.
This commit is contained in:
parent
9088c29f4b
commit
640daf0c92
|
@ -9,6 +9,7 @@
|
|||
#ifndef CIRCT_C_DIALECT_AIG_H
|
||||
#define CIRCT_C_DIALECT_AIG_H
|
||||
|
||||
#include "circt-c/Support/InstanceGraph.h"
|
||||
#include "mlir-c/IR.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -28,6 +29,15 @@ MLIR_CAPI_EXPORTED void registerAIGPasses(void);
|
|||
// LongestPathAnalysis
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Opaque handle to LongestPathObject
|
||||
DEFINE_C_API_STRUCT(AIGLongestPathObject, void);
|
||||
|
||||
// Opaque handle to LongestPathHistory
|
||||
DEFINE_C_API_STRUCT(AIGLongestPathHistory, void);
|
||||
|
||||
// Opaque handle to LongestPathDataflowPath
|
||||
DEFINE_C_API_STRUCT(AIGLongestPathDataflowPath, void);
|
||||
|
||||
// Opaque handle to LongestPathAnalysis
|
||||
DEFINE_C_API_STRUCT(AIGLongestPathAnalysis, void);
|
||||
|
||||
|
@ -64,9 +74,57 @@ aigLongestPathCollectionDestroy(AIGLongestPathCollection 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);
|
||||
// Get a specific path from the collection as DataflowPath object
|
||||
MLIR_CAPI_EXPORTED AIGLongestPathDataflowPath
|
||||
aigLongestPathCollectionGetDataflowPath(AIGLongestPathCollection collection,
|
||||
size_t pathIndex);
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// DataflowPath API
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
MLIR_CAPI_EXPORTED int64_t
|
||||
aigLongestPathDataflowPathGetDelay(AIGLongestPathDataflowPath dataflowPath);
|
||||
|
||||
MLIR_CAPI_EXPORTED AIGLongestPathObject
|
||||
aigLongestPathDataflowPathGetFanIn(AIGLongestPathDataflowPath dataflowPath);
|
||||
|
||||
MLIR_CAPI_EXPORTED AIGLongestPathObject
|
||||
aigLongestPathDataflowPathGetFanOut(AIGLongestPathDataflowPath dataflowPath);
|
||||
|
||||
MLIR_CAPI_EXPORTED AIGLongestPathHistory
|
||||
aigLongestPathDataflowPathGetHistory(AIGLongestPathDataflowPath dataflowPath);
|
||||
|
||||
MLIR_CAPI_EXPORTED MlirOperation
|
||||
aigLongestPathDataflowPathGetRoot(AIGLongestPathDataflowPath dataflowPath);
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// History API
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
MLIR_CAPI_EXPORTED bool
|
||||
aigLongestPathHistoryIsEmpty(AIGLongestPathHistory history);
|
||||
|
||||
MLIR_CAPI_EXPORTED void
|
||||
aigLongestPathHistoryGetHead(AIGLongestPathHistory history,
|
||||
AIGLongestPathObject *object, int64_t *delay,
|
||||
MlirStringRef *comment);
|
||||
|
||||
MLIR_CAPI_EXPORTED AIGLongestPathHistory
|
||||
aigLongestPathHistoryGetTail(AIGLongestPathHistory history);
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Object API
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
MLIR_CAPI_EXPORTED IgraphInstancePath
|
||||
aigLongestPathObjectGetInstancePath(AIGLongestPathObject object);
|
||||
|
||||
MLIR_CAPI_EXPORTED MlirStringRef
|
||||
aigLongestPathObjectName(AIGLongestPathObject object);
|
||||
|
||||
MLIR_CAPI_EXPORTED size_t
|
||||
aigLongestPathObjectBitPos(AIGLongestPathObject object);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef CIRCT_C_SUPPORT_INSTANCEGRAPH_H
|
||||
#define CIRCT_C_SUPPORT_INSTANCEGRAPH_H
|
||||
|
||||
#include "mlir-c/IR.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct IgraphInstancePath {
|
||||
void *ptr;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
typedef struct IgraphInstancePath IgraphInstancePath;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// InstancePath API
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
MLIR_CAPI_EXPORTED MlirOperation igraphInstancePathGet(IgraphInstancePath path,
|
||||
size_t index);
|
||||
MLIR_CAPI_EXPORTED size_t igraphInstancePathSize(IgraphInstancePath path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // CIRCT_C_SUPPORT_INSTANCEPATH_H
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import circt
|
||||
from circt.dialects import aig, hw
|
||||
from circt.ir import Context, Location, Module, InsertionPoint, IntegerType
|
||||
from circt.ir import Context, Location, Module, InsertionPoint, IntegerType, ArrayAttr
|
||||
from circt.dialects.aig import LongestPathAnalysis, LongestPathCollection, DataflowPath
|
||||
|
||||
with Context() as ctx, Location.unknown():
|
||||
|
@ -15,7 +15,7 @@ with Context() as ctx, Location.unknown():
|
|||
i32 = IntegerType.get_signless(32)
|
||||
|
||||
# Create a simple hardware module with AIG operations
|
||||
def build_module(module):
|
||||
def build_child(module):
|
||||
a, b = module.entry_block.arguments
|
||||
|
||||
result1 = aig.and_inv([a, b], [False, True])
|
||||
|
@ -23,10 +23,29 @@ with Context() as ctx, Location.unknown():
|
|||
|
||||
hw.OutputOp([result1, result2])
|
||||
|
||||
hw.HWModuleOp(name="test_aig",
|
||||
hw.HWModuleOp(name="test_child",
|
||||
input_ports=[("a", i32), ("b", i32)],
|
||||
output_ports=[("out1", i32), ("out2", i32)],
|
||||
body_builder=build_module)
|
||||
body_builder=build_child)
|
||||
|
||||
def build_top(module):
|
||||
a, b = module.entry_block.arguments
|
||||
out1, out2 = hw.instance(
|
||||
[i32, i32],
|
||||
"child",
|
||||
"test_child",
|
||||
[a, b],
|
||||
["a", "b"],
|
||||
["out1", "out2"],
|
||||
parameters=ArrayAttr.get([]),
|
||||
)
|
||||
hw.OutputOp([out1, out2])
|
||||
|
||||
hw.HWModuleOp(name="test_aig",
|
||||
input_ports=[("c", i32), ("d", i32)],
|
||||
output_ports=[("out1", i32), ("out2", i32)],
|
||||
body_builder=build_top)
|
||||
|
||||
# CHECK-LABEL: AIG dialect registration and basic operations successful!
|
||||
print("AIG dialect registration and basic operations successful!")
|
||||
# Test aig.and_inv operation
|
||||
|
@ -47,6 +66,7 @@ with Context() as ctx, Location.unknown():
|
|||
# CHECK-NEXT: 50th percentile delay: 1
|
||||
# CHECK-NEXT: 90th percentile delay: 2
|
||||
# CHECK-NEXT: 95th percentile delay: 2
|
||||
|
||||
# CHECK-NEXT: 99th percentile delay: 2
|
||||
# CHECK-NEXT: 99.9th percentile delay: 2
|
||||
collection.print_summary()
|
||||
|
@ -56,11 +76,6 @@ with Context() as ctx, Location.unknown():
|
|||
# CHECK-NEXT: index -1 delay: 1
|
||||
print("index 1 delay:", collection[1].delay)
|
||||
print("index -1 delay:", collection[-1].delay)
|
||||
# CHECK-NEXT: collection.get_path(5) == collection[5]: True
|
||||
print(
|
||||
"collection.get_path(5) == collection[5]:",
|
||||
DataflowPath.from_json_string(
|
||||
collection.collection.get_path(5)) == collection[5])
|
||||
# Check that len and get_size are the same
|
||||
# CHECK-NEXT: 128 128
|
||||
print(len(collection), collection.collection.get_size())
|
||||
|
@ -84,6 +99,6 @@ with Context() as ctx, Location.unknown():
|
|||
print("minus index slice:", len(collection[:-2]) == len(collection) - 2)
|
||||
|
||||
# Test framegraph emission.
|
||||
# CHECK: top:test_aig;a[0] 0
|
||||
# CHECK-NEXT: top:test_aig;out2[0] 2
|
||||
# CHECK: top:test_aig;c[0] 0
|
||||
# CHECK: top:test_aig;child:test_child;a[0] 2
|
||||
print(collection.longest_path.to_flamegraph())
|
||||
|
|
|
@ -69,9 +69,66 @@ void circt::python::populateDialectAIGSubmodule(nb::module_ &m) {
|
|||
})
|
||||
.def("get_path",
|
||||
[](AIGLongestPathCollection &self,
|
||||
int pathIndex) -> std::string_view {
|
||||
MlirStringRef pathRef =
|
||||
aigLongestPathCollectionGetPath(self, pathIndex);
|
||||
return std::string_view(pathRef.data, pathRef.length);
|
||||
int pathIndex) -> AIGLongestPathDataflowPath {
|
||||
return aigLongestPathCollectionGetDataflowPath(self, pathIndex);
|
||||
});
|
||||
|
||||
nb::class_<AIGLongestPathDataflowPath>(m, "_LongestPathDataflowPath")
|
||||
.def_prop_ro("delay",
|
||||
[](AIGLongestPathDataflowPath &self) {
|
||||
return aigLongestPathDataflowPathGetDelay(self);
|
||||
})
|
||||
.def_prop_ro("fan_in",
|
||||
[](AIGLongestPathDataflowPath &self) {
|
||||
return aigLongestPathDataflowPathGetFanIn(self);
|
||||
})
|
||||
.def_prop_ro("fan_out",
|
||||
[](AIGLongestPathDataflowPath &self) {
|
||||
return aigLongestPathDataflowPathGetFanOut(self);
|
||||
})
|
||||
.def_prop_ro("history",
|
||||
[](AIGLongestPathDataflowPath &self) {
|
||||
return aigLongestPathDataflowPathGetHistory(self);
|
||||
})
|
||||
.def_prop_ro("root", [](AIGLongestPathDataflowPath &self) {
|
||||
return aigLongestPathDataflowPathGetRoot(self);
|
||||
});
|
||||
|
||||
nb::class_<AIGLongestPathHistory>(m, "_LongestPathHistory")
|
||||
.def_prop_ro("empty",
|
||||
[](AIGLongestPathHistory &self) {
|
||||
return aigLongestPathHistoryIsEmpty(self);
|
||||
})
|
||||
.def_prop_ro("head",
|
||||
[](AIGLongestPathHistory &self) {
|
||||
AIGLongestPathObject object;
|
||||
int64_t delay;
|
||||
MlirStringRef comment;
|
||||
aigLongestPathHistoryGetHead(self, &object, &delay,
|
||||
&comment);
|
||||
return std::make_tuple(object, delay, comment);
|
||||
})
|
||||
.def_prop_ro("tail", [](AIGLongestPathHistory &self) {
|
||||
return aigLongestPathHistoryGetTail(self);
|
||||
});
|
||||
|
||||
nb::class_<AIGLongestPathObject>(m, "_LongestPathObject")
|
||||
.def_prop_ro("instance_path",
|
||||
[](AIGLongestPathObject &self) {
|
||||
auto path = aigLongestPathObjectGetInstancePath(self);
|
||||
if (!path.ptr)
|
||||
return std::vector<MlirOperation>();
|
||||
size_t size = igraphInstancePathSize(path);
|
||||
std::vector<MlirOperation> result;
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
result.push_back(igraphInstancePathGet(path, i));
|
||||
return result;
|
||||
})
|
||||
.def_prop_ro("name",
|
||||
[](AIGLongestPathObject &self) {
|
||||
return aigLongestPathObjectName(self);
|
||||
})
|
||||
.def_prop_ro("bit_pos", [](AIGLongestPathObject &self) {
|
||||
return aigLongestPathObjectBitPos(self);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ set(PYTHON_BINDINGS_LINK_LIBS
|
|||
CIRCTCAPIRtgTool
|
||||
CIRCTCAPISeq
|
||||
CIRCTCAPISV
|
||||
CIRCTCAPISupport
|
||||
CIRCTCAPIVerif
|
||||
CIRCTCAPITransforms
|
||||
CIRCTCAPISynthesis
|
||||
|
|
|
@ -2,11 +2,10 @@
|
|||
# See https://llvm.org/LICENSE.txt for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
from . import aig
|
||||
from . import aig, hw
|
||||
from ._aig_ops_gen import *
|
||||
from .._mlir_libs._circt._aig import _LongestPathAnalysis, _LongestPathCollection
|
||||
from .._mlir_libs._circt._aig import _LongestPathAnalysis, _LongestPathCollection, _LongestPathDataflowPath, _LongestPathHistory, _LongestPathObject
|
||||
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Dict, List, Union
|
||||
|
||||
|
@ -16,7 +15,7 @@ from typing import Any, Dict, List, Union
|
|||
|
||||
|
||||
@dataclass
|
||||
class InstancePathElement:
|
||||
class Instance:
|
||||
"""
|
||||
Represents a single element in a hierarchical instance path.
|
||||
In hardware design, modules are instantiated hierarchically. This class
|
||||
|
@ -26,15 +25,18 @@ class InstancePathElement:
|
|||
instance_name: The name of this specific instance
|
||||
module_name: The type/name of the module being instantiated
|
||||
"""
|
||||
_instance: hw.InstanceOp
|
||||
|
||||
instance_name: str
|
||||
module_name: str
|
||||
def __init__(self, instance: hw.InstanceOp):
|
||||
self._instance = instance
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict[str, Any]) -> "InstancePathElement":
|
||||
"""Create an InstancePathElement from a dictionary representation."""
|
||||
return cls(instance_name=data["instance_name"],
|
||||
module_name=data["module_name"])
|
||||
@property
|
||||
def instance_name(self) -> str:
|
||||
return self._instance.attributes["instanceName"].value
|
||||
|
||||
@property
|
||||
def module_name(self) -> str:
|
||||
return self._instance.attributes["moduleName"].value
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -50,9 +52,7 @@ class Object:
|
|||
bit_pos: Bit position for multi-bit signals (0 for single-bit)
|
||||
"""
|
||||
|
||||
instance_path: List[InstancePathElement]
|
||||
name: str
|
||||
bit_pos: int
|
||||
_object: _LongestPathObject
|
||||
|
||||
# TODO: Associate with an MLIR value/op
|
||||
|
||||
|
@ -65,15 +65,25 @@ class Object:
|
|||
for elem in self.instance_path)
|
||||
return f"{path} {self.name}[{self.bit_pos}]"
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict[str, Any]) -> "Object":
|
||||
"""Create an Object from a dictionary representation."""
|
||||
instance_path = [
|
||||
InstancePathElement.from_dict(elem) for elem in data["instance_path"]
|
||||
]
|
||||
return cls(instance_path=instance_path,
|
||||
name=data["name"],
|
||||
bit_pos=data["bit_pos"])
|
||||
def __repr__(self) -> str:
|
||||
return f"Object({self.instance_path}, {self.name}, {self.bit_pos})"
|
||||
|
||||
@property
|
||||
def instance_path(self) -> List[Instance]:
|
||||
"""Get the hierarchical instance path to this object."""
|
||||
operations = self._object.instance_path
|
||||
|
||||
return [Instance(op) for op in operations]
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Get the name of this signal/port."""
|
||||
return self._object.name
|
||||
|
||||
@property
|
||||
def bit_pos(self) -> int:
|
||||
"""Get the bit position for multi-bit signals."""
|
||||
return self._object.bit_pos
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -103,34 +113,6 @@ class DebugPoint:
|
|||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class OpenPath:
|
||||
"""
|
||||
Represents an open timing path with detailed history.
|
||||
An open path represents a timing path that hasn't reached its final
|
||||
destination yet. It contains the current fan-in point, accumulated delay,
|
||||
and a history of debug points showing how the signal propagated.
|
||||
Attributes:
|
||||
fan_in: The input signal/object where this path segment begins
|
||||
delay: Total accumulated delay for this path segment
|
||||
history: Chronological list of debug points along the path
|
||||
"""
|
||||
|
||||
fan_in: Object
|
||||
delay: int
|
||||
history: List[DebugPoint]
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict[str, Any]) -> "OpenPath":
|
||||
"""Create an OpenPath from a dictionary representation."""
|
||||
history = [DebugPoint.from_dict(point) for point in data["history"]]
|
||||
return cls(
|
||||
fan_in=Object.from_dict(data["fan_in"]),
|
||||
delay=data["delay"],
|
||||
history=history,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class DataflowPath:
|
||||
"""
|
||||
|
@ -144,43 +126,32 @@ class DataflowPath:
|
|||
root: The root module name for this analysis
|
||||
"""
|
||||
|
||||
fan_out: Object # Output endpoint of the path
|
||||
path: OpenPath # Detailed path information with history
|
||||
root: str # Root module name
|
||||
|
||||
# ========================================================================
|
||||
# Factory Methods for Object Creation
|
||||
# ========================================================================
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict[str, Any]) -> "DataflowPath":
|
||||
"""Create a DataflowPath from a dictionary representation."""
|
||||
return cls(
|
||||
fan_out=Object.from_dict(data["fan_out"]),
|
||||
path=OpenPath.from_dict(data["path"]),
|
||||
root=data["root"],
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_json_string(cls, json_str: str) -> "DataflowPath":
|
||||
"""Create a DataflowPath from a JSON string representation."""
|
||||
data = json.loads(json_str)
|
||||
return cls.from_dict(data)
|
||||
_path: _LongestPathDataflowPath
|
||||
|
||||
@property
|
||||
def delay(self) -> int:
|
||||
"""Get the total delay of this path in timing units."""
|
||||
return self.path.delay
|
||||
return self._path.delay
|
||||
|
||||
@property
|
||||
def fan_in(self) -> "DataflowPath":
|
||||
def fan_in(self) -> Object:
|
||||
"""Get the input signal/object where this path begins."""
|
||||
return self.path.fan_in
|
||||
return Object(self._path.fan_in)
|
||||
|
||||
@property
|
||||
def fan_out(self) -> Object:
|
||||
"""Get the output signal/object where this path ends."""
|
||||
return Object(self._path.fan_out)
|
||||
|
||||
@property
|
||||
def history(self) -> List[DebugPoint]:
|
||||
"""Get the history of debug points along this path."""
|
||||
return self.path.history
|
||||
return [i for i in LongestPathHistory(self._path.history)]
|
||||
|
||||
@property
|
||||
def root(self) -> str:
|
||||
"""Get the root module name for this analysis."""
|
||||
return self._path.root.attributes["sym_name"].value
|
||||
|
||||
# ========================================================================
|
||||
# Visualization and Analysis Methods
|
||||
|
@ -244,7 +215,7 @@ class DataflowPath:
|
|||
|
||||
# Add each level of the instance hierarchy
|
||||
for elem in obj.instance_path:
|
||||
parts.append(f"{elem.module_name}:{elem.instance_name}")
|
||||
parts.append(f"{elem.instance_name}:{elem.module_name}")
|
||||
|
||||
# Add the signal name with bit position if applicable
|
||||
signal_part = obj.name
|
||||
|
@ -268,7 +239,6 @@ class LongestPathCollection:
|
|||
Attributes:
|
||||
collection: The underlying C++ collection object
|
||||
length: Number of paths in the collection
|
||||
cache: Cache for parsed DataflowPath objects to avoid re-parsing JSON
|
||||
"""
|
||||
|
||||
def __init__(self, collection):
|
||||
|
@ -279,7 +249,6 @@ class LongestPathCollection:
|
|||
"""
|
||||
self.collection = collection
|
||||
self.length = self.collection.get_size()
|
||||
self.cache = [None for _ in range(self.length)]
|
||||
|
||||
# ========================================================================
|
||||
# Collection Interface Methods
|
||||
|
@ -295,7 +264,6 @@ class LongestPathCollection:
|
|||
"""
|
||||
Get a specific path from the collection by index.
|
||||
Supports both integer and slice indexing. Integer indices can be negative.
|
||||
Results are cached to avoid expensive JSON parsing on repeated access.
|
||||
|
||||
Args:
|
||||
index: Integer index or slice object to access paths
|
||||
|
@ -305,7 +273,7 @@ class LongestPathCollection:
|
|||
|
||||
Raises:
|
||||
IndexError: If index is out of range
|
||||
"""
|
||||
"""
|
||||
if isinstance(index, slice):
|
||||
return [self[i] for i in range(*index.indices(len(self)))]
|
||||
|
||||
|
@ -315,14 +283,7 @@ class LongestPathCollection:
|
|||
if index < 0 or index >= self.length:
|
||||
raise IndexError("Index out of range")
|
||||
|
||||
# Use cache to avoid expensive JSON parsing
|
||||
if self.cache[index] is not None:
|
||||
return self.cache[index]
|
||||
|
||||
# Parse JSON and cache the result
|
||||
json_str = self.collection.get_path(index)
|
||||
self.cache[index] = DataflowPath.from_json_string(json_str)
|
||||
return self.cache[index]
|
||||
return DataflowPath(self.collection.get_path(index))
|
||||
|
||||
# ========================================================================
|
||||
# Analysis and Query Methods
|
||||
|
@ -387,7 +348,9 @@ class LongestPathAnalysis:
|
|||
"""
|
||||
self.analysis = aig._LongestPathAnalysis(module, trace_debug_points)
|
||||
|
||||
def get_all_paths(self, module_name: str) -> LongestPathCollection:
|
||||
def get_all_paths(self,
|
||||
module_name: str,
|
||||
elaborate_paths: bool = True) -> LongestPathCollection:
|
||||
"""
|
||||
Perform longest path analysis and return all timing paths.
|
||||
This method analyzes the specified module and returns a collection
|
||||
|
@ -397,4 +360,24 @@ class LongestPathAnalysis:
|
|||
Returns:
|
||||
LongestPathCollection containing all paths sorted by delay
|
||||
"""
|
||||
return LongestPathCollection(self.analysis.get_all_paths(module_name, True))
|
||||
return LongestPathCollection(
|
||||
self.analysis.get_all_paths(module_name, elaborate_paths))
|
||||
|
||||
|
||||
@dataclass
|
||||
class LongestPathHistory:
|
||||
"""
|
||||
Represents the history of a timing path, including intermediate debug points.
|
||||
This class provides a Python wrapper around the C++ LongestPathHistory,
|
||||
enabling iteration over the path's history and access to debug points.
|
||||
Attributes:
|
||||
history: The underlying C++ history object
|
||||
"""
|
||||
history: _LongestPathHistory
|
||||
|
||||
def __iter__(self):
|
||||
"""Iterate over the debug points in the history."""
|
||||
while not self.history.empty:
|
||||
object, delay, comment = self.history.head
|
||||
yield DebugPoint(Object(object), delay, comment)
|
||||
self.history = self.history.tail
|
||||
|
|
|
@ -4,5 +4,6 @@ add_subdirectory(ExportVerilog)
|
|||
add_subdirectory(Dialect)
|
||||
add_subdirectory(Firtool)
|
||||
add_subdirectory(RtgTool)
|
||||
add_subdirectory(Support)
|
||||
add_subdirectory(Synthesis)
|
||||
add_subdirectory(Transforms)
|
||||
|
|
|
@ -7,9 +7,12 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "circt-c/Dialect/AIG.h"
|
||||
#include "circt-c/Support/InstanceGraph.h"
|
||||
#include "circt/Dialect/AIG/AIGDialect.h"
|
||||
#include "circt/Dialect/AIG/AIGPasses.h"
|
||||
#include "circt/Dialect/AIG/Analysis/LongestPathAnalysis.h"
|
||||
#include "circt/Support/InstanceGraph.h"
|
||||
#include "circt/Support/InstanceGraphInterface.h"
|
||||
#include "mlir-c/BuiltinAttributes.h"
|
||||
#include "mlir-c/IR.h"
|
||||
#include "mlir-c/Support.h"
|
||||
|
@ -17,7 +20,11 @@
|
|||
#include "mlir/CAPI/Registration.h"
|
||||
#include "mlir/CAPI/Support.h"
|
||||
#include "mlir/Pass/AnalysisManager.h"
|
||||
#include "llvm/ADT/ImmutableList.h"
|
||||
#include "llvm/ADT/PointerUnion.h"
|
||||
#include "llvm/Support/JSON.h"
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
using namespace circt;
|
||||
using namespace circt::aig;
|
||||
|
@ -34,6 +41,34 @@ struct LongestPathAnalysisWrapper {
|
|||
|
||||
DEFINE_C_API_PTR_METHODS(AIGLongestPathAnalysis, LongestPathAnalysisWrapper)
|
||||
DEFINE_C_API_PTR_METHODS(AIGLongestPathCollection, LongestPathCollection)
|
||||
DEFINE_C_API_PTR_METHODS(AIGLongestPathDataflowPath, DataflowPath)
|
||||
DEFINE_C_API_PTR_METHODS(AIGLongestPathHistory,
|
||||
llvm::ImmutableListImpl<DebugPoint>)
|
||||
|
||||
// AIGLongestPathObject is a pointer to either an Object or an OutputPort so we
|
||||
// can not use DEFINE_C_API_PTR_METHODS.
|
||||
llvm::PointerUnion<Object *, DataflowPath::OutputPort *>
|
||||
unwrap(AIGLongestPathObject object) {
|
||||
return llvm::PointerUnion<
|
||||
Object *, DataflowPath::OutputPort *>::getFromOpaqueValue(object.ptr);
|
||||
}
|
||||
|
||||
AIGLongestPathObject
|
||||
wrap(llvm::PointerUnion<Object *, DataflowPath::OutputPort *> object) {
|
||||
return AIGLongestPathObject{object.getOpaqueValue()};
|
||||
}
|
||||
|
||||
AIGLongestPathObject wrap(const Object *object) {
|
||||
auto ptr = llvm::PointerUnion<Object *, DataflowPath::OutputPort *>(
|
||||
const_cast<Object *>(object));
|
||||
return wrap(ptr);
|
||||
}
|
||||
|
||||
AIGLongestPathObject wrap(const DataflowPath::OutputPort *object) {
|
||||
auto ptr = llvm::PointerUnion<Object *, DataflowPath::OutputPort *>(
|
||||
const_cast<DataflowPath::OutputPort *>(object));
|
||||
return wrap(ptr);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// LongestPathAnalysis C API
|
||||
|
@ -92,30 +127,116 @@ size_t aigLongestPathCollectionGetSize(AIGLongestPathCollection collection) {
|
|||
return wrapper->paths.size();
|
||||
}
|
||||
|
||||
MlirStringRef
|
||||
aigLongestPathCollectionGetPath(AIGLongestPathCollection collection,
|
||||
int pathIndex) {
|
||||
// Get a specific path from the collection as DataflowPath object
|
||||
AIGLongestPathDataflowPath
|
||||
aigLongestPathCollectionGetDataflowPath(AIGLongestPathCollection collection,
|
||||
size_t index) {
|
||||
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);
|
||||
auto &path = wrapper->paths[index];
|
||||
return wrap(&path);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// DataflowPath
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
int64_t aigLongestPathDataflowPathGetDelay(AIGLongestPathDataflowPath path) {
|
||||
auto *wrapper = unwrap(path);
|
||||
return wrapper->getDelay();
|
||||
}
|
||||
|
||||
AIGLongestPathObject
|
||||
aigLongestPathDataflowPathGetFanIn(AIGLongestPathDataflowPath path) {
|
||||
auto *wrapper = unwrap(path);
|
||||
auto &fanIn = wrapper->getFanIn();
|
||||
return wrap(const_cast<Object *>(&fanIn));
|
||||
}
|
||||
|
||||
AIGLongestPathObject
|
||||
aigLongestPathDataflowPathGetFanOut(AIGLongestPathDataflowPath path) {
|
||||
auto *wrapper = unwrap(path);
|
||||
if (auto *object = std::get_if<Object>(&wrapper->getFanOut())) {
|
||||
return wrap(object);
|
||||
}
|
||||
auto *ptr = std::get_if<DataflowPath::OutputPort>(&wrapper->getFanOut());
|
||||
return wrap(ptr);
|
||||
}
|
||||
|
||||
AIGLongestPathHistory
|
||||
aigLongestPathDataflowPathGetHistory(AIGLongestPathDataflowPath path) {
|
||||
auto *wrapper = unwrap(path);
|
||||
return wrap(const_cast<llvm::ImmutableListImpl<DebugPoint> *>(
|
||||
wrapper->getHistory().getInternalPointer()));
|
||||
}
|
||||
|
||||
MlirOperation
|
||||
aigLongestPathDataflowPathGetRoot(AIGLongestPathDataflowPath path) {
|
||||
auto *wrapper = unwrap(path);
|
||||
return wrap(wrapper->getRoot());
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// History
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
bool aigLongestPathHistoryIsEmpty(AIGLongestPathHistory history) {
|
||||
auto *wrapper = unwrap(history);
|
||||
return llvm::ImmutableList<DebugPoint>(wrapper).isEmpty();
|
||||
}
|
||||
|
||||
void aigLongestPathHistoryGetHead(AIGLongestPathHistory history,
|
||||
AIGLongestPathObject *object, int64_t *delay,
|
||||
MlirStringRef *comment) {
|
||||
auto *wrapper = unwrap(history);
|
||||
auto list = llvm::ImmutableList<DebugPoint>(wrapper);
|
||||
|
||||
auto &head = list.getHead();
|
||||
*object = wrap(&head.object);
|
||||
*delay = head.delay;
|
||||
*comment = mlirStringRefCreate(head.comment.data(), head.comment.size());
|
||||
}
|
||||
|
||||
AIGLongestPathHistory
|
||||
aigLongestPathHistoryGetTail(AIGLongestPathHistory history) {
|
||||
auto *wrapper = unwrap(history);
|
||||
auto list = llvm::ImmutableList<DebugPoint>(wrapper);
|
||||
auto *tail = list.getTail().getInternalPointer();
|
||||
return wrap(const_cast<llvm::ImmutableListImpl<DebugPoint> *>(tail));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Object
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
IgraphInstancePath
|
||||
aigLongestPathObjectGetInstancePath(AIGLongestPathObject object) {
|
||||
auto *ptr = dyn_cast<Object *>(unwrap(object));
|
||||
if (ptr) {
|
||||
IgraphInstancePath result;
|
||||
result.ptr = const_cast<igraph::InstanceOpInterface *>(
|
||||
ptr->instancePath.getPath().data());
|
||||
result.size = ptr->instancePath.getPath().size();
|
||||
return result;
|
||||
}
|
||||
|
||||
// This is output port so the instance path is empty.
|
||||
return {nullptr, 0};
|
||||
}
|
||||
|
||||
MlirStringRef aigLongestPathObjectName(AIGLongestPathObject rawObject) {
|
||||
auto ptr = unwrap(rawObject);
|
||||
if (auto *object = dyn_cast<Object *>(ptr)) {
|
||||
auto name = object->getName();
|
||||
return mlirStringRefCreate(name.data(), name.size());
|
||||
}
|
||||
auto [module, resultNumber, _] = *dyn_cast<DataflowPath::OutputPort *>(ptr);
|
||||
auto name = module.getOutputName(resultNumber);
|
||||
return mlirStringRefCreate(name.data(), name.size());
|
||||
}
|
||||
|
||||
size_t aigLongestPathObjectBitPos(AIGLongestPathObject rawObject) {
|
||||
auto ptr = unwrap(rawObject);
|
||||
if (auto *object = dyn_cast<Object *>(ptr))
|
||||
return object->bitPos;
|
||||
return std::get<2>(*dyn_cast<DataflowPath::OutputPort *>(ptr));
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ add_circt_public_c_api_library(CIRCTCAPIAIG
|
|||
CIRCTAIG
|
||||
CIRCTAIGAnalysis
|
||||
CIRCTAIGTransforms
|
||||
CIRCTCAPISupport
|
||||
)
|
||||
|
||||
add_circt_public_c_api_library(CIRCTCAPIArc
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
add_circt_public_c_api_library(CIRCTCAPISupport
|
||||
InstanceGraph.cpp
|
||||
|
||||
LINK_LIBS PUBLIC
|
||||
MLIRCAPIIR
|
||||
)
|
|
@ -0,0 +1,49 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "circt-c/Support/InstanceGraph.h"
|
||||
|
||||
#include "circt/Support/InstanceGraph.h"
|
||||
#include "mlir/CAPI/IR.h"
|
||||
#include "mlir/CAPI/Support.h"
|
||||
#include "mlir/CAPI/Utils.h"
|
||||
#include <string>
|
||||
|
||||
using namespace circt;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// C API Helpers
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
ArrayRef<igraph::InstanceOpInterface> unwrap(IgraphInstancePath instancePath) {
|
||||
return ArrayRef(
|
||||
reinterpret_cast<igraph::InstanceOpInterface *>(instancePath.ptr),
|
||||
instancePath.size);
|
||||
}
|
||||
|
||||
IgraphInstancePath wrap(ArrayRef<igraph::InstanceOpInterface> instancePath) {
|
||||
return IgraphInstancePath{
|
||||
const_cast<igraph::InstanceOpInterface *>(instancePath.data()),
|
||||
instancePath.size()};
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// InstancePath
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
size_t igraphInstancePathSize(IgraphInstancePath instancePath) {
|
||||
return unwrap(instancePath).size();
|
||||
}
|
||||
|
||||
MlirOperation igraphInstancePathGet(IgraphInstancePath instancePath,
|
||||
size_t index) {
|
||||
assert(instancePath.ptr);
|
||||
auto path = unwrap(instancePath);
|
||||
Operation *operation = path[index];
|
||||
return wrap(operation);
|
||||
}
|
|
@ -125,3 +125,19 @@ target_link_libraries(circt-capi-aig-test
|
|||
CIRCTCAPIHW
|
||||
CIRCTCAPISeq
|
||||
)
|
||||
|
||||
add_llvm_executable(circt-capi-support-test
|
||||
PARTIAL_SOURCES_INTENDED
|
||||
support.c
|
||||
)
|
||||
llvm_update_compile_flags(circt-capi-support-test)
|
||||
|
||||
target_link_libraries(circt-capi-support-test
|
||||
PRIVATE
|
||||
|
||||
MLIRCAPIIR
|
||||
CIRCTCAPIAIG
|
||||
CIRCTCAPIHW
|
||||
CIRCTCAPISeq
|
||||
CIRCTCAPISupport
|
||||
)
|
||||
|
|
116
test/CAPI/aig.c
116
test/CAPI/aig.c
|
@ -42,14 +42,14 @@ void testLongestPathAnalysis(void) {
|
|||
|
||||
// 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"
|
||||
"hw.module private @ch(in %c : !seq.clock, in %a: i2, out x: i2) {\n"
|
||||
" %p = seq.compreg %q, %c : i2\n"
|
||||
" %q = aig.and_inv %p, %p {sv.namehint = \"q\"}: i2\n"
|
||||
" hw.output %p: i2\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"
|
||||
"hw.module private @top(in %c : !seq.clock, in %a: i2) {\n"
|
||||
" %0 = hw.instance \"i2\" @ch(c: %c: !seq.clock, a: %a: i2) -> (x: i2)\n"
|
||||
" %1 = hw.instance \"i2\" @ch(c: %c: !seq.clock, a: %a: i2) -> (x: i2)\n"
|
||||
"}\n";
|
||||
// clang-format on
|
||||
|
||||
|
@ -71,19 +71,78 @@ void testLongestPathAnalysis(void) {
|
|||
|
||||
size_t pathCount = aigLongestPathCollectionGetSize(collection1);
|
||||
printf("Path count with elaboration: %zu\n", pathCount);
|
||||
// CHECK: Path count with elaboration: 2
|
||||
// CHECK: Path count with elaboration: 4
|
||||
|
||||
pathCount = aigLongestPathCollectionGetSize(collection2);
|
||||
printf("Path count without elaboration: %zu\n", pathCount);
|
||||
// CHECK: Path count without elaboration: 1
|
||||
// CHECK: Path count without elaboration: 2
|
||||
|
||||
// Test getting individual paths
|
||||
// Test DataflowPath API
|
||||
if (pathCount > 0) {
|
||||
AIGLongestPathDataflowPath path =
|
||||
aigLongestPathCollectionGetDataflowPath(collection1, 0);
|
||||
|
||||
// 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":[{
|
||||
int64_t delay = aigLongestPathDataflowPathGetDelay(path);
|
||||
printf("Path delay: %lld\n", (long long)delay);
|
||||
// CHECK: Path delay: 1
|
||||
|
||||
AIGLongestPathObject fanIn = aigLongestPathDataflowPathGetFanIn(path);
|
||||
AIGLongestPathObject fanOut = aigLongestPathDataflowPathGetFanOut(path);
|
||||
|
||||
// Test Object API
|
||||
MlirStringRef fanInName = aigLongestPathObjectName(fanIn);
|
||||
MlirStringRef fanOutName = aigLongestPathObjectName(fanOut);
|
||||
size_t fanInBitPos = aigLongestPathObjectBitPos(fanIn);
|
||||
size_t fanOutBitPos = aigLongestPathObjectBitPos(fanOut);
|
||||
|
||||
printf("FanIn: %.*s[%zu]\n", (int)fanInName.length, fanInName.data,
|
||||
fanInBitPos);
|
||||
printf("FanOut: %.*s[%zu]\n", (int)fanOutName.length, fanOutName.data,
|
||||
fanOutBitPos);
|
||||
// CHECK: FanIn: p[[[BIT:[0-9]]]]
|
||||
// CHECK: FanOut: p[[[BIT]]]
|
||||
|
||||
// Test instance path
|
||||
IgraphInstancePath fanInPath = aigLongestPathObjectGetInstancePath(fanIn);
|
||||
IgraphInstancePath fanOutPath =
|
||||
aigLongestPathObjectGetInstancePath(fanOut);
|
||||
printf("FanIn instance path size: %zu\n", fanInPath.size);
|
||||
printf("FanOut instance path size: %zu\n", fanOutPath.size);
|
||||
// CHECK: FanIn instance path size: 1
|
||||
// CHECK: FanOut instance path size: 1
|
||||
|
||||
// Test History API
|
||||
AIGLongestPathHistory history =
|
||||
aigLongestPathDataflowPathGetHistory(path);
|
||||
bool isEmpty = aigLongestPathHistoryIsEmpty(history);
|
||||
printf("History is empty: %s\n", isEmpty ? "true" : "false");
|
||||
// CHECK: History is empty: false
|
||||
|
||||
if (!isEmpty) {
|
||||
AIGLongestPathObject historyObject;
|
||||
int64_t historyDelay;
|
||||
MlirStringRef historyComment;
|
||||
|
||||
aigLongestPathHistoryGetHead(history, &historyObject, &historyDelay,
|
||||
&historyComment);
|
||||
printf("History head delay: %lld\n", (long long)historyDelay);
|
||||
printf("History head comment: %.*s\n", (int)historyComment.length,
|
||||
historyComment.data);
|
||||
// CHECK: History head delay: 1
|
||||
// CHECK: History head comment: namehint
|
||||
|
||||
AIGLongestPathHistory tail = aigLongestPathHistoryGetTail(history);
|
||||
bool tailIsEmpty = aigLongestPathHistoryIsEmpty(tail);
|
||||
printf("History tail is empty: %s\n", tailIsEmpty ? "true" : "false");
|
||||
// CHECK: History tail is empty: true
|
||||
}
|
||||
|
||||
// Test root operation
|
||||
MlirOperation root = aigLongestPathDataflowPathGetRoot(path);
|
||||
printf("Root operation is null: %s\n",
|
||||
mlirOperationIsNull(root) ? "true" : "false");
|
||||
// CHECK: Root operation is null: false
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
aigLongestPathCollectionDestroy(collection1);
|
||||
|
@ -103,9 +162,20 @@ void testLongestPathAnalysis(void) {
|
|||
AIGLongestPathCollection collection =
|
||||
aigLongestPathAnalysisGetAllPaths(analysis, moduleName, true);
|
||||
|
||||
MlirStringRef pathJson = aigLongestPathCollectionGetPath(collection, 1);
|
||||
printf("Path: %.*s\n", (int)pathJson.length, pathJson.data);
|
||||
// CHECK: "history":[]
|
||||
if (!aigLongestPathCollectionIsNull(collection)) {
|
||||
size_t size = aigLongestPathCollectionGetSize(collection);
|
||||
if (size > 1) {
|
||||
AIGLongestPathDataflowPath path =
|
||||
aigLongestPathCollectionGetDataflowPath(collection, 1);
|
||||
|
||||
AIGLongestPathHistory history =
|
||||
aigLongestPathDataflowPathGetHistory(path);
|
||||
bool isEmpty = aigLongestPathHistoryIsEmpty(history);
|
||||
printf("History without debug points is empty: %s\n",
|
||||
isEmpty ? "true" : "false");
|
||||
// CHECK: History without debug points is empty: true
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
aigLongestPathCollectionDestroy(collection);
|
||||
|
@ -146,17 +216,7 @@ void testErrorHandling(void) {
|
|||
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);
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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-support-test 2>&1 | FileCheck %s
|
||||
*/
|
||||
|
||||
#include "circt-c/Support/InstanceGraph.h"
|
||||
|
||||
#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 testInstancePath(void) {
|
||||
MlirContext ctx = mlirContextCreate();
|
||||
mlirDialectHandleLoadDialect(mlirGetDialectHandle__hw__(), ctx);
|
||||
mlirDialectHandleLoadDialect(mlirGetDialectHandle__seq__(), ctx);
|
||||
|
||||
// clang-format off
|
||||
const char *moduleStr =
|
||||
"hw.module private @foo(in %c : !seq.clock, in %a: i1, out x: i1) {\n"
|
||||
" %0 = seq.compreg %0, %c : i1\n"
|
||||
" hw.output %0: i1\n"
|
||||
"}\n"
|
||||
"hw.module private @bar(in %c : !seq.clock, in %a: i1, out x: i1) {\n"
|
||||
" %0 = hw.instance \"foo\" @foo(c: %c: !seq.clock, a: %a: i1) -> (x: i1)\n"
|
||||
" hw.output %0: i1\n"
|
||||
"}\n"
|
||||
"hw.module private @top(in %c : !seq.clock, in %a: i1, out x: i1) {\n"
|
||||
" %0 = hw.instance \"bar\" @bar(c: %c: !seq.clock, a: %a: i1) -> (x: i1)\n"
|
||||
" hw.output %0: i1\n"
|
||||
"}\n";
|
||||
// clang-format on
|
||||
|
||||
MlirModule module =
|
||||
mlirModuleCreateParse(ctx, mlirStringRefCreateFromCString(moduleStr));
|
||||
|
||||
MlirOperation moduleOp = mlirModuleGetOperation(module);
|
||||
|
||||
// Note: We test IgraphInstancePath functionality through the AIG longest path
|
||||
// analysis since it provides access to InstancePath objects. While
|
||||
// InstancePath objects are created in InstancePathCache, that interface is
|
||||
// not yet exposed through the C API. Using the analysis results is sufficient
|
||||
// for testing purposes without needing to expose additional InstancePathCache
|
||||
// APIs.
|
||||
|
||||
AIGLongestPathAnalysis analysis =
|
||||
aigLongestPathAnalysisCreate(moduleOp, true);
|
||||
|
||||
MlirStringRef moduleName = mlirStringRefCreateFromCString("top");
|
||||
AIGLongestPathCollection collection =
|
||||
aigLongestPathAnalysisGetAllPaths(analysis, moduleName, true);
|
||||
|
||||
if (aigLongestPathCollectionIsNull(collection) ||
|
||||
aigLongestPathCollectionGetSize(collection) < 1)
|
||||
return;
|
||||
|
||||
// Get instance path of fanin object.
|
||||
IgraphInstancePath instancePath =
|
||||
aigLongestPathObjectGetInstancePath(aigLongestPathDataflowPathGetFanIn(
|
||||
aigLongestPathCollectionGetDataflowPath(collection, 0)));
|
||||
|
||||
if (instancePath.size != 2) {
|
||||
printf("Instance path size mismatch\n");
|
||||
return;
|
||||
}
|
||||
MlirOperation bar = igraphInstancePathGet(instancePath, 0);
|
||||
MlirOperation foo = igraphInstancePathGet(instancePath, 1);
|
||||
|
||||
// CHECK: hw.instance "bar" @bar
|
||||
mlirOperationDump(bar);
|
||||
// CHECK-NEXT: hw.instance "foo" @foo
|
||||
mlirOperationDump(foo);
|
||||
|
||||
aigLongestPathCollectionDestroy(collection);
|
||||
aigLongestPathAnalysisDestroy(analysis);
|
||||
|
||||
mlirModuleDestroy(module);
|
||||
mlirContextDestroy(ctx);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
testInstancePath();
|
||||
|
||||
printf("=== All tests completed ===\n");
|
||||
// CHECK: === All tests completed ===
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -26,6 +26,7 @@ set(CIRCT_TEST_DEPENDS
|
|||
circt-capi-rtg-pipelines-test
|
||||
circt-capi-rtg-test
|
||||
circt-capi-rtgtest-test
|
||||
circt-capi-support-test
|
||||
circt-as
|
||||
circt-bmc
|
||||
circt-dis
|
||||
|
|
|
@ -61,9 +61,10 @@ tools = [
|
|||
'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'
|
||||
'circt-capi-rtg-test', 'circt-capi-rtgtest-test', 'circt-capi-support-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