Files
apache--tvm/python/tvm/backend/cuda/operator/intrinsics/_schema.py
T
wehub-resource-sync 26446540fa
Lint / lint (push) Has been cancelled
CI / MacOS (push) Has been cancelled
CI / Windows (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:36:25 +08:00

145 lines
5.9 KiB
Python

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# pylint: disable=invalid-name
"""Thin device-helper registration for TIRx intrinsic codegens.
Exposes one entry point: :func:`device_intrinsic`. Given an op name plus
``(helper_name, c_signature, body)`` (each a string or a ``(*args) -> str``
callable), it:
* wraps the body in
``__forceinline__ __device__ <ret> <name><sig> { <body> }``,
* registers a codegen function under the op name so
``call_intrin("", "tirx.<op_name>", *args)`` resolves to a call to that
helper. TVM Op registration is static C++ only.
Args passed to the codegen are split into ``(forward_args, attr_args)``:
the trailing ``n_attrs`` are attrs (consumed by the ``helper_name`` /
``c_signature`` / ``body`` callables but **not** forwarded to the helper),
and the rest are operand args (forwarded). The default ``n_attrs=0`` means
every arg is forwarded — appropriate for fixed-arity ops with literal
``c_signature`` and ``helper_name``.
Coerce / validate attrs explicitly inside the callables — there is no
``Choice`` / ``Bool`` / ``IntAttr`` machinery; just call ``parse_str`` /
``int`` / ``bool`` on the raw arg as needed.
"""
from __future__ import annotations
from collections.abc import Callable
from tvm.backend.cuda.op import cuda_func_call
from tvm.backend.cuda.operator.intrinsics.registry import register_codegen
# C primitive type → TVM dtype string. Used when the caller specifies a
# non-void ``return_type`` but no explicit ``tvm_return_type`` — the helper
# knows the TVM-side dtype from the C return type.
_C_TO_TVM_DTYPE = {
"float": "float32",
"double": "float64",
"uint32_t": "uint32",
"int32_t": "int32",
"uint64_t": "uint64",
"int64_t": "int64",
"uint16_t": "uint16",
"int16_t": "int16",
"unsigned long long": "uint64",
"long long": "int64",
"unsigned short": "uint16",
"bool": "bool",
"unsigned int": "uint32",
"int": "int32",
}
def device_intrinsic(
op_name: str,
*,
helper_name: str | Callable | None = None,
c_signature: str | Callable = "()",
body: str | Callable,
n_attrs: int = 0,
return_type: str | Callable = "void",
tvm_return_type: str | Callable | None = None,
templated: bool = False,
extra_deps: tuple = (),
) -> None:
"""Register a CUDA device-helper intrinsic.
Parameters
----------
op_name :
Registry key — ``call_intrin("", "tirx.<op_name>", ...)`` resolves
here. Also used as the default helper name (``tvm_builtin_<op_name>``)
when ``helper_name`` is not provided.
helper_name :
Literal C function name, OR ``(*args) -> str`` to compute it from
attr values. Defaults to ``f"tvm_builtin_{op_name}"``.
c_signature :
Literal C parameter list including outer parens (``"(int x, int y)"``),
OR ``(*args) -> str`` to compute it from attr values. Defaults to
``"()"``.
body :
Literal C body string (already indented), OR ``(*args) -> str``.
n_attrs :
Number of trailing args that are attrs (consumed by ``helper_name``
/ ``c_signature`` / ``body`` callables, NOT forwarded to the helper
as call arguments). The first ``len(args) - n_attrs`` args are the
operand args forwarded to the helper.
return_type :
C return type. Default ``"void"``. Either a literal string or
``(*args) -> str`` when the helper return type depends on attrs.
tvm_return_type :
TVM dtype for the call result, when the helper has a non-void
return. Either a literal string (``"int32"``) or ``(*args) -> str``.
If omitted and ``return_type`` is non-void, it is auto-derived from
the ``_C_TO_TVM_DTYPE`` table.
templated :
Prefix the helper with ``template <typename T>``.
extra_deps :
Helper-tag list (e.g. ``("get_tmem_addr",)``) forwarded as the second
element of the codegen result so the header generator emits the
prerequisite snippets.
"""
if helper_name is None:
helper_name = f"tvm_builtin_{op_name}"
extra_deps = tuple(extra_deps)
def codegen(*args):
forward = args if n_attrs == 0 else args[:-n_attrs]
name = helper_name(*args) if callable(helper_name) else helper_name
sig = c_signature(*args) if callable(c_signature) else c_signature
body_str = body(*args) if callable(body) else body
ret_type = return_type(*args) if callable(return_type) else return_type
prefix = "template <typename T>\n" if templated else ""
source_code = (
f"\n{prefix}__forceinline__ __device__ {ret_type} {name}{sig} {{\n{body_str}\n}}\n"
)
kwargs = {"source_code": source_code}
if tvm_return_type is not None:
kwargs["return_type"] = (
tvm_return_type(*args) if callable(tvm_return_type) else tvm_return_type
)
elif ret_type != "void":
kwargs["return_type"] = _C_TO_TVM_DTYPE.get(ret_type, ret_type)
result = cuda_func_call(name, *forward, **kwargs)
return (result, list(extra_deps)) if extra_deps else result
codegen.__name__ = f"codegen_{op_name}"
register_codegen(op_name)(codegen)