Files
apache--tvm/python/tvm/tirx/operator/tile_primitive/ops.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

568 lines
15 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.
"""Implementation of TIR operator."""
from tvm.ir import Op
from tvm.tirx import Expr
from tvm.tirx.stmt import TilePrimitiveCall
def get_tirx_op(op_name: str):
assert isinstance(op_name, str)
return Op.get("tirx.tile." + op_name)
class ArgProperty:
def __init__(self, index):
self.index = index
def __get__(self, obj, objtype=None):
assert obj is not None, "TilePrimitiveCall cannot be None"
return obj.args[self.index]
### Base Operator Classes ###
class UnaryOp(TilePrimitiveCall):
"""Base class for unary operators: unary(output, input).
Unary operators take a single input tensor and produce a single output tensor.
"""
scalar_input = False
output = ArgProperty(0)
input = ArgProperty(1)
@property
def srcs(self) -> list[Expr]:
"""Get the source expression (input) of the operator."""
return [self.input]
@property
def dsts(self) -> list[Expr]:
"""Get the destination expression (output) of the operator."""
return [self.output]
class UnaryOpWithBiasScale(UnaryOp):
"""Extended unary operator with bias and scale parameters: unary_with_bias_scale(output, input, bias, scale).
These operators support additional bias and scale parameters for more complex operations (only on trn).
output = unary(input * scale + bias)
""" # noqa: E501
bias = ArgProperty(2)
scale = ArgProperty(3)
@property
def srcs(self) -> list[Expr]:
"""Get the source expressions (inputs) of the operator."""
return [self.input, self.bias, self.scale]
class BinaryOp(TilePrimitiveCall):
"""Base class for binary operators: binary(output, input0, input1).
Binary operators take two input tensors and produce a single output tensor.
"""
lhs = ArgProperty(1)
rhs = ArgProperty(2)
output = ArgProperty(0)
@property
def srcs(self) -> list[Expr]:
"""Get the source expressions (inputs) of the operator."""
return [self.lhs, self.rhs]
@property
def dsts(self) -> list[Expr]:
"""Get the destination expression (output) of the operator."""
return [self.output]
class ReduceOp(TilePrimitiveCall):
"""Base class for reduction operators: reduce(output, input, reduce_axes, accum).
Reduction operators reduce one or more dimensions of the input tensor.
"""
input = ArgProperty(1)
output = ArgProperty(0)
reduce_axes = ArgProperty(2)
accum = ArgProperty(3)
@property
def srcs(self) -> list[Expr]:
"""Get the source expression (input) of the operator."""
return [self.input]
@property
def dsts(self) -> list[Expr]:
"""Get the destination expression (output) of the operator."""
return [self.output]
### Schedule Operators ###
class Zero(UnaryOp):
"""Zero out all elements in src and store to dst."""
op = get_tirx_op("zero")
class Sqrt(UnaryOpWithBiasScale):
"""Compute square root of all elements in src and store to dst.
If bias and scale are provided: dst = sqrt(src * scale + bias)
"""
op = get_tirx_op("sqrt")
class Fill(UnaryOp):
"""Fill dst with a scalar value."""
op = get_tirx_op("fill")
scalar_input = True
class Add(BinaryOp):
"""Add src1 and src2 element-wise and store to dst."""
op = get_tirx_op("add")
class Sub(BinaryOp):
"""Subtract src2 from src1 element-wise and store to dst."""
op = get_tirx_op("sub")
class Mul(BinaryOp):
"""Multiply src1 and src2 element-wise and store to dst."""
op = get_tirx_op("mul")
class FDiv(BinaryOp):
"""Divide src1 by src2 element-wise using floating point division and store to dst."""
op = get_tirx_op("fdiv")
class FMA(TilePrimitiveCall):
"""Fused multiply-add: output = input * scale + bias.
fma(output, input, scale, bias)
scale and bias can each be either a BufferRegion or a Expr scalar.
"""
op = get_tirx_op("fma")
output = ArgProperty(0)
input = ArgProperty(1)
scale = ArgProperty(2)
bias = ArgProperty(3)
@property
def srcs(self) -> list[Expr]:
"""Get the source expressions (inputs) of the operator."""
return [self.input, self.scale, self.bias]
@property
def dsts(self) -> list[Expr]:
"""Get the destination expression (output) of the operator."""
return [self.output]
class Cast(UnaryOp):
"""Cast src to dst."""
op = get_tirx_op("cast")
class Copy(TilePrimitiveCall):
"""Copy all elements from src to dst.
Args:
dst: Destination buffer region
src: Source buffer region
"""
op = get_tirx_op("copy")
dst = ArgProperty(0)
src = ArgProperty(1)
@property
def srcs(self) -> list[Expr]:
"""Get the source expressions (inputs) of the operator."""
return [self.src]
@property
def dsts(self) -> list[Expr]:
"""Get the destination expressions (outputs) of the operator."""
return [self.dst]
class CopyAsync(TilePrimitiveCall):
"""Copy all elements from src to dst asynchronously.
Args:
dst: Destination buffer region
src: Source buffer region
"""
op = get_tirx_op("copy_async")
dst = ArgProperty(0)
src = ArgProperty(1)
@property
def srcs(self) -> list[Expr]:
"""Get the source expressions (inputs) of the operator."""
return [self.src]
@property
def dsts(self) -> list[Expr]:
"""Get the destination expressions (outputs) of the operator."""
return [self.dst]
class Gemm(TilePrimitiveCall):
"""General matrix multiplication: D = A * B * alpha + C * beta.
Args:
D: Output matrix
A: First input matrix
B: Second input matrix
C: Third input matrix (for bias)
transpose_A: Whether to transpose A
transpose_B: Whether to transpose B
alpha: Scalar multiplier for A*B
beta: Scalar multiplier for C
"""
op = get_tirx_op("gemm")
output = ArgProperty(0)
lhs = ArgProperty(1)
rhs = ArgProperty(2)
bias = ArgProperty(3)
transpose_A = ArgProperty(4)
transpose_B = ArgProperty(5)
alpha = ArgProperty(6)
beta = ArgProperty(7)
@property
def srcs(self) -> list[Expr]:
"""Get the source matrices."""
return [self.lhs, self.rhs, self.bias]
@property
def dsts(self) -> list[Expr]:
"""Get the destination matrix."""
return [self.output]
class GemmAsync(TilePrimitiveCall):
"""General matrix multiplication asynchronously.
Supports two arg layouts:
- Regular (6 args): C, A, B, transA, transB, accum
- Block-scaled (8 args): C, A, B, SFA, SFB, transA, transB, accum
"""
op = get_tirx_op("gemm_async")
output = ArgProperty(0)
lhs = ArgProperty(1)
rhs = ArgProperty(2)
@property
def is_block_scaled(self) -> bool:
"""Whether this is a block-scaled MMA operation."""
return len(self.args) == 8
@property
def sfa(self):
"""Get the scale factor buffer for A (None for regular MMA)."""
return self.args[3] if self.is_block_scaled else None
@property
def sfb(self):
"""Get the scale factor buffer for B (None for regular MMA)."""
return self.args[4] if self.is_block_scaled else None
@property
def transA(self):
return self.args[5] if self.is_block_scaled else self.args[3]
@property
def transB(self):
return self.args[6] if self.is_block_scaled else self.args[4]
@property
def accum(self):
return self.args[7] if self.is_block_scaled else self.args[5]
@property
def srcs(self) -> list[Expr]:
"""Get the source matrices (including scale factors if block-scaled)."""
srcs = [self.lhs, self.rhs]
if self.is_block_scaled:
srcs.extend([self.sfa, self.sfb])
return srcs
@property
def dsts(self) -> list[Expr]:
"""Get the destination matrix."""
return [self.output]
class Sum(ReduceOp):
"""Sum elements in src along specified axes and store in dst."""
op = get_tirx_op("sum")
class Max(ReduceOp):
"""Compute maximum value in src along specified axes and store in dst."""
op = get_tirx_op("max")
class Min(ReduceOp):
"""Compute minimum value in src along specified axes and store in dst."""
op = get_tirx_op("min")
class Reciprocal(UnaryOp):
"""Compute reciprocal (1/x) for all elements in src and store to dst."""
op = get_tirx_op("reciprocal")
class SiLU(UnaryOp):
"""Compute SiLU (x * sigmoid(x)) for all elements in src and store to dst."""
op = get_tirx_op("silu")
class Memset(UnaryOp):
"""Set all elements in dst to a specified value."""
op = get_tirx_op("memset")
scalar_input = True
class Maximum(BinaryOp):
"""Compute element-wise maximum of src1 and src2 and store to dst."""
op = get_tirx_op("maximum")
class Minimum(BinaryOp):
"""Compute element-wise minimum of src1 and src2 and store to dst."""
op = get_tirx_op("minimum")
class Exp(UnaryOpWithBiasScale):
"""Compute exponential (e^x) of all elements in src and store to dst.
If bias and scale are provided: dst = exp(src * scale + bias)
"""
op = get_tirx_op("exp")
class Exp2(UnaryOpWithBiasScale):
"""Compute base-2 exponential (2^x) of all elements in src and store to dst.
If bias and scale are provided: dst = exp2(src * scale + bias)
"""
op = get_tirx_op("exp2")
class Select(BinaryOp):
"""Select elements from src1 or src2 based on the predicate.
select(dst, src1, src2, predicate)
"""
op = get_tirx_op("select")
predicate = ArgProperty(3)
### Compose Ops ###
class BinaryReduce(TilePrimitiveCall):
"""Combine a binary operation with a reduction operation.
binary_reduce(binary_output, reduce_output, binary_input1, binary_input2, binary_op, reduce_op, reduce_axes, )
""" # noqa: E501
op = get_tirx_op("binary_reduce")
binary_output = ArgProperty(0)
reduce_output = ArgProperty(1)
binary_input1 = ArgProperty(2)
binary_input2 = ArgProperty(3)
binary_op = ArgProperty(4)
reduce_op = ArgProperty(5)
reduce_axes = ArgProperty(6)
@property
def srcs(self) -> list[Expr]:
"""Get the source expressions (inputs) of the operator."""
return [self.binary_input1, self.binary_input2]
@property
def dsts(self) -> list[Expr]:
"""Get the destination expressions (outputs) of the operator."""
return [self.binary_output, self.reduce_output]
class UnaryReduce(TilePrimitiveCall):
"""Combine a unary operation with a reduction operation.
unary_reduce(unary_output, reduce_output, unary_input, unary_op, reduce_op, bias, scale, reduce_axes)
""" # noqa: E501
op = get_tirx_op("unary_reduce")
unary_output = ArgProperty(0)
reduce_output = ArgProperty(1)
unary_input = ArgProperty(2)
unary_op = ArgProperty(3)
reduce_op = ArgProperty(4)
bias = ArgProperty(5)
scale = ArgProperty(6)
reduce_axes = ArgProperty(7)
@property
def srcs(self) -> list[Expr]:
"""Get the source expressions (inputs) of the operator."""
return [self.unary_input, self.bias, self.scale]
@property
def dsts(self) -> list[Expr]:
"""Get the destination expressions (outputs) of the operator."""
return [self.unary_output, self.reduce_output]
class BinaryChain(TilePrimitiveCall):
"""Chain multiple binary operations together.
binary_chain(output, data, operand0, operand1, op0, op1, reverse1)
if not reverse1:
output = (operand0 op0 data) op1 operand1
else:
output = operand1 op1 (operand0 op0 data)
"""
op = get_tirx_op("binary_chain")
output = ArgProperty(0)
data = ArgProperty(1)
operand0 = ArgProperty(2)
operand1 = ArgProperty(3)
op0 = ArgProperty(4)
op1 = ArgProperty(5)
reverse1 = ArgProperty(6)
@property
def srcs(self) -> list[Expr]:
"""Get the source expressions (inputs) of the operator."""
return [self.data, self.operand0, self.operand1]
@property
def dsts(self) -> list[Expr]:
"""Get the destination expressions (outputs) of the operator."""
return [self.output]
class ReduceNegate(ReduceOp):
"""
Negate the result of a reduction operation.
reduce_negate(output, input, reduce_axes, accum, reduce_op)
"""
op = get_tirx_op("reduce_negate")
reduce_op = ArgProperty(4)
class ComposeOp(TilePrimitiveCall):
"""Generic operator for composition of multiple operations.
Must be lowered to specific compose operations before operator-level passes.
"""
# TODO: add a pass to lower generic compose_op to specific compose ops
op = get_tirx_op("compose_op")
@property
def srcs(self) -> list[Expr]:
"""Get the source expressions (inputs) of the operator."""
raise NotImplementedError(
"Generic compose_op must be lowered to specific compose ops before operator-level passes" # noqa: E501
)
@property
def dsts(self) -> list[Expr]:
"""Get the destination expressions (outputs) of the operator."""
raise NotImplementedError(
"Generic compose_op must be lowered to specific compose ops before operator-level passes" # noqa: E501
)
class PermuteLayout(TilePrimitiveCall):
"""Move data so the buffer's bytes are arranged under a different layout.
Logical shape is preserved; only the byte placement changes. ``dst`` and
``src`` carry their own TileLayouts; on lowering, the dispatcher reads
those layouts and emits a register-staged warp transpose, optionally
inserting a bank-conflict-avoiding XOR-swizzle on the per-lane register
slots.
Args: ``permute_layout(dst_region, src_region)``.
``dst`` and ``src`` may alias the same underlying SMEM (in-place).
"""
op = get_tirx_op("permute_layout")
@property
def dst(self) -> Expr:
return self.args[0]
@property
def src(self) -> Expr:
return self.args[1]
@property
def srcs(self) -> list[Expr]:
return [self.src]
@property
def dsts(self) -> list[Expr]:
return [self.dst]