/*************************************************************************************************** * Copyright (c) 2025 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * **************************************************************************************************/ #pragma once #include #include #include #include #include namespace cute { namespace { // (T32,V4) -> (M16,N8) using SM120_16x8_Row = Layout,Shape < _2,_2>>, Stride,Stride<_16,_8>>>; } namespace SM120::BLOCKSCALED::SPARSE { // Unpack explode/mma call with sparse and block scalaring inputs. template CUTE_HOST_DEVICE constexpr void mma_unpack(MMA_Traits const&, Tensor & D, Tensor const& A, Tensor const& B, Tensor const& C) { static_assert(is_rmem_v, "Expected registers in MMA_Atom::call"); static_assert(is_rmem_v, "Expected registers in MMA_Atom::call"); static_assert(is_rmem_v, "Expected registers in MMA_Atom::call"); static_assert(is_rmem_v, "Expected registers in MMA_Atom::call"); using DRegisters = typename MMAOp::DRegisters; using ARegisters = typename MMAOp::ARegisters; using ERegisters = typename MMAOp::ERegisters; using BRegisters = typename MMAOp::BRegisters; using CRegisters = typename MMAOp::CRegisters; using SFARegisters = typename MMAOp::SFARegisters; using SFBRegisters = typename MMAOp::SFBRegisters; // Register value types from the MMAOp register arrays using RegTypeD = typename remove_extent::type; using RegTypeA = typename remove_extent::type; using RegTypeE = typename remove_extent::type; using RegTypeB = typename remove_extent::type; using RegTypeC = typename remove_extent::type; using RegTypeSFA = typename remove_extent::type; using RegTypeSFB = typename remove_extent::type; constexpr int RegNumD = extent::value; constexpr int RegNumA = extent::value; constexpr int RegNumE = extent::value; constexpr int RegNumB = extent::value; constexpr int RegNumC = extent::value; constexpr int RegNumSFA = extent::value; constexpr int RegNumSFB = extent::value; auto [tA, tSFA, tE] = unzip_tensor(A); auto [tB, tSFB ] = unzip_tensor(B); Tensor rA = recast(tA); Tensor rE = recast(tE); Tensor rB = recast(tB); Tensor rD = recast(D); Tensor rC = recast(C); Tensor rSFA = recast(tSFA); Tensor rSFB = recast(tSFB); CUTE_STATIC_ASSERT_V(size(rA) == Int{}); CUTE_STATIC_ASSERT_V(size(rE) == Int{}); CUTE_STATIC_ASSERT_V(size(rB) == Int{}); CUTE_STATIC_ASSERT_V(size(rD) == Int{}); CUTE_STATIC_ASSERT_V(size(rC) == Int{}); CUTE_STATIC_ASSERT_V(size(filter_zeros(rSFA)) == Int{}); CUTE_STATIC_ASSERT_V(size(filter_zeros(rSFB)) == Int{}); detail::explode(MMAOp::fma, rD, make_int_sequence{}, rA, make_int_sequence{}, rB, make_int_sequence{}, rC, make_int_sequence{}, rE, make_int_sequence{}, rSFA, make_int_sequence{}, rSFB, make_int_sequence{}); } } // end namespace SM120::BLOCKSCALED::SPARSE namespace SM120::SPARSE { template CUTE_HOST_DEVICE constexpr void mma_unpack(MMA_Traits const&, Tensor & D, Tensor const& A, Tensor const& B, Tensor const& C) { static_assert(is_rmem_v, "Expected registers in MMA_Atom::call"); static_assert(is_rmem_v, "Expected registers in MMA_Atom::call"); static_assert(is_rmem_v, "Expected registers in MMA_Atom::call"); static_assert(is_rmem_v, "Expected registers in MMA_Atom::call"); using DRegisters = typename MMAOp::DRegisters; using ARegisters = typename MMAOp::ARegisters; using ERegisters = typename MMAOp::ERegisters; using BRegisters = typename MMAOp::BRegisters; using CRegisters = typename MMAOp::CRegisters; // Register value types from the MMAOp register arrays using RegTypeD = typename remove_extent::type; using RegTypeA = typename remove_extent::type; using RegTypeE = typename remove_extent::type; using RegTypeB = typename remove_extent::type; using RegTypeC = typename remove_extent::type; constexpr int RegNumD = extent::value; constexpr int RegNumA = extent::value; constexpr int RegNumE = extent::value; constexpr int RegNumB = extent::value; constexpr int RegNumC = extent::value; auto [tA, tE] = unzip_tensor(A); Tensor rA = recast(tA); Tensor rE = recast(tE); Tensor rB = recast(B); Tensor rD = recast(D); Tensor rC = recast(C); CUTE_STATIC_ASSERT_V(size(rA) == Int{}); CUTE_STATIC_ASSERT_V(size(rE) == Int{}); CUTE_STATIC_ASSERT_V(size(rB) == Int{}); CUTE_STATIC_ASSERT_V(size(rD) == Int{}); CUTE_STATIC_ASSERT_V(size(rC) == Int{}); detail::explode(MMAOp::fma, rD, make_int_sequence{}, rA, make_int_sequence{}, rB, make_int_sequence{}, rC, make_int_sequence{}, rE, make_int_sequence{}); } } // end namespace SM120::SPARSE // sparse F8F6F4 without block-scaling template struct MMA_Traits> { using ValTypeA = sparse_elem<2, a_type>; using ValTypeE = sparse_elem<8, uint8_t>; using ValTypeB = uint8_t; using FrgTypeA = sparse_elem<2, uint8_t>; using FrgTypeE = sparse_elem<8, uint8_t>; using ValTypeC = c_type; using ValTypeD = c_type; using Shape_MNK = Shape<_16, _8, _64>; using ThrID = Layout<_32>; // (T32,V32) -> (M16,K64) using ALayout = Layout,Shape < _8,_2, _2>>, Stride,Stride<_16,_8,_512>>>; // (T32,V16) -> (N8,K64) using BLayout = Layout,Shape <_4, _4>>, Stride,Stride<_8,_128>>>; // (T32,V4) -> (M16,N8) using CLayout = SM120_16x8_Row; // (T32, V32) -> (M16, K64) using ELayout = Layout, _32>, Stride,_16>>; }; // sparse MXF8F6F4 with block-scaling. template struct MMA_Traits> : MMA_Traits> { using ValTypeA = sparse_elem<2, a_type>; using ValTypeE = sparse_elem<8, uint8_t>; using ValTypeB = uint8_t; using FrgTypeA = sparse_elem<2, uint8_t>; using FrgTypeE = sparse_elem<8, uint8_t>; using ValTypeD = c_type; using ValTypeC = c_type; using ValTypeSF = sf_type; constexpr static int SFVecSize = VS; using UnderlyingSFTraits = MMA_Traits>; using SFALayout = typename UnderlyingSFTraits::SFALayout; using SFBLayout = typename UnderlyingSFTraits::SFBLayout; }; template struct MMA_Traits> { using ValTypeA = sparse_elem<4, uint8_t>; using ValTypeE = sparse_elem<16, uint8_t>; using ValTypeB = uint4_t; using FrgTypeA = sparse_elem<4, uint8_t>; using FrgTypeE = sparse_elem<16, uint8_t>; using ValTypeC = c_type; using ValTypeD = c_type; using ValTypeSF = sf_type; constexpr static int SFVecSize = VS; using Shape_MNK = Shape<_16, _8, _128>; using ThrID = Layout<_32>; // (T32,V64) -> (M16,K128) using ALayout = Layout,Shape <_16,_2, _2>>, Stride,Stride<_16,_8,_1024>>>; // (T32,V32) -> (N8,K128) using BLayout = Layout,Shape <_8, _4>>, Stride,Stride<_8,_256>>>; // (T32,V128) -> (M16,K128) using SFALayout = Layout,_128>, Stride, _16>>; // (T32,V128) -> (N8,K128) using SFBLayout = Layout,_128>, Stride, _8>>; // (T32,V4) -> (M16,N8) using CLayout = SM120_16x8_Row; // (T32, V64) -> (M16, K128) using ELayout = Layout, Shape< _64>>, Stride,Stride<_16>>>; }; namespace SM120::SPARSE { // For SM120 MMA F8F6F4 input fp4, the operand A/B are load from ld.matrix. // ld.matrix b4x16_p64 places FP4 data at the first four bits in each // eight-bit container, whereas MMA F8F6F4 expects the four-bit data to be in // the middle of the eight-bit container. Thus, e2m1 operands being fed // to MMA F8F6F4 must be shifted left by two bits. // 0b0000ABCD --> 0b00ABCD00 // NOTE: Same transformation is NOT needed for FP6 and FP8. template CUTLASS_DEVICE void fp4_shift_A(SM120_SPARSE_16x8x64_TN const&, Tensor&& tensor) { using RegisterTypeA = typename remove_extent::ARegisters>::type; if constexpr (cute::is_same_v) { cute::transform(recast(tensor), [](RegisterTypeA& v){ return v << 2; }); } } template CUTLASS_DEVICE void fp4_shift_B(SM120_SPARSE_16x8x64_TN const&, Tensor&& tensor) { using RegisterTypeB = typename remove_extent::BRegisters>::type; if constexpr (cute::is_same_v) { cute::transform(recast(tensor), [](RegisterTypeB& v){ return v << 2; }); } } } // end namespace SM120::SPARSE namespace SM120::BLOCKSCALED::SPARSE { // Template function with scale factor needs to enmuerate types one by one, as template // arguments contatins two variadic lists, which cannot be deduced in one shot. template CUTLASS_DEVICE void fp4_shift_A(SM120_SPARSE_16x8x64_TN_VS const&, Tensor&& tensor) { using RegisterTypeA = typename remove_extent::ARegisters>::type; if constexpr (cute::is_same_v) { cute::transform(recast(tensor), [](RegisterTypeA& v){ return v << 2; }); } } template CUTLASS_DEVICE void fp4_shift_B(SM120_SPARSE_16x8x64_TN_VS const&, Tensor&& tensor) { using RegisterTypeB = typename remove_extent::BRegisters>::type; if constexpr (cute::is_same_v) { cute::transform(recast(tensor), [](RegisterTypeB& v){ return v << 2; }); } } } // end namespace SM120::BLOCKSCALED::SPARSE } // end namespace cute