[FEAT] Add structural map and inplace mutate APIs#649
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces the StructuralMapper and StructuralMapperObj APIs in include/tvm/ffi/extra/structural_map.h to support structural mapping and in-place mutation of object-backed values. It also adds corresponding custom hooks (kStructuralMap and kStructuralInplaceMutate), registers test leaf objects, and includes comprehensive unit tests. The review feedback suggests two minor optimizations in structural_map.h: avoiding a const_cast when obtaining the field address, and reusing the calculated field address instead of recalculating it for the field setter.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const void* field_addr = reinterpret_cast<const char*>(new_obj) + field_info->offset; | ||
| int ret_code = field_info->getter(const_cast<void*>(field_addr), | ||
| reinterpret_cast<TVMFFIAny*>(&field_value)); |
There was a problem hiding this comment.
Since new_obj is a non-const pointer (Object*), we can avoid the const_cast on field_addr by declaring it as void* and using reinterpret_cast<char*>(new_obj).
void* field_addr = reinterpret_cast<char*>(new_obj) + field_info->offset;
int ret_code = field_info->getter(field_addr,
reinterpret_cast<TVMFFIAny*>(&field_value));| void* new_field_addr = reinterpret_cast<char*>(new_obj) + field_info->offset; | ||
| ret_code = reflection::CallFieldSetter(field_info, new_field_addr, | ||
| reinterpret_cast<const TVMFFIAny*>(&new_field)); |
2d1fe02 to
94ef772
Compare
d407312 to
a72586a
Compare
1fa228c to
994c4f7
Compare
| * adding temporary internal owners. | ||
| * \return The transformed value, or an Error if validation or transformation failed. | ||
| */ | ||
| TVM_FFI_INLINE Expected<Any> DefaultMapOrInplaceMutateExpected(AnyView value, |
There was a problem hiding this comment.
I'm thinking if it would make sense to add another parameter to DefaultMapOrInplaceMutateExpected to control the selection between inplace_mutate and map, instead of obj->unique() to improve flexibility?
There was a problem hiding this comment.
Thanks @Seven-Streams ! This is a very insightful feedback. After thinking it over, I designed the ABI of MapOrInplaceMutate to accept three args, the third one is bool require_map, which allows the caller function to decide whether a mapping operation is required (inplace mutate is an optimization anyway).
For example, caller can specify require_map=True when node is not unique, in which inplace mutation is dangerous. In the default inplace mutator, reflection getter adds a second reference count, so it can specify require_map=True when node has ref_count > 2.
Another example is in StructuralMap, where require_map=True additionally holds when user provides a map-based callback, as implemented in the overwritten MapOrInplaceMutate dispatch function in StructuralMapCallbackMapperObj.
@tqchen Please do not hesitate to tell me if this design makes sense to you, or if you have any questions!
d635d50 to
5768af7
Compare
e59c54e to
1780cc1
Compare
No description provided.