Files
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

1000 lines
22 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=redefined-builtin, wrong-import-order, no-member, invalid-name
"""IRBuilder for Relax dialect"""
import builtins
import functools
import inspect
from collections.abc import Callable
from typing import Any
import tvm
from tvm import DataType, relax
from tvm.ir import IRModule, VDevice
from tvm.relax import (
Call,
Expr,
ExternFunc,
ShapeExpr,
StringImm,
TupleGetItem,
Var,
VarBinding,
const,
)
from tvm.relax.dpl import PatternMatchingRewriter
############################### Operators ###############################
from tvm.relax.op import (
abs,
acos,
acosh,
add,
arange,
argmax,
argmin,
argsort,
asin,
asinh,
assert_op,
astype,
atan,
atan2,
atanh,
bitwise_and,
bitwise_not,
bitwise_or,
bitwise_xor,
broadcast_to,
bucketize,
builtin,
call_builtin_with_ctx,
call_dps_packed,
call_inplace_packed,
call_pure_packed,
call_tir,
call_tir_inplace,
call_tir_with_grad,
ccl,
ceil,
clip,
collapse_sum_like,
collapse_sum_to,
concat,
cos,
cosh,
cumprod,
cumsum,
dequantize,
divide,
dynamic_strided_slice,
einsum,
equal,
erf,
ewise_fma,
exp,
expand_dims,
eye,
eye_like,
flatten,
flip,
floor,
floor_divide,
floor_mod,
full,
full_like,
gather_elements,
gather_nd,
grad,
greater,
greater_equal,
hamming_window,
hint_on_device,
image,
index_put,
index_tensor,
invoke_closure,
invoke_pure_closure,
isfinite,
isinf,
isnan,
layout_transform,
left_shift,
less,
less_equal,
linear,
log,
log_add_exp,
logical_and,
logical_not,
logical_or,
logical_xor,
make_closure,
matmul,
max,
maximum,
mean,
median,
memory,
meshgrid,
min,
minimum,
mod,
multinomial_from_uniform,
multiply,
negative,
nn,
nonzero,
not_equal,
null_value,
one_hot,
ones,
ones_like,
outer,
permute_dims,
power,
print,
prod,
quantize,
repeat,
reshape,
reverse_sequence,
right_shift,
round,
rsqrt,
scatter_elements,
scatter_nd,
shape_of,
shape_to_tensor,
sigmoid,
sign,
sin,
sinh,
size,
slice_scatter,
sort,
split,
sqrt,
square,
squeeze,
stack,
std,
strided_slice,
subtract,
sum,
take,
tan,
tanh,
tensor_to_shape,
tile,
topk,
tril,
triu,
trunc,
unique,
variance,
vision,
vm,
where,
wrap_param,
zeros,
zeros_like,
)
from tvm.relax.op import (
call_py_func as _call_py_func,
)
from tvm.relax.op.builtin import stop_lift_params
from tvm.relax.type import Type
from tvm.relax.utils import convert_to_expr, gen_call_tir_inputs
from tvm.runtime import Object as tvm_Object
from tvm.runtime import ObjectConvertible
from tvm.runtime._tensor import (
cpu,
cuda,
device,
ext_dev,
hexagon,
metal,
opencl,
rocm,
vpi,
vulkan,
webgpu,
)
from tvm.script.ir_builder.ir import decl_function, lookup_vdevice
from . import _ffi_api, frame
##################### Python Native Function Alias ######################
py_print = builtins.print
py_tuple = tuple # pylint: disable=used-before-assignment
py_str = str # pylint: disable=used-before-assignment
################################ Device ################################
def to_vdevice(data: Expr, dst_vdevice: py_str | VDevice) -> Expr:
"""Copy data to the destination device.
Parameters
----------
data : Expr
The tensor to be copied.
dst_device : Union[py_str, VDevice]
The destination device where the data is copied to.
Returns
-------
result : Expr
The copied result.
"""
if isinstance(dst_vdevice, py_str):
if ":" in dst_vdevice:
split_vdev = dst_vdevice.split(":")
dst_vdevice = lookup_vdevice(split_vdev[0], int(split_vdev[1]))
else:
dst_vdevice = lookup_vdevice(dst_vdevice, 0)
return tvm.relax.op.to_vdevice(data, dst_vdevice)
############################### Function ################################
def function(is_pure: bool = True, is_private: bool = False) -> frame.FunctionFrame:
"""Start a function frame.
Parameters
----------
is_pure: bool
Whether the function is annotated as pure.
is_private : bool
Whether the function is annotated as private.
Returns
-------
frame: FunctionFrame
The constructed function frame.
"""
return _ffi_api.Function( # type: ignore[attr-defined] # pylint: disable=no-member
is_pure, is_private
)
def arg(name: py_str, ty: Type) -> Var:
"""Add a parameter to the last function frame.
Parameters
----------
name: str
The name of the parameter.
ty: Type
The type of the parameter
Returns
-------
var: Var
The created function parameter var.
"""
return _ffi_api.Arg(name, ty) # type: ignore[attr-defined] # pylint: disable=no-member
def func_name(name: py_str) -> None:
"""Specify the name of the last function frame.
Parameters
----------
name: str
The function name.
"""
return _ffi_api.FuncName(name) # type: ignore[attr-defined] # pylint: disable=no-member
def func_attr(attrs: dict[py_str, tvm_Object]) -> None:
"""Specify the attrs of the last function frame.
Parameters
----------
attrs: Dict[str, Object]
The function attrs.
"""
return _ffi_api.FuncAttrs(attrs) # type: ignore[attr-defined] # pylint: disable=no-member
def func_ret_type(ret_ty: Type) -> None:
"""Specify the return type of the last function frame.
Parameters
----------
ret_ty: Type
The function return type.
"""
return _ffi_api.FuncRetType(ret_ty) # type: ignore[attr-defined] # pylint: disable=no-member
def func_ret_ty(ret_ty: Type) -> None:
"""Backward-compatible alias for `func_ret_type`."""
return func_ret_type(ret_ty)
def func_ret_value(value: Expr) -> None:
"""Specify the return value of the last function frame.
Parameters
----------
value: Expr
The function return value.
"""
return _ffi_api.FuncRetValue(value) # type: ignore[attr-defined] # pylint: disable=no-member
def rewriter(rewriter_mod: IRModule | type) -> PatternMatchingRewriter:
"""Define a pattern-rewrite rule
The IRModule must have two publicly-exposed functions, `pattern`
and `replacement`, where `pattern` and `replacement` have the same
function signature.
.. code-block:: python
@R.rewriter
class RewriteAddIntoMultiply:
@R.function
def pattern(A: R.Tensor):
B = A + A
return B
@R.function
def replacement(A: R.Tensor):
B = A * 2
return B
Parameters
----------
rewriter_mod: Union[IRModule, Type]
Either an IRModule that defines a rewrite pattern, or a
TVMScript class that can be parsed into an IRModule.
Returns
-------
rewriter: PatternMatchingRewriter
A rewriter object, which can be applied either to a Relax
function or to an entire IRModule.
"""
if not isinstance(rewriter_mod, IRModule):
rewriter_mod = tvm.script.ir_module(rewriter_mod)
return PatternMatchingRewriter.from_module(rewriter_mod)
############################# BindingBlock ##############################
def dataflow() -> frame.BindingBlockFrame:
"""Start a dataflow binding block frame.
Returns
-------
frame: frame.BindingBlockFrame
The created ir_builder Block frame.
"""
return _ffi_api.Dataflow() # type: ignore[attr-defined] # pylint: disable=no-member
def output(*vars: tuple[Var]) -> None:
"""Expose the dataflow block output variables as global ones.
Parameters
----------
vars: Tuple[Var]
The output variables of a dataflow block.
"""
return _ffi_api.DataflowBlockOutput(vars) # type: ignore[attr-defined] # pylint: disable=no-member
################################## Ops #################################
def call_packed(
func: py_str,
*args: Expr,
ty_args: Type | list[Type] | None = None,
**kwargs: Any,
) -> Call:
"""Create a relax Call, which calls a packed function.
Parameters
----------
func: str
The name of extern function.
*args : Expr
The arguments.
ty_args: Optional[Union[Type, List[Type]]]
The list of type information arguments.
kwargs: Expr
The keyword arguments.
Returns
-------
call: Call
The created Relax Call
"""
op = ExternFunc(func)
args = py_tuple(convert_to_expr(a) for a in args)
if ty_args is None:
ty_args = []
if isinstance(ty_args, py_tuple): # type: ignore
ty_args = list(ty_args)
elif not isinstance(ty_args, list):
ty_args = [ty_args]
ty_args = [
(ty() if callable(ty) else ty.asobject() if isinstance(ty, ObjectConvertible) else ty)
for ty in ty_args
]
is_default = False
if "attrs_type_key" in kwargs:
attrs_type_key = kwargs["attrs_type_key"]
kwargs.pop("attrs_type_key")
else:
attrs_type_key = "ir.DictAttrs"
is_default = True
attrs = None
if kwargs or not is_default:
attrs = tvm.ir.attrs.make_node(attrs_type_key, **kwargs)
return Call(op, args, attrs=attrs, ty_args=ty_args)
def call_py_func(
py_func_name: py_str,
*args: Expr,
out_ty: Type | list[Type],
) -> Call:
"""Create a relax Call, which calls a Python function.
Parameters
----------
py_func_name: str
The name of the Python function to call. This should correspond to a function
in the IRModule's pyfuncs attribute.
*args : Expr
The arguments.
out_ty: Union[Type, List[Type]]
The type information of the call_py_func output.
It should be a single or a list of TensorType. Each one denotes the
type information of a returned tensor.
Returns
-------
call: Call
The created Relax Call for call_py_func operator.
"""
args = py_tuple(convert_to_expr(a) for a in args)
if isinstance(out_ty, py_tuple): # type: ignore
out_ty = list(out_ty)
elif not isinstance(out_ty, list):
out_ty = [out_ty]
out_ty = [
(ty() if callable(ty) else ty.asobject() if isinstance(ty, ObjectConvertible) else ty)
for ty in out_ty
]
# Convert string to StringImm
try:
func_name_imm = (
StringImm(py_func_name) if isinstance(py_func_name, py_str) else py_func_name
)
except (TypeError, ValueError, AttributeError):
func_name_imm = StringImm(py_func_name)
return _call_py_func(func_name_imm, args, out_ty)
def _ty_arg_wrapper(func):
"""A wrapper to convert TypeProxies to Type for builtin operators with ty_args"""
def _convert_tensor_type(args):
if isinstance(args, list | py_tuple): # type: ignore
new_args = [_convert_tensor_type(x) for x in args]
return type(args)(new_args)
if isinstance(args, dict):
return {_convert_tensor_type(k): _convert_tensor_type(v) for k, v in args.items()}
if inspect.isfunction(args):
args = args()
if isinstance(args, ObjectConvertible):
args = args.asobject()
return args
@functools.wraps(func)
def wrapped(*args, **kwargs):
return func(*_convert_tensor_type(args), **_convert_tensor_type(kwargs))
return wrapped # type: ignore
invoke_closure = _ty_arg_wrapper(invoke_closure) # pylint: disable=invalid-name
call_builtin_with_ctx = _ty_arg_wrapper(call_builtin_with_ctx) # pylint: disable=invalid-name
############################### Emits ###############################
def emit(value: Expr, annotate_ty: Type | None = None) -> Var:
"""Emit a binding to the last binding block frame.
Parameters
----------
value: Expr
The right side value of the bindings to be emitted.
annotate_ty: Optional[Type]
The optional type annotation for the emitted value.
Returns
-------
var: Var
The left side var of the emitted binding.
"""
return _ffi_api.Emit(value, annotate_ty) # type: ignore[attr-defined] # pylint: disable=no-member
def emit_te(func: Callable, *args: Any, **kwargs: Any) -> Call:
"""Emit a call node according to the te function.
This function converts arguments from relax expression to te tensor,
The callback func should return a te tensor or a list of te tensors.
Parameters
----------
func : Callable
A function that returns a te tensor or a list of te tensors.
args : Any, optional
arguments passed to the function.
kwargs : Any, optional
The keyword arguments passed to the function.
Note that the following keyword args are reserved:
- 'primfunc_name_hint' for passing name hint to the PrimFunc
that gets generated.
- 'primfunc_attrs' is reserved for passing func attributes to
be added to the PrimFunc that gets created.
Returns
-------
call : Call
A newly created call that calls into a tirx function.
"""
primfunc_name_hint = kwargs.pop("primfunc_name_hint", None)
tir_func, call_args, out_ty, tir_vars = gen_call_tir_inputs(func, *args, **kwargs)
if not primfunc_name_hint:
primfunc_name_hint = func.__name__
gvar = decl_function(primfunc_name_hint, tir_func) # type: ignore
return call_tir(gvar, call_args, out_ty, tir_vars)
def emit_match_cast(value: Expr, ty: Type) -> Var:
"""Emit a match_cast binding to the last binding block frame.
Parameters
----------
value: Expr
The value of the MatchCast to be emitted.
ty: Type
The ty of the MatchCast to be emitted.
Returns
-------
var: Var
The left side var of the emitted binding.
"""
return _ffi_api.EmitMatchCast(value, ty) # type: ignore
def emit_var_binding(value: VarBinding) -> Var:
"""Emit a binding to the last binding block frame.
Parameters
----------
value: VarBinding
The binding to be emitted.
Returns
-------
var: Var
The left side var of the emitted binding.
"""
return _ffi_api.EmitVarBinding(value) # type: ignore
def emit_with_type(
op: str,
args: Expr,
ty_args: Type | list[Type] | None = None,
) -> Call:
"""Create a Relax Call with type arguments.
Parameters
----------
op: Expr
The relax op for which type args are to be appended
args : Expr
The arguments.
ty_args: Optional[Union[Type, List[Type]]]
The list of type arguments.
Returns
-------
call: Call
The created Relax Call
"""
builtin_call = tvm.ir.Op.get(op)
return Call(builtin_call, args, attrs=None, ty_args=ty_args)
def emit_with_ty(
op: str,
args: Expr,
ty_args: Type | list[Type] | None = None,
) -> Call:
"""Backward-compatible alias for `emit_with_type`."""
return emit_with_type(op, args, ty_args)
############################### SeqExpr ###############################
def SeqExpr() -> frame.SeqExprFrame: # pylint: disable=invalid-name
"""Create a SeqExpr frame.
Returns
-------
res : frame.SeqExprFrame
The result SeqExprFrame
"""
return _ffi_api.SeqExpr() # type: ignore[attr-defined] # pylint: disable=no-member
############################# If Then Else #############################
def If(condition: Expr) -> frame.IfFrame: # pylint: disable=invalid-name
"""Create an if frame.
Parameters
----------
condition : Expr
The condition of if statement, executes the true branch if the
condition is true, otherwise jump into the false branch.
Returns
-------
res : frame.IfFrame
The result IfFrame.
"""
if not isinstance(condition, Expr):
condition = relax.prim_value(condition)
return _ffi_api.If(condition) # type: ignore[attr-defined] # pylint: disable=no-member
def Then() -> frame.ThenFrame: # pylint: disable=invalid-name
"""Create a then frame.
Returns
-------
res : frame.ThenFrame
The result ThenFrame.
"""
return _ffi_api.Then() # type: ignore[attr-defined] # pylint: disable=no-member
def Else() -> frame.ElseFrame: # pylint: disable=invalid-name
"""Create an else frame.
Returns
-------
res : frame.ElseFrame
The result ElseFrame.
"""
return _ffi_api.Else() # type: ignore[attr-defined] # pylint: disable=no-member
############################### R.tuple ################################
def tuple(*fields: Expr) -> Expr:
"""Create a tuple expression.
Parameters
----------
*fields : Expr
The fields of the tuple.
Returns
-------
res : Expr
The result tuple.
"""
if len(fields) == 0:
fields = py_tuple()
return relax.Tuple(fields) # type: ignore[attr-defined] # pylint: disable=no-member
############################### R.shape ################################
def shape(value: list[Expr]) -> Expr:
"""Create a ShapeExpr.
Parameters
----------
value : List[Expr]
The fields of the tuple.
Returns
-------
res : Expr
The result tuple.
"""
return relax.ShapeExpr(value) # pylint: disable=no-member # type: ignore
############################### Expr ###############################
def prim_value(value: Expr | int | float) -> Expr:
"""Convert a value to a primitive expression.
Parameters
----------
value : Expr | int | float
The value to convert.
Returns
-------
res : Expr
The primitive expression.
"""
return relax.prim_value(value) # type: ignore[attr-defined] # pylint: disable=no-member
def str(value: py_str) -> Expr:
"""Create a string imm expression.
Parameters
----------
value : str
The value of the str.
Returns
-------
res : Expr
The result str.
"""
return relax.StringImm(value) # type: ignore[attr-defined] # pylint: disable=no-member
def dtype(value: py_str | DataType) -> Expr:
"""Create a dtype imm expression.
Parameters
----------
value : dtype
The value of the dtype.
Returns
-------
res : Expr
The result dtype.
"""
return relax.DataTypeImm(value) # type: ignore[attr-defined] # pylint: disable=no-member
############################### Importer ###############################
__all__ = [
"Else",
"ExternFunc",
"If",
"SeqExpr",
"ShapeExpr",
"Then",
"TupleGetItem",
"abs",
"acos",
"acosh",
"add",
"arange",
"arg",
"argmax",
"argmin",
"argsort",
"asin",
"asinh",
"assert_op",
"astype",
"atan",
"atan2",
"atanh",
"bitwise_and",
"bitwise_not",
"bitwise_or",
"bitwise_xor",
"broadcast_to",
"bucketize",
"builtin",
"call_builtin_with_ctx",
"call_dps_packed",
"call_inplace_packed",
"call_packed",
"call_pure_packed",
"call_py_func",
"call_tir",
"call_tir_inplace",
"call_tir_with_grad",
"ccl",
"ceil",
"clip",
"collapse_sum_like",
"collapse_sum_to",
"concat",
"const",
"cos",
"cosh",
"cpu",
"cuda",
"cumprod",
"cumsum",
"dataflow",
"dequantize",
"device",
"divide",
"dtype",
"dynamic_strided_slice",
"einsum",
"emit",
"emit_match_cast",
"emit_te",
"emit_var_binding",
"emit_with_ty",
"emit_with_type",
"equal",
"erf",
"ewise_fma",
"exp",
"expand_dims",
"ext_dev",
"eye",
"eye_like",
"flatten",
"flip",
"floor",
"floor_divide",
"floor_mod",
"full",
"full_like",
"func_attr",
"func_name",
"func_ret_ty",
"func_ret_type",
"func_ret_value",
"function",
"gather_elements",
"gather_nd",
"grad",
"greater",
"greater_equal",
"hamming_window",
"hexagon",
"hint_on_device",
"image",
"index_put",
"index_tensor",
"invoke_closure",
"invoke_pure_closure",
"isfinite",
"isinf",
"isnan",
"layout_transform",
"left_shift",
"less",
"less_equal",
"linear",
"log",
"log_add_exp",
"logical_and",
"logical_not",
"logical_or",
"logical_xor",
"make_closure",
"matmul",
"max",
"maximum",
"mean",
"median",
"memory",
"meshgrid",
"metal",
"min",
"minimum",
"mod",
"multinomial_from_uniform",
"multiply",
"negative",
"nn",
"nonzero",
"not_equal",
"null_value",
"one_hot",
"ones",
"ones_like",
"opencl",
"outer",
"output",
"permute_dims",
"power",
"prim_value",
"print",
"prod",
"quantize",
"repeat",
"reshape",
"reverse_sequence",
"rewriter",
"right_shift",
"rocm",
"round",
"rsqrt",
"scatter_elements",
"scatter_nd",
"shape",
"shape_of",
"shape_to_tensor",
"sigmoid",
"sign",
"sin",
"sinh",
"size",
"slice_scatter",
"sort",
"split",
"sqrt",
"square",
"squeeze",
"stack",
"std",
"stop_lift_params",
"str",
"str",
"strided_slice",
"subtract",
"sum",
"take",
"tan",
"tanh",
"tensor_to_shape",
"tile",
"to_vdevice",
"topk",
"tril",
"triu",
"trunc",
"tuple",
"unique",
"variance",
"vision",
"vm",
"vpi",
"vulkan",
"webgpu",
"where",
"wrap_param",
"zeros",
"zeros_like",
]