Skip to content

Commit 73574c3

Browse files
committed
Allow strings as constant kernel arguments
Signed-off-by: Greg Bonik <gbonik@nvidia.com>
1 parent f4c2ac6 commit 73574c3

6 files changed

Lines changed: 64 additions & 2 deletions

File tree

cext/py.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,12 @@ static inline PyPtr getattr(const PyPtr& obj, const char* attrname) {
256256
return getattr(obj.get(), attrname);
257257
}
258258

259+
static inline void pyunicode_intern_in_place(PyPtr* s) {
260+
PyObject* raw = s->release();
261+
PyUnicode_InternInPlace(&raw);
262+
*s = steal(raw);
263+
}
264+
259265
struct ErrorGuard {
260266
SavedException exc;
261267

cext/tile_kernel.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,7 @@ struct AggregateArgType {
573573
X(Int) \
574574
X(Float) \
575575
X(None_) \
576+
X(String) \
576577
X(Enum) \
577578
X(NativeDType) \
578579
X(ForeignDType)
@@ -651,6 +652,7 @@ enum class PythonArgKind : uint8_t {
651652
ConstantInt,
652653
ConstantFloat,
653654
ConstantNone,
655+
ConstantString,
654656
IdentityConstant,
655657
ForeignDTypeConstant,
656658
// A torch.Tensor that we can access via torch._C._to_dlpack
@@ -675,6 +677,7 @@ static inline PythonArgKind constant_kind_as_arg_kind(ConstantKind kind) {
675677
case ConstantKind::Int: return PythonArgKind::ConstantInt;
676678
case ConstantKind::Float: return PythonArgKind::ConstantFloat;
677679
case ConstantKind::None_: return PythonArgKind::ConstantNone;
680+
case ConstantKind::String: return PythonArgKind::ConstantString;
678681
case ConstantKind::Enum: return PythonArgKind::IdentityConstant;
679682
case ConstantKind::NativeDType: return PythonArgKind::IdentityConstant;
680683
case ConstantKind::ForeignDType: return PythonArgKind::ForeignDTypeConstant;
@@ -689,6 +692,7 @@ static ParameterKind::Category param_category_from_pyarg_kind(PythonArgKind k) {
689692
case PythonArgKind::ConstantInt: return ParameterKind::ConstantInt;
690693
case PythonArgKind::ConstantFloat: return ParameterKind::ConstantFloat;
691694
case PythonArgKind::ConstantNone: return ParameterKind::ConstantNone;
695+
case PythonArgKind::ConstantString: return ParameterKind::IdentityConstant;
692696
case PythonArgKind::IdentityConstant: return ParameterKind::IdentityConstant;
693697
case PythonArgKind::ForeignDTypeConstant: return ParameterKind::IdentityConstant;
694698
case PythonArgKind::TorchTensorDlpack: return ParameterKind::Array;
@@ -915,6 +919,9 @@ static std::optional<ConstantKind> classify_constant(PyObject* obj, bool kernel_
915919
if (obj == Py_None)
916920
return ConstantKind::None_;
917921

922+
if (PyUnicode_CheckExact(obj))
923+
return ConstantKind::String;
924+
918925
if (PyObject_TypeCheck(obj, reinterpret_cast<PyTypeObject*>(g_enum_Enum_type)))
919926
return ConstantKind::Enum;
920927

@@ -2097,6 +2104,7 @@ static void extract_identity_constant(PyObject* object, Vec<int64_t>* constants,
20972104
identity_constants->push_back(object);
20982105
}
20992106

2107+
21002108
static PyPtr parse_identity_constant_constraint(ConstantCursor& cursor,
21012109
const Vec<PyObject*>& identity_constants) {
21022110
int64_t address = cursor.next();
@@ -2107,6 +2115,21 @@ static PyPtr parse_identity_constant_constraint(ConstantCursor& cursor,
21072115
CHECK_UNREACHABLE;
21082116
}
21092117

2118+
static Status extract_string_constant(PyObject* pyobj, Vec<int64_t>* constants,
2119+
Vec<PyObject*>* identity_constants,
2120+
Vec<PyPtr>* pyarg_refs) {
2121+
if (!PyUnicode_CHECK_INTERNED(pyobj)) {
2122+
PyPtr ref = newref(pyobj);
2123+
pyunicode_intern_in_place(&ref);
2124+
if (!PyUnicode_CHECK_INTERNED(ref.get()))
2125+
return raise(PyExc_RuntimeError, "Failed to intern a string kernel argument");
2126+
pyobj = ref.get();
2127+
pyarg_refs->push_back(std::move(ref));
2128+
}
2129+
extract_identity_constant(pyobj, constants, identity_constants);
2130+
return OK;
2131+
}
2132+
21102133
static Status extract_foreign_dtype_constant(PyObject* object, Vec<int64_t>* constants,
21112134
Vec<PyObject*>* identity_constants) {
21122135
HashMap<PyPtr, ForeignDTypeInfo>::Item* item = get_foreign_dtype_registry()->find(object);
@@ -2408,6 +2431,9 @@ static Status extract_arg(const DriverApi* driver, PyObject* obj, PythonArgKind
24082431
return OK;
24092432
case PythonArgKind::ConstantNone:
24102433
return OK;
2434+
case PythonArgKind::ConstantString:
2435+
return extract_string_constant(obj, &helper.constants, &helper.identity_constants,
2436+
&helper.pyarg_refs);
24112437
case PythonArgKind::IdentityConstant:
24122438
extract_identity_constant(obj, &helper.constants, &helper.identity_constants);
24132439
return OK;

src/cuda/tile/_ir/typing_support.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ def type_of_constant_python_value(val, typing_hooks: TypingHooks) -> Type:
137137
return typing_hooks.get_tensor_like_type(dtype_of_constant_scalar(val), ())
138138
case ConstantKind.None_:
139139
return NONE
140+
case ConstantKind.String:
141+
return StringTy(val)
140142
case ConstantKind.Enum:
141143
return EnumTy(val)
142144
case ConstantKind.NativeDType:
@@ -145,8 +147,6 @@ def type_of_constant_python_value(val, typing_hooks: TypingHooks) -> Type:
145147
return _get_dtype_spec(foreign_dtype_object_to_native(val))
146148
case _: assert False
147149

148-
if isinstance(val, str):
149-
return StringTy(val)
150150
if val is Ellipsis:
151151
return ELLIPSIS
152152
if isinstance(val, slice):

src/cuda/tile/compilation/_name_mangling.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,8 @@ def _mangle_constraint(p: ParameterConstraint, alias_group_map: dict[str, int],
212212
return "F" + f"{i:016x}"
213213
case ConstantKind.None_:
214214
return "Cn_"
215+
case ConstantKind.String:
216+
return "Cs_" + _mangle_string(p.value)
215217
case ConstantKind.Enum:
216218
assert cconv_v3_enabled()
217219
return "Ce_" + _mangle_enum_constant(p.value, collected_globals)
@@ -249,6 +251,8 @@ def _demangle_constraint(cursor: _Cursor,
249251
return ConstantConstraint(f)
250252
elif c == "Cn_" and cconv_v3_enabled():
251253
return ConstantConstraint(None)
254+
elif c == "Cs_" and cconv_v3_enabled():
255+
return ConstantConstraint(_demangle_string(cursor))
252256
elif c == "Ce_" and cconv_v3_enabled():
253257
return ConstantConstraint(_demangle_enum_constant(cursor, allowed_globals))
254258
elif c == "Cd_" and cconv_v3_enabled():

test/test_constant.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,22 @@ def kern(c: ct.Constant, out):
9292
out = torch.full((), -1, dtype=torch.int32, device="cuda")
9393
ct.launch(torch.cuda.current_stream(), (1,), kern, (123, out))
9494
assert out.tolist() == 0
95+
96+
97+
@pytest.mark.skipif(not cconv_v3_enabled(), reason="Requires cconv3 enabled")
98+
def test_str_kernel_argument():
99+
@ct.kernel
100+
def kern(c: ct.Constant, out):
101+
ct.scatter(out, 0, c == "hello")
102+
ct.scatter(out, 1, c == "test string for test_str_kernel_argument!")
103+
104+
# Repeat a few times to make sure we exercise different branches that depend on string interning
105+
for i in range(3):
106+
out = torch.full((2,), -1, dtype=torch.int32, device="cuda")
107+
ct.launch(torch.cuda.current_stream(), (1,), kern, ("hello", out))
108+
assert out.tolist() == [1, 0]
109+
110+
out = torch.full((2,), -1, dtype=torch.int32, device="cuda")
111+
ct.launch(torch.cuda.current_stream(), (1,), kern,
112+
("test string for test_str_kernel_argument!", out))
113+
assert out.tolist() == [0, 1]

test/test_name_mangling.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,13 @@ class MyEnum(Enum):
458458
"_Cn__I123_Cn_",
459459
id="none_constant",
460460
),
461+
462+
# String constant
463+
pytest.param(
464+
["Hello", "world!"],
465+
"_Cs_Hello_z_Cs_world_21_z",
466+
id="string_constant",
467+
),
461468
] if cconv_v3_enabled() else [])
462469
@pytest.mark.skipif(not cconv_v3_enabled(), reason="Requires cconv3 enabled")
463470
def test_name_mangling_cutile_python_v3(parameters, expected_suffix):

0 commit comments

Comments
 (0)