422 lines
14 KiB
Python
422 lines
14 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.
|
|
"""Common expressions data structures in the IR."""
|
|
|
|
from numbers import Number
|
|
|
|
import tvm_ffi
|
|
|
|
import tvm
|
|
|
|
from ..runtime import Object, Scriptable
|
|
from . import _ffi_api, _overload_prim_expr, _tensor_expr_overload
|
|
from .base import Node, Span
|
|
|
|
|
|
@tvm_ffi.register_object("ir.Expr")
|
|
class Expr(Node):
|
|
"""Base class of all the expressions."""
|
|
|
|
span: Span | None
|
|
ty: "tvm.ir.Type"
|
|
|
|
|
|
def is_prim_expr(value: object) -> bool:
|
|
"""Return whether an expression has a primitive result type."""
|
|
return isinstance(value, Expr) and isinstance(value.ty, tvm.ir.PrimType)
|
|
|
|
|
|
@tvm_ffi.register_object("ir.GlobalVar")
|
|
class GlobalVar(Expr):
|
|
"""A global variable in the IR.
|
|
|
|
GlobalVar is used to refer to the global functions
|
|
stored in the IRModule.
|
|
|
|
Parameters
|
|
----------
|
|
name_hint: str
|
|
The name of the variable.
|
|
"""
|
|
|
|
name_hint: str
|
|
|
|
def __init__(self, name_hint: str):
|
|
self.__init_handle_by_constructor__(_ffi_api.GlobalVar, name_hint)
|
|
|
|
def __call__(self, *args: Expr) -> Expr:
|
|
"""Call the global variable.
|
|
|
|
Parameters
|
|
----------
|
|
args: List[Expr]
|
|
The arguments to the call.
|
|
|
|
Returns
|
|
-------
|
|
call: Expr
|
|
A call taking the variable as a function.
|
|
"""
|
|
from .type import PointerType
|
|
|
|
def is_tir_arg(x):
|
|
return (
|
|
isinstance(x, Number)
|
|
or is_prim_expr(x)
|
|
or (isinstance(x, Expr) and isinstance(x.ty, PointerType))
|
|
)
|
|
|
|
if args and all(is_tir_arg(x) for x in args):
|
|
return tvm.tirx.call_tir(self, *args)
|
|
|
|
if all(isinstance(x, Expr) for x in args):
|
|
return Call(self, args)
|
|
|
|
arg_types = [type(x) for x in args]
|
|
raise RuntimeError(f"Do not know how to handle GlobalVar.__call__ for types {arg_types}")
|
|
|
|
|
|
@tvm_ffi.register_object("ir.Call")
|
|
class Call(Expr, Scriptable):
|
|
"""Core function call node."""
|
|
|
|
__hash__ = Expr.__hash__
|
|
|
|
op: Expr
|
|
args: list[Expr]
|
|
attrs: "tvm.ir.Attrs | None"
|
|
ty_args: list["tvm.ir.Type"]
|
|
span: Span | None
|
|
|
|
def __init__(
|
|
self,
|
|
op: Expr | str,
|
|
args: list[Expr] | tuple[Expr, ...],
|
|
attrs: "tvm.ir.Attrs | dict | None" = None,
|
|
ty_args: list["tvm.ir.Type"] | tuple["tvm.ir.Type", ...] | None = None,
|
|
span: Span | None = None,
|
|
ret_ty: "tvm.ir.Type | str | None" = None,
|
|
) -> None:
|
|
# pylint: disable=import-outside-toplevel
|
|
from .attrs import DictAttrs
|
|
from .op import Op
|
|
from .type import PointerType, PrimType, Type
|
|
|
|
if isinstance(op, str):
|
|
op = Op.get(op)
|
|
if attrs is not None and isinstance(attrs, dict):
|
|
attrs = DictAttrs(attrs)
|
|
if ret_ty is None:
|
|
ret_ty = Type.missing()
|
|
if isinstance(ret_ty, str) and ret_ty == "handle":
|
|
ret_ty = PointerType(PrimType("void"))
|
|
elif ret_ty is not None and not isinstance(ret_ty, Type):
|
|
ret_ty = PrimType(ret_ty)
|
|
if ty_args is None:
|
|
ty_args = []
|
|
self.__init_handle_by_constructor__(_ffi_api.Call, ret_ty, op, args, attrs, ty_args, span)
|
|
|
|
def expr_ty(self):
|
|
"""Return this expression's primitive result type."""
|
|
if is_prim_expr(self):
|
|
return self.ty
|
|
raise TypeError(f"Expected primitive-valued Call, but result type is {self.ty}")
|
|
|
|
def __add__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__add__(self, other)
|
|
return _tensor_expr_overload.__add__(self, other)
|
|
|
|
def __radd__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__radd__(self, other)
|
|
return _tensor_expr_overload.__radd__(self, other)
|
|
|
|
def __sub__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__sub__(self, other)
|
|
return _tensor_expr_overload.__sub__(self, other)
|
|
|
|
def __rsub__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__rsub__(self, other)
|
|
return _tensor_expr_overload.__rsub__(self, other)
|
|
|
|
def __mul__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__mul__(self, other)
|
|
return _tensor_expr_overload.__mul__(self, other)
|
|
|
|
def __rmul__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__rmul__(self, other)
|
|
return _tensor_expr_overload.__rmul__(self, other)
|
|
|
|
def __div__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__div__(self, other)
|
|
return _tensor_expr_overload.__div__(self, other)
|
|
|
|
def __rdiv__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__rdiv__(self, other)
|
|
return _tensor_expr_overload.__rdiv__(self, other)
|
|
|
|
def __truediv__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__truediv__(self, other)
|
|
return _tensor_expr_overload.__truediv__(self, other)
|
|
|
|
def __rtruediv__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__rtruediv__(self, other)
|
|
return _tensor_expr_overload.__rtruediv__(self, other)
|
|
|
|
def __floordiv__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__floordiv__(self, other)
|
|
return _tensor_expr_overload.__floordiv__(self, other)
|
|
|
|
def __rfloordiv__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__rfloordiv__(self, other)
|
|
return _tensor_expr_overload.__rfloordiv__(self, other)
|
|
|
|
def __mod__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__mod__(self, other)
|
|
return _tensor_expr_overload.__mod__(self, other)
|
|
|
|
def __rmod__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__rmod__(self, other)
|
|
return _tensor_expr_overload.__rmod__(self, other)
|
|
|
|
def __pow__(self, other):
|
|
if is_prim_expr(self):
|
|
return NotImplemented
|
|
return _tensor_expr_overload.__pow__(self, other)
|
|
|
|
def __rpow__(self, other):
|
|
if is_prim_expr(self):
|
|
return NotImplemented
|
|
return _tensor_expr_overload.__rpow__(self, other)
|
|
|
|
def __neg__(self):
|
|
if is_prim_expr(self):
|
|
result = _overload_prim_expr.__neg__(self)
|
|
if result is NotImplemented:
|
|
raise TypeError("Primitive expression overload __neg__ is not registered")
|
|
return result
|
|
result = _tensor_expr_overload.__neg__(self)
|
|
if result is NotImplemented:
|
|
raise TypeError("Tensor expression overload negative is not registered")
|
|
return result
|
|
|
|
def __lshift__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__lshift__(self, other)
|
|
return NotImplemented
|
|
|
|
def __rlshift__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__rlshift__(self, other)
|
|
return NotImplemented
|
|
|
|
def __rshift__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__rshift__(self, other)
|
|
return NotImplemented
|
|
|
|
def __rrshift__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__rrshift__(self, other)
|
|
return NotImplemented
|
|
|
|
def __and__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__and__(self, other)
|
|
return NotImplemented
|
|
|
|
def __rand__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__rand__(self, other)
|
|
return NotImplemented
|
|
|
|
def __or__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__or__(self, other)
|
|
return NotImplemented
|
|
|
|
def __ror__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__ror__(self, other)
|
|
return NotImplemented
|
|
|
|
def __xor__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__xor__(self, other)
|
|
return NotImplemented
|
|
|
|
def __rxor__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__rxor__(self, other)
|
|
return NotImplemented
|
|
|
|
def __invert__(self):
|
|
if is_prim_expr(self):
|
|
result = _overload_prim_expr.__invert__(self)
|
|
if result is NotImplemented:
|
|
raise TypeError("Primitive expression overload __invert__ is not registered")
|
|
return result
|
|
return NotImplemented
|
|
|
|
def __lt__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__lt__(self, other)
|
|
return _tensor_expr_overload.__lt__(self, other)
|
|
|
|
def __le__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__le__(self, other)
|
|
return _tensor_expr_overload.__le__(self, other)
|
|
|
|
def __eq__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__eq__(self, other)
|
|
return Object.__eq__(self, other)
|
|
|
|
def __ne__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__ne__(self, other)
|
|
return Object.__ne__(self, other)
|
|
|
|
def __gt__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__gt__(self, other)
|
|
return _tensor_expr_overload.__gt__(self, other)
|
|
|
|
def __ge__(self, other):
|
|
if is_prim_expr(self):
|
|
return _overload_prim_expr.__ge__(self, other)
|
|
return _tensor_expr_overload.__ge__(self, other)
|
|
|
|
def __nonzero__(self):
|
|
raise ValueError(
|
|
"Cannot use and / or / not operator to Expr, hint: use tvm.tirx.all / "
|
|
"tvm.tirx.any, if it is None checking, use node is not None"
|
|
)
|
|
|
|
def __bool__(self):
|
|
return self.__nonzero__()
|
|
|
|
def equal(self, other, span=None):
|
|
result = _overload_prim_expr.equal(self, other, span)
|
|
if result is NotImplemented:
|
|
raise TypeError("Primitive expression overload equal is not registered")
|
|
return result
|
|
|
|
def astype(self, dtype, span=None):
|
|
if is_prim_expr(self):
|
|
result = _overload_prim_expr.astype(self, dtype, span)
|
|
if result is NotImplemented:
|
|
raise TypeError("Primitive expression overload astype is not registered")
|
|
return result
|
|
result = _tensor_expr_overload.astype(self, dtype, span)
|
|
if result is NotImplemented:
|
|
raise TypeError("Tensor expression overload astype is not registered")
|
|
return result
|
|
|
|
def __call__(self, *args, attrs=None):
|
|
if is_prim_expr(self):
|
|
raise TypeError("A primitive-valued Call cannot be called")
|
|
result = _tensor_expr_overload.__call__(self, *args, attrs=attrs)
|
|
if result is NotImplemented:
|
|
raise TypeError("Tensor expression overload __call__ is not registered")
|
|
return result
|
|
|
|
def __getitem__(self, index):
|
|
if is_prim_expr(self):
|
|
raise TypeError("A primitive-valued Call cannot be indexed")
|
|
result = _tensor_expr_overload.__getitem__(self, index)
|
|
if result is NotImplemented:
|
|
raise TypeError("Tensor expression overload __getitem__ is not registered")
|
|
return result
|
|
|
|
|
|
@tvm_ffi.register_object("ir.Range")
|
|
class Range(Node, Scriptable):
|
|
"""Represent a range in TVM.
|
|
|
|
You do not need to create a Range explicitly.
|
|
Python lists and tuples will be converted automatically to a Range in API functions.
|
|
|
|
Parameters
|
|
----------
|
|
begin : Expr
|
|
The begin value of the range when end is None.
|
|
Otherwise it is the length of the range.
|
|
|
|
end : Optional[Expr]
|
|
The end value of the range.
|
|
|
|
span : Optional[Span]
|
|
The location of this node in the source code.
|
|
|
|
Note
|
|
----
|
|
The constructor creates the range `[begin, end)`
|
|
if the end argument is not None. Otherwise, it creates `[0, begin)`.
|
|
"""
|
|
|
|
min: Expr
|
|
extent: Expr
|
|
span: Span | None
|
|
|
|
def __init__(self, begin: Expr, end: Expr | None = None, span: Span | None = None) -> None:
|
|
self.__init_handle_by_constructor__(_ffi_api.Range, begin, end, span)
|
|
|
|
@staticmethod
|
|
def from_min_extent(min_value: Expr, extent: Expr, span: Span | None = None) -> "Range":
|
|
"""Construct a Range by min and extent.
|
|
|
|
This constructs a range in [min_value, min_value + extent)
|
|
|
|
Parameters
|
|
----------
|
|
min_value : Expr
|
|
The minimum value of the range.
|
|
|
|
extent : Expr
|
|
The extent of the range.
|
|
|
|
span : Optional[Span]
|
|
The location of this node in the source code.
|
|
|
|
Returns
|
|
-------
|
|
rng : Range
|
|
The constructed range.
|
|
"""
|
|
return _ffi_api.Range_from_min_extent(min_value, extent, span)
|
|
|
|
def __eq__(self, other: Object) -> bool:
|
|
return tvm_ffi.structural_equal(self, other)
|
|
|
|
def __ne__(self, other: Object) -> bool:
|
|
return not self.__eq__(other)
|