@@ -37,6 +37,7 @@ static PyObject* g_cooperative_pyunicode;
3737static PyObject* g_block_in_cluster_count_pyunicode;
3838static PyObject* g_preferred_block_in_cluster_count_pyunicode;
3939static PyObject* g_programmatic_dependent_launch_pyunicode;
40+ static PyObject* g___dataclass_fields___pyunicode;
4041
4142static PyTypeObject* g_torch_Tensor_type;
4243static PyTypeObject* g_torch_cuda_Stream_type;
@@ -250,7 +251,6 @@ static PyMethodDef CallingConvention_methods[] = {
250251 " cutile_python_v3()\n "
251252 " --\n\n "
252253 " Returns the ``cutile_python_v3`` calling convention.\n\n "
253-
254254 },
255255#endif
256256 {} // sentinel
@@ -430,6 +430,7 @@ static LaunchHelper* g_helper_freelist; // protected by the GIL or g_launch_mut
430430
431431namespace { struct LaunchHelperDeleter {
432432 void operator () (LaunchHelper* helper) const {
433+ helper->pyarg_refs .clear ();
433434 helper->next_free = g_helper_freelist;
434435 g_helper_freelist = helper;
435436 }
@@ -455,18 +456,37 @@ static LaunchHelperPtr launch_helper_get() {
455456 }
456457}
457458
459+
460+ namespace { struct DataclassInfo : SimpleRefcount<DataclassInfo> {
461+ PyPtr dataclass;
462+ Vec<PyPtr> field_names;
463+
464+ DataclassInfo (PyPtr dataclass, Vec<PyPtr> field_names)
465+ : dataclass(std::move(dataclass)), field_names(std::move(field_names))
466+ { }
467+ }; }
468+
469+
458470struct AggregateArgType {
459471 enum Kind {
460- Tuple
472+ Tuple,
473+ #ifdef ENABLE_CCONV_V3
474+ Dataclass
475+ #endif
461476 };
462477
463478 Kind kind;
479+ RefPtr<DataclassInfo> dataclass_info;
464480
465481 bool operator == (const AggregateArgType& other) const {
466482 if (kind != other.kind ) return false ;
467483 switch (kind) {
468484 case Kind::Tuple:
469485 return true ;
486+ #ifdef ENABLE_CCONV_V3
487+ case Kind::Dataclass:
488+ return dataclass_info->dataclass == other.dataclass_info ->dataclass ;
489+ #endif
470490 }
471491 CHECK (false );
472492 }
@@ -713,6 +733,44 @@ namespace { struct ProfileMapKey {
713733// We use the HashMap as a hash set by providing a dummy value type.
714734using ProfileMap = HashMap<ProfileMapKey, int /* dummy*/ >;
715735
736+
737+
738+ #ifdef ENABLE_CCONV_V3
739+ static Status get_dataclass_field_names (PyObject* cls, Vec<PyPtr>* field_names) {
740+ PyPtr fields = steal (PyObject_GetAttr (cls, g___dataclass_fields___pyunicode));
741+ if (!fields) return ErrorRaised;
742+
743+ PyPtr field_iter = steal (PyObject_GetIter (fields.get ()));
744+ if (!field_iter) return ErrorRaised;
745+
746+ while (PyPtr name = steal (PyIter_Next (field_iter.get ()))) {
747+ field_names->push_back (std::move (name));
748+ }
749+ if (PyErr_Occurred ())
750+ return ErrorRaised;
751+ return OK ;
752+ }
753+
754+
755+ static RefPtr<DataclassInfo> get_dataclass_info (PyTypeObject* ty) {
756+ static HashMap<PyPtr, RefPtr<DataclassInfo>>* cache;
757+ if (!cache) cache = new HashMap<PyPtr, RefPtr<DataclassInfo>>();
758+
759+ PyPtr cls_ref = newref (reinterpret_cast <PyObject*>(ty));
760+ HashMap<PyPtr, RefPtr<DataclassInfo>>::Item* cached = cache->find (cls_ref);
761+ if (cached) return cached->value ;
762+
763+ Vec<PyPtr> field_names;
764+ if (!get_dataclass_field_names (cls_ref.get (), &field_names))
765+ return {};
766+
767+ RefPtr<DataclassInfo> info = newref (new DataclassInfo (cls_ref, std::move (field_names)));
768+ cache->insert (cls_ref, info);
769+ return info;
770+ }
771+ #endif // ENABLE_CCONV_V3
772+
773+
716774namespace {struct AggregateArgInfo {
717775 size_t breadth_first_index;
718776 AggregateArgType type;
@@ -2151,10 +2209,21 @@ static PyPtr create_tuple_constraint(PyObject* items_list) {
21512209 return steal (PyObject_CallOneArg (constraint_class.get (), items_list));
21522210}
21532211
2212+ #ifdef ENABLE_CCONV_V3
2213+ static PyPtr create_dataclass_constraint (PyObject* dataclass, PyObject* items_list) {
2214+ PyObject* signature_module = get_signature_module ();
2215+ if (!signature_module) return {};
2216+ PyPtr constraint_class = getattr (signature_module, " DataclassConstraint" );
2217+ if (!constraint_class) return {};
2218+ return steal (PyObject_CallFunctionObjArgs (constraint_class.get (),
2219+ dataclass, items_list, nullptr ));
2220+ }
2221+ #endif
2222+
21542223static PyPtr parse_param_constraint (ConstantCursor& cursor,
21552224 Cursor<ParameterKind>* param_cursor,
21562225 Cursor<RefPtr<LeafAnnotationNode>>* annotation_cursor) {
2157- ParameterKind pk = param_cursor->next ();
2226+ const ParameterKind& pk = param_cursor->next ();
21582227 if (pk.category == ParameterKind::AggregateBegin) {
21592228 PyPtr items_list = steal (PyList_New (0 ));
21602229 if (!items_list) return {};
@@ -2169,6 +2238,11 @@ static PyPtr parse_param_constraint(ConstantCursor& cursor,
21692238 switch (pk.agg_type .kind ) {
21702239 case AggregateArgType::Tuple:
21712240 return create_tuple_constraint (items_list.get ());
2241+ #ifdef ENABLE_CCONV_V3
2242+ case AggregateArgType::Dataclass:
2243+ return create_dataclass_constraint (pk.agg_type .dataclass_info ->dataclass .get (),
2244+ items_list.get ());
2245+ #endif
21722246 }
21732247 CHECK (false );
21742248 }
@@ -2209,6 +2283,11 @@ static CallConvVersion minimum_calling_convention(
22092283 case AggregateArgType::Tuple:
22102284 require (CallConvVersion::CutilePython_V2);
22112285 break ;
2286+ #ifdef ENABLE_CCONV_V3
2287+ case AggregateArgType::Dataclass:
2288+ require (CallConvVersion::CutilePython_V3);
2289+ break ;
2290+ #endif
22122291 }
22132292 }
22142293 }
@@ -2832,9 +2911,24 @@ static Status stage_list_args_on_stream(const DriverApi* driver,
28322911 return OK ;
28332912}
28342913
2914+ #ifdef ENABLE_CCONV_V3
2915+ static bool is_dataclass (PyTypeObject* ty) {
2916+ return PyObject_HasAttr (reinterpret_cast <PyObject*>(ty),
2917+ g___dataclass_fields___pyunicode);
2918+ }
2919+ #endif
2920+
28352921static Result<std::optional<AggregateArgType>> classify_aggregate_type (PyTypeObject* ty) {
2836- if (ty == &PyTuple_Type) {
2837- return {{{ AggregateArgType::Tuple }}};
2922+ if (ty == nullptr ) {
2923+ return {std::nullopt };
2924+ } else if (ty == &PyTuple_Type) {
2925+ return {{{ AggregateArgType::Tuple, {} }}};
2926+ #ifdef ENABLE_CCONV_V3
2927+ } else if (is_dataclass (ty)) {
2928+ RefPtr<DataclassInfo> info = get_dataclass_info (ty);
2929+ if (!info) return ErrorRaised;
2930+ return {{{ AggregateArgType::Dataclass, std::move (info) }}};
2931+ #endif
28382932 } else {
28392933 return {std::nullopt };
28402934 }
@@ -2884,6 +2978,13 @@ static ErrorRaised_t raise_invalid_kernel_arg_type_impl(
28842978 case AggregateArgType::Tuple:
28852979 new_str = steal (PyUnicode_FromFormat (" %Uitem #%zu of " , ret.get (), path[i].item_idx ));
28862980 break ;
2981+ #ifdef ENABLE_CCONV_V3
2982+ case AggregateArgType::Dataclass:
2983+ CHECK (path[i].item_idx < agg_type->dataclass_info ->field_names .size ());
2984+ new_str = steal (PyUnicode_FromFormat (" %Ufield '%U' of " , ret.get (),
2985+ agg_type->dataclass_info ->field_names [path[i].item_idx ].get ()));
2986+ break ;
2987+ #endif
28872988 }
28882989 if (!new_str) return {};
28892990 ret = std::move (new_str);
@@ -3125,11 +3226,30 @@ static void gather_leaf_pyargs(const Vec<PyObject*>& pyarg_objs_breadth_first,
31253226 leaf_pyarg_objs->push_back (pyarg_objs_breadth_first[i]);
31263227}
31273228
3229+ #ifdef ENABLE_CCONV_V3
3230+ static Status expand_dataclass_instance (PyObject* arg, const DataclassInfo& info,
3231+ Vec<PyObject*>* pyarg_objs_breadth_first,
3232+ Vec<PyTypeObject*>* pyarg_types_breadth_first,
3233+ Vec<PyPtr>* pyarg_refs) {
3234+ for (const PyPtr& name : info.field_names ) {
3235+ PyPtr value = steal (PyObject_GetAttr (arg, name.get ()));
3236+ if (!value) return ErrorRaised;
3237+ pyarg_objs_breadth_first->push_back (value.get ());
3238+ pyarg_types_breadth_first->push_back (Py_TYPE (value.get ()));
3239+ pyarg_refs->push_back (std::move (value));
3240+ }
3241+ pyarg_objs_breadth_first->push_back (nullptr );
3242+ pyarg_types_breadth_first->push_back (nullptr );
3243+ return OK ;
3244+ }
3245+ #endif
3246+
31283247static Status expand_aggregate_arg (
31293248 PyObject* arg,
31303249 const AggregateArgType& agg_type,
31313250 Vec<PyObject*>* pyarg_objs_breadth_first,
3132- Vec<PyTypeObject*>* pyarg_types_breadth_first) {
3251+ Vec<PyTypeObject*>* pyarg_types_breadth_first,
3252+ Vec<PyPtr>* pyarg_refs) {
31333253 switch (agg_type.kind ) {
31343254 case AggregateArgType::Tuple:
31353255 CHECK (PyTuple_CheckExact (arg));
@@ -3138,6 +3258,11 @@ static Status expand_aggregate_arg(
31383258 pyarg_objs_breadth_first,
31393259 pyarg_types_breadth_first);
31403260 return OK ;
3261+ #ifdef ENABLE_CCONV_V3
3262+ case AggregateArgType::Dataclass:
3263+ return expand_dataclass_instance (arg, *agg_type.dataclass_info ,
3264+ pyarg_objs_breadth_first, pyarg_types_breadth_first, pyarg_refs);
3265+ #endif
31413266 }
31423267 CHECK (false );
31433268}
@@ -3150,7 +3275,8 @@ static PythonArgProfile* python_arg_profile_lookup_impl(
31503275 Vec<RefPtr<KernelFamily>>* kernel_families,
31513276 Vec<PyObject*>* pyarg_objs_breadth_first,
31523277 Vec<PyTypeObject*>* pyarg_types_breadth_first,
3153- Vec<PyObject*>* leaf_pyarg_objs) {
3278+ Vec<PyObject*>* leaf_pyarg_objs,
3279+ Vec<PyPtr>* pyarg_refs) {
31543280 ProfileMapQuery query = {pyarg_types_breadth_first, 0 , 0 };
31553281 query.mark_start ();
31563282 get_pyarg_objects_and_types (pyargs, num_pyargs,
@@ -3248,7 +3374,8 @@ static PythonArgProfile* python_arg_profile_lookup_impl(
32483374 for (const AggregateArgInfo& agg_info : next->aggregate_args ) {
32493375 PyObject* arg = (*pyarg_objs_breadth_first)[agg_info.breadth_first_index ];
32503376 if (!expand_aggregate_arg (arg, agg_info.type ,
3251- pyarg_objs_breadth_first, pyarg_types_breadth_first))
3377+ pyarg_objs_breadth_first, pyarg_types_breadth_first,
3378+ pyarg_refs))
32523379 return nullptr ;
32533380 }
32543381 query.mark_end ();
@@ -3275,7 +3402,8 @@ static PythonArgProfile* python_arg_profile_lookup(PyObject* const* pyargs,
32753402 &ctx_dispatcher->kernel_families ,
32763403 &helper->pyarg_objs_breadth_first ,
32773404 &helper->pyarg_types_breadth_first ,
3278- &helper->leaf_pyarg_objs );
3405+ &helper->leaf_pyarg_objs ,
3406+ &helper->pyarg_refs );
32793407}
32803408
32813409static Result<PreparedLaunch> prepare_launch (
@@ -4537,6 +4665,7 @@ Status tile_kernel_init(PyObject* m) {
45374665 INIT_STRING_CONSTANT (block_in_cluster_count);
45384666 INIT_STRING_CONSTANT (preferred_block_in_cluster_count);
45394667 INIT_STRING_CONSTANT (programmatic_dependent_launch);
4668+ INIT_STRING_CONSTANT (__dataclass_fields__);
45404669
45414670 g_constant_kind_enum = define_constant_kind_enum ().release ();
45424671 if (!g_constant_kind_enum) return ErrorRaised;
0 commit comments