Skip to content

Commit c85fd42

Browse files
authored
[ABI] Add begin_index to TypeAttrColumn (#471)
This PR adds a begin_index field to TypeAttrColumn. The begin_index enables the type attributes to store narrowly a range of type indices which can be useful when type attribute is narrowed to specific subscope where objects are allocated continuously so we can optimize for space and locality. As of now the accessor of the TypeAttrColumn is limited to extra/cc so impact is limited. To be careful, we begin_index is set to 0 for next few versions and will migrate to nonzero size in 1.0 (so i64 platform size is compatible)
1 parent 7786133 commit c85fd42

File tree

8 files changed

+100
-34
lines changed

8 files changed

+100
-34
lines changed

include/tvm/ffi/any.h

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -626,12 +626,14 @@ struct AnyHash {
626626
} else {
627627
if (src.data_.type_index >= TypeIndex::kTVMFFIStaticObjectBegin) {
628628
static const TVMFFITypeAttrColumn* custom_hash_column = GetAnyHashTypeAttrColumn();
629-
if (custom_hash_column != nullptr &&
630-
static_cast<size_t>(src.data_.type_index) < custom_hash_column->size) {
631-
const TVMFFIAny& custom_any_hash = custom_hash_column->data[src.data_.type_index];
632-
if (custom_any_hash.type_index != TypeIndex::kTVMFFINone) {
633-
return details::StableHashCombine(src.data_.type_index,
634-
CallCustomAnyHash(custom_any_hash, src));
629+
if (custom_hash_column != nullptr) {
630+
int32_t offset = src.data_.type_index - custom_hash_column->begin_index;
631+
if (offset >= 0 && offset < custom_hash_column->size) {
632+
const TVMFFIAny& custom_any_hash = custom_hash_column->data[offset];
633+
if (custom_any_hash.type_index != TypeIndex::kTVMFFINone) {
634+
return details::StableHashCombine(src.data_.type_index,
635+
CallCustomAnyHash(custom_any_hash, src));
636+
}
635637
}
636638
}
637639
}
@@ -724,11 +726,13 @@ struct AnyEqual {
724726
}
725727
if (lhs.data_.type_index >= TypeIndex::kTVMFFIStaticObjectBegin) {
726728
static const TVMFFITypeAttrColumn* custom_equal_column = GetAnyEqualTypeAttrColumn();
727-
if (custom_equal_column != nullptr &&
728-
static_cast<size_t>(lhs.data_.type_index) < custom_equal_column->size) {
729-
const TVMFFIAny& custom_any_equal = custom_equal_column->data[lhs.data_.type_index];
730-
if (custom_any_equal.type_index != TypeIndex::kTVMFFINone) {
731-
return CallCustomAnyEqual(custom_any_equal, lhs, rhs);
729+
if (custom_equal_column != nullptr) {
730+
int32_t offset = lhs.data_.type_index - custom_equal_column->begin_index;
731+
if (offset >= 0 && offset < custom_equal_column->size) {
732+
const TVMFFIAny& custom_any_equal = custom_equal_column->data[offset];
733+
if (custom_any_equal.type_index != TypeIndex::kTVMFFINone) {
734+
return CallCustomAnyEqual(custom_any_equal, lhs, rhs);
735+
}
732736
}
733737
}
734738
}

include/tvm/ffi/c_api.h

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,20 +1070,42 @@ typedef struct {
10701070
} TVMFFITypeMetadata;
10711071

10721072
/*!
1073-
* \brief Column array that stores extra attributes about types
1073+
* \brief One column of a type–attribute table: extra attributes keyed by runtime type index.
10741074
*
1075-
* The attributes stored in a column array that can be looked up by type index.
1076-
* Note that the TypeAttr behaves like type_traits so column[T] so not contain
1077-
* attributes from base classes.
1075+
* TypeAttr is designed to support an open set of possible attributes that can be
1076+
* registered and queried across different downstream use cases.
1077+
*
1078+
* Conceptually, TypeAttr is a dynamic variant of TypeTraits as seen in C++/Rust.
1079+
* It behaves like c++ type_traits, so column[T] does not contain attributes from base classes.
1080+
*
1081+
* Typical use cases for TypeAttr include:
1082+
* - Storing extra magic trait functions that can customize behaviors like hash/eq/repr.
1083+
* - Storing extra metadata about type data structures (e.g., mutability) and properties of
1084+
* op/operator structures that can be used by compiler transformations (e.g., passes).
1085+
*
1086+
* This column covers type indices in the range [begin_index, begin_index + size).
1087+
* For a given type_index, the corresponding entry is data[type_index - begin_index]
1088+
* when begin_index <= type_index < begin_index + size; otherwise the entry is
1089+
* not present (treat as None/null).
10781090
*
1079-
* \note
10801091
* \sa TVMFFIRegisterTypeAttr
10811092
*/
10821093
typedef struct {
1083-
/*! \brief The data of the column. */
1094+
/*! \brief The data of the column, indexed by (type_index - begin_index). */
10841095
const TVMFFIAny* data;
1085-
/*! \brief The size of the column. */
1086-
size_t size;
1096+
/*!
1097+
* \brief The number of elements in the data array.
1098+
*
1099+
* The column covers type indices in the range [begin_index, begin_index + size).
1100+
* For a given type_index, the corresponding entry is data[type_index - begin_index]
1101+
* when begin_index <= type_index < begin_index + size.
1102+
*/
1103+
int32_t size;
1104+
/*!
1105+
* \brief The starting type index of the column data.
1106+
* A value of 0 means the data array starts at type_index 0.
1107+
*/
1108+
int32_t begin_index;
10871109
} TVMFFITypeAttrColumn;
10881110

10891111
/*!

include/tvm/ffi/reflection/accessor.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,12 @@ class TypeAttrColumn {
154154
* \return The type attribute column.
155155
*/
156156
AnyView operator[](int32_t type_index) const {
157-
size_t tindex = static_cast<size_t>(type_index);
158-
if (tindex >= column_->size) {
157+
int32_t offset = type_index - column_->begin_index;
158+
if (offset < 0 || offset >= column_->size) {
159159
return AnyView();
160160
}
161161
const AnyView* any_view_data = reinterpret_cast<const AnyView*>(column_->data);
162-
return any_view_data[tindex];
162+
return any_view_data[offset];
163163
}
164164

165165
private:

python/tvm_ffi/cython/base.pxi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,8 @@ cdef extern from "tvm/ffi/c_api.h":
249249

250250
ctypedef struct TVMFFITypeAttrColumn:
251251
const TVMFFIAny* data
252-
size_t size
252+
int32_t size
253+
int32_t begin_index
253254

254255
int TVMFFIObjectDecRef(TVMFFIObjectHandle obj) nogil
255256
int TVMFFIObjectIncRef(TVMFFIObjectHandle obj) nogil

python/tvm_ffi/cython/object.pxi

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -535,9 +535,13 @@ def _lookup_type_attr(type_index: int32_t, attr_key: str) -> Any:
535535
cdef ByteArrayArg attr_key_bytes = ByteArrayArg(c_str(attr_key))
536536
cdef const TVMFFITypeAttrColumn* column = TVMFFIGetTypeAttrColumn(&attr_key_bytes.cdata)
537537
cdef TVMFFIAny data
538-
if column == NULL or column.size <= type_index:
538+
cdef int32_t offset
539+
if column == NULL:
539540
return None
540-
return make_ret(column.data[type_index])
541+
offset = type_index - column.begin_index
542+
if offset < 0 or offset >= column.size:
543+
return None
544+
return make_ret(column.data[offset])
541545

542546

543547
def _type_cls_to_type_info(type_cls: type) -> TypeInfo | None:

rust/tvm-ffi-sys/src/c_api.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -339,10 +339,15 @@ pub struct TVMFFITypeMetadata {
339339
/// attributes from base classes.
340340
#[repr(C)]
341341
pub struct TVMFFITypeAttrColumn {
342-
/// The data of the column
342+
/// The data of the column, indexed by (type_index - begin_index).
343343
pub data: *const TVMFFIAny,
344-
/// The size of the column
345-
pub size: usize,
344+
/// The number of elements in the data array.
345+
/// The column covers type indices [begin_index, begin_index + size).
346+
pub size: i32,
347+
/// The starting type index of the column data.
348+
/// Lookup: if begin_index <= type_index < begin_index + size,
349+
/// the entry is data[(type_index - begin_index) as usize].
350+
pub begin_index: i32,
346351
}
347352

348353
/// Runtime type information for object type checking

src/ffi/object.cc

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -263,17 +263,29 @@ class TypeTable {
263263
column_index = (*it).second;
264264
}
265265
TypeAttrColumnData* column = type_attr_columns_[column_index].get();
266-
if (column->data_.size() < static_cast<size_t>(type_index) + 1) {
266+
if (type_index == kTVMFFINone) {
267+
// Sentinel: just ensure the column exists without registering a value.
268+
if (column->data_.empty()) {
269+
column->data = reinterpret_cast<const TVMFFIAny*>(column->data_.data());
270+
column->size = 0;
271+
column->begin_index = 0;
272+
}
273+
return;
274+
}
275+
// TODO(1.0): set begin_index to first registered type_index for sparse column storage
276+
// For now, begin_index is always 0 (resize from index 0).
277+
if (static_cast<size_t>(type_index) >= column->data_.size()) {
278+
// Extend back from index 0.
267279
column->data_.resize(static_cast<size_t>(type_index) + 1, Any(nullptr));
268-
column->data = reinterpret_cast<const TVMFFIAny*>(column->data_.data());
269-
column->size = column->data_.size();
270280
}
271-
if (type_index == kTVMFFINone) return;
272-
if (column->data_[type_index] != nullptr) {
281+
column->data = reinterpret_cast<const TVMFFIAny*>(column->data_.data());
282+
column->size = static_cast<int32_t>(column->data_.size());
283+
column->begin_index = 0;
284+
if (column->data_[type_index - column->begin_index] != nullptr) {
273285
TVM_FFI_THROW(RuntimeError) << "Type attribute `" << name_str << "` is already set for type `"
274286
<< TypeIndexToTypeKey(type_index) << "`";
275287
}
276-
column->data_[type_index] = value_view;
288+
column->data_[type_index - column->begin_index] = value_view;
277289
}
278290
const TVMFFITypeAttrColumn* GetTypeAttrColumn(const TVMFFIByteArray* name) {
279291
String name_str(*name);

tests/cpp/test_reflection.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,24 @@ TEST(Reflection, TypeAttrColumn) {
179179
EXPECT_EQ(size_attr[TIntObj::RuntimeTypeIndex()].cast<int>(), sizeof(TIntObj));
180180
}
181181

182+
TEST(Reflection, TypeAttrColumnBeginIndex) {
183+
// Get the column and verify begin_index
184+
TVMFFIByteArray attr_name = {"test.size", std::char_traits<char>::length("test.size")};
185+
const TVMFFITypeAttrColumn* column = TVMFFIGetTypeAttrColumn(&attr_name);
186+
ASSERT_NE(column, nullptr);
187+
// begin_index should be >= 0
188+
EXPECT_GE(column->begin_index, 0);
189+
// size should cover the range from begin_index
190+
EXPECT_GT(column->size, 0);
191+
// verify that lookup of a type_index below begin_index returns None
192+
reflection::TypeAttrColumn size_attr("test.size");
193+
AnyView result = size_attr[0]; // index 0 is kTVMFFINone, unlikely to have this attr
194+
(void)result; // suppress unused variable warning; we only verify no crash occurs
195+
// The result may or may not be None depending on begin_index; the key is no crash.
196+
// verify the known registered entry still works
197+
EXPECT_EQ(size_attr[TIntObj::RuntimeTypeIndex()].cast<int>(), sizeof(TIntObj));
198+
}
199+
182200
TVM_FFI_STATIC_INIT_BLOCK() {
183201
namespace refl = tvm::ffi::reflection;
184202
refl::GlobalDef().def_method("testing.Int_GetValue", &TIntObj::GetValue);

0 commit comments

Comments
 (0)