197 lines
5.5 KiB
Python
197 lines
5.5 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
import torch
|
|
from cutlass import (
|
|
BFloat16,
|
|
Float8E4M3FN,
|
|
Float16,
|
|
Float32,
|
|
Int32,
|
|
Int64,
|
|
Uint32,
|
|
cute,
|
|
)
|
|
from cutlass._mlir import ir
|
|
from cutlass._mlir.dialects import llvm, vector
|
|
from cutlass.cute.nvgpu import cpasync
|
|
from cutlass.cutlass_dsl import T, dsl_user_op
|
|
|
|
_TORCH_TO_CUTE_DTYPE = {
|
|
torch.bfloat16: BFloat16,
|
|
torch.float8_e4m3fn: Float8E4M3FN,
|
|
torch.float32: Float32,
|
|
}
|
|
|
|
_CUTE_TO_PTX_DTYPE = {
|
|
BFloat16: "bf16",
|
|
Float16: "f16",
|
|
Float8E4M3FN: "e4m3",
|
|
Float32: "f32",
|
|
}
|
|
|
|
# https://github.com/NVIDIA/cutlass/blob/v4.3.2/include/cute/arch/copy_sm90_desc.hpp#L193-L197
|
|
EVICT_NORMAL = Int64(0x1000000000000000)
|
|
EVICT_FIRST = Int64(0x12F0000000000000)
|
|
EVICT_LAST = Int64(0x14F0000000000000)
|
|
|
|
|
|
@dsl_user_op
|
|
def recast_val(x, dtype, *, loc=None, ip=None):
|
|
return dtype(llvm.bitcast(dtype.mlir_type, x.ir_value(loc=loc, ip=ip)))
|
|
|
|
|
|
@dsl_user_op
|
|
def to_cta0_smem(ptr: cute.Pointer, *, loc=None, ip=None):
|
|
return cute.make_ptr(
|
|
ptr.dtype,
|
|
ptr.toint(loc=loc, ip=ip) & 0xFEFF_FFFF,
|
|
cute.AddressSpace.smem,
|
|
assumed_align=8,
|
|
loc=loc,
|
|
ip=ip,
|
|
)
|
|
|
|
|
|
def simple_tma_copy(atom, src, dst, mbar=None, cache_policy=None):
|
|
"""A simple helper that wraps group_modes() and tma_partition()
|
|
NOTE: this should be called WITHOUT cute.elect_one()
|
|
"""
|
|
if isinstance(atom.op, cpasync.CopyBulkTensorTileG2SOp):
|
|
gmem = src
|
|
smem = dst
|
|
elif isinstance(atom.op, cpasync.CopyBulkTensorTileS2GOp):
|
|
smem = src
|
|
gmem = dst
|
|
else:
|
|
raise ValueError
|
|
|
|
s_part, g_part = cpasync.tma_partition(
|
|
atom,
|
|
0,
|
|
cute.make_layout(1),
|
|
cute.group_modes(smem, 0),
|
|
cute.group_modes(gmem, 0),
|
|
)
|
|
|
|
if isinstance(atom.op, cpasync.CopyBulkTensorTileG2SOp):
|
|
cute.copy(atom, g_part, s_part, tma_bar_ptr=mbar, cache_policy=cache_policy)
|
|
elif isinstance(atom.op, cpasync.CopyBulkTensorTileS2GOp):
|
|
cute.copy(atom, s_part, g_part, cache_policy=cache_policy)
|
|
else:
|
|
raise ValueError
|
|
|
|
|
|
# can't find the equivalent in nvvm
|
|
@dsl_user_op
|
|
def fence_before_tma_store(*, loc=None, ip=None):
|
|
llvm.inline_asm(
|
|
T.i32(),
|
|
[],
|
|
"mov.u32 $0, 0;\n\t"
|
|
"fence.proxy.async::generic.release.sync_restrict::shared::cta.cluster;",
|
|
"=r",
|
|
has_side_effects=True,
|
|
is_align_stack=False,
|
|
loc=loc,
|
|
ip=ip,
|
|
)
|
|
|
|
|
|
@dsl_user_op
|
|
def mma_sync(a, b, c: cute.Tensor, *, loc=None, ip=None):
|
|
a_ty = _CUTE_TO_PTX_DTYPE[a.element_type]
|
|
b_ty = _CUTE_TO_PTX_DTYPE[b.element_type]
|
|
c_ty = _CUTE_TO_PTX_DTYPE[c.element_type]
|
|
mlir_ty = c.element_type.mlir_type
|
|
K = 256 // a.element_type.width # 32B
|
|
|
|
# Recast expects tensor-backed fragments; materialize SSA fragments here so
|
|
# callsites can pass converted FP8 fragments directly.
|
|
if isinstance(a, cute.TensorSSA):
|
|
a_ = cute.make_rmem_tensor_like(a)
|
|
a_.store(a, loc=loc, ip=ip)
|
|
a = a_
|
|
if isinstance(b, cute.TensorSSA):
|
|
b_ = cute.make_rmem_tensor_like(b)
|
|
b_.store(b, loc=loc, ip=ip)
|
|
b = b_
|
|
|
|
a = cute.recast_tensor(a, Int32, loc=loc, ip=ip)
|
|
b = cute.recast_tensor(b, Int32, loc=loc, ip=ip)
|
|
out = llvm.inline_asm(
|
|
llvm.StructType.get_literal([mlir_ty] * 4),
|
|
[a[i].ir_value(loc=loc, ip=ip) for i in range(4)]
|
|
+ [b[i].ir_value(loc=loc, ip=ip) for i in range(2)]
|
|
+ [c[i].ir_value(loc=loc, ip=ip) for i in range(4)],
|
|
f"mma.sync.aligned.m16n8k{K}.row.col.{c_ty}.{a_ty}.{b_ty}.{c_ty} "
|
|
"{$0, $1, $2, $3}, "
|
|
"{$4, $5, $6, $7}, "
|
|
"{$8, $9}, "
|
|
"{$10, $11, $12, $13};",
|
|
"=f,=f,=f,=f,r,r,r,r,r,r,f,f,f,f",
|
|
has_side_effects=False,
|
|
is_align_stack=False,
|
|
loc=loc,
|
|
ip=ip,
|
|
)
|
|
vec = vector.from_elements(
|
|
ir.VectorType.get([4], mlir_ty, loc=loc),
|
|
[llvm.extractvalue(mlir_ty, out, [i], loc=loc, ip=ip) for i in range(4)],
|
|
loc=loc,
|
|
ip=ip,
|
|
)
|
|
return cute.TensorSSA(vec, 4, c.element_type)
|
|
|
|
|
|
def _bf16x2_unary(asm: str, a: Uint32, *, loc=None, ip=None) -> Uint32:
|
|
out = llvm.inline_asm(
|
|
T.i32(),
|
|
[a.ir_value(loc=loc, ip=ip)],
|
|
f"{asm}.bf16x2 $0, $1;",
|
|
"=r,r",
|
|
has_side_effects=False,
|
|
is_align_stack=False,
|
|
loc=loc,
|
|
ip=ip,
|
|
)
|
|
return Uint32(out)
|
|
|
|
|
|
def _bf16x2_binary(asm: str, a: Uint32, b: Uint32, *, loc=None, ip=None) -> Uint32:
|
|
out = llvm.inline_asm(
|
|
T.i32(),
|
|
[a.ir_value(loc=loc, ip=ip), b.ir_value(loc=loc, ip=ip)],
|
|
f"{asm}.bf16x2 $0, $1, $2;",
|
|
"=r,r,r",
|
|
has_side_effects=False,
|
|
is_align_stack=False,
|
|
loc=loc,
|
|
ip=ip,
|
|
)
|
|
return Uint32(out)
|
|
|
|
|
|
@dsl_user_op
|
|
def _bf16x2_abs(a: Uint32, *, loc=None, ip=None) -> Uint32:
|
|
return _bf16x2_unary("abs", a, loc=loc, ip=ip)
|
|
|
|
|
|
@dsl_user_op
|
|
def _bf16x2_neg(a: Uint32, *, loc=None, ip=None) -> Uint32:
|
|
return _bf16x2_unary("neg", a, loc=loc, ip=ip)
|
|
|
|
|
|
@dsl_user_op
|
|
def _bf16x2_max(a: Uint32, b: Uint32, *, loc=None, ip=None) -> Uint32:
|
|
return _bf16x2_binary("max", a, b, loc=loc, ip=ip)
|
|
|
|
|
|
@dsl_user_op
|
|
def _bf16x2_mul(a: Uint32, b: Uint32, *, loc=None, ip=None) -> Uint32:
|
|
return _bf16x2_binary("mul.rn", a, b, loc=loc, ip=ip)
|
|
|
|
|
|
@dsl_user_op
|
|
def _bf16x2_sub(a: Uint32, b: Uint32, *, loc=None, ip=None) -> Uint32:
|
|
return _bf16x2_binary("sub.rn", a, b, loc=loc, ip=ip)
|