/******************************************************************************* * * MIT License * * Copyright (C) 2021-2025 Advanced Micro Devices, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * *******************************************************************************/ #ifndef GEMM_DRIVER_IMPL_HPP #define GEMM_DRIVER_IMPL_HPP #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #include #include #pragma GCC diagnostic pop namespace rocwmma { namespace CooperativeGemm { #define GemmDriverT typename GlobalMapping, typename LdsMapping #define GemmDriverT_impl GlobalMapping, LdsMapping template template __device__ inline void GemmDriver::globalReadCoopA( GRFragA (&grFragsA)[BlocksX], GetDataType_t const* gAddrA, uint32_t lda) { auto blockOffset = MappingUtil::dataOffset(GlobalMapping::blockOffsetA(), lda); #pragma unroll for(int i = 0; i < BlocksX; i++) { globalReadCoopA(grFragsA[i], gAddrA + i * blockOffset, lda); } } template __device__ inline void GemmDriver::globalReadCoopA( GRFragA& grFragA, GetDataType_t const* gAddrA, uint32_t lda) { rocwmma::load_matrix_sync(grFragA, gAddrA, lda); } template template __device__ inline void GemmDriver::globalReadCoopB( GRFragB (&grFragsB)[BlocksY], GetDataType_t const* gAddrB, uint32_t ldb) { auto blockOffset = MappingUtil::dataOffset(GlobalMapping::blockOffsetB(), ldb); #pragma unroll for(int i = 0; i < BlocksY; i++) { globalReadCoopB(grFragsB[i], gAddrB + i * blockOffset, ldb); } } template __device__ inline void GemmDriver::globalReadCoopB( GRFragB& grFragB, GetDataType_t const* gAddrB, uint32_t ldb) { rocwmma::load_matrix_sync(grFragB, gAddrB, ldb); } template template __device__ inline void GemmDriver::localWriteCoopA( GetDataType_t* ldsAddr, GRFragA const (&grFragsA)[BlocksX], uint32_t ldlds) { auto blockOffset = MappingUtil::dataOffset(LdsMapping::blockOffsetA(), ldlds); #pragma unroll for(int i = 0; i < BlocksX; i++) { localWriteCoopA(ldsAddr + i * blockOffset, grFragsA[i], ldlds); } } template __device__ inline void GemmDriver::localWriteCoopA( GetDataType_t* ldsAddr, GRFragA const& grFragA, uint32_t ldlds) { rocwmma::store_matrix_sync(ldsAddr, LdsMapping::formatLWFragA(grFragA), ldlds); } template template __device__ inline void GemmDriver::localWriteCoopB( GetDataType_t* ldsAddr, GRFragB const (&grFragsB)[BlocksY], uint32_t ldlds) { auto blockOffset = MappingUtil::dataOffset(LdsMapping::blockOffsetB(), ldlds); #pragma unroll for(int i = 0; i < BlocksY; i++) { localWriteCoopB(ldsAddr + i * blockOffset, grFragsB[i], ldlds); } } template __device__ inline void GemmDriver::localWriteCoopB( GetDataType_t* ldsAddr, GRFragB const& grFragB, uint32_t ldlds) { rocwmma::store_matrix_sync(ldsAddr, LdsMapping::formatLWFragB(grFragB), ldlds); } template __device__ inline void GemmDriver::localReadA( MfmaFragA& fragsA, GetDataType_t const* ldsAddrA, uint32_t ldlds) { LRFragA lrFragA; rocwmma::load_matrix_sync(lrFragA, ldsAddrA, ldlds); fragsA.mStorage = LdsMapping::formatLRFragA(lrFragA).mStorage; } template template __device__ inline void GemmDriver::localReadA( MfmaFragA (&fragsA)[BlocksX], GetDataType_t const* ldsAddrA, uint32_t ldlds) { auto blockStep = MappingUtil::dataOffset(LdsMapping::blockOffsetA(), ldlds); #pragma unroll for(int i = 0; i < BlocksX; i++) { localReadA(fragsA[i], ldsAddrA, ldlds); ldsAddrA += blockStep; } } template __device__ inline void GemmDriver::localReadB( MfmaFragB& fragsB, GetDataType_t const* ldsAddrB, uint32_t ldlds) { LRFragB lrFragB; rocwmma::load_matrix_sync(lrFragB, ldsAddrB, ldlds); fragsB.mStorage = LdsMapping::formatLRFragB(lrFragB).mStorage; } template template __device__ inline void GemmDriver::localReadB( MfmaFragB (&fragsB)[BlocksY], GetDataType_t const* ldsAddrB, uint32_t ldlds) { auto blockStep = MappingUtil::dataOffset(LdsMapping::blockOffsetB(), ldlds); #pragma unroll for(int i = 0; i < BlocksY; i++) { localReadB(fragsB[i], ldsAddrB, ldlds); ldsAddrB += blockStep; } } template template __device__ inline void GemmDriver::fill(FragT& frag, GetDataType_t value) { rocwmma::fill_fragment(frag, value); } template template __device__ inline void GemmDriver::fill(FragT (&frags)[BlocksX][BlocksY], GetDataType_t value) { #pragma unroll for(int i = 0; i < BlocksX; i++) { #pragma unroll for(int j = 0; j < BlocksY; j++) { fill(frags[i][j], value); } } } template __device__ inline void GemmDriver::mfma(MfmaFragAcc& fragAccOut, MfmaFragA const& fragA, MfmaFragB const& fragB, MfmaFragAcc const& fragAccIn) { rocwmma::mma_sync(fragAccOut, fragA, fragB, fragAccIn); } template template __device__ inline void GemmDriver::mfma(MfmaFragAcc (&fragAccOut)[BlocksX][BlocksY], MfmaFragA const (&fragA)[BlocksX], MfmaFragB const (&fragB)[BlocksY], MfmaFragAcc const (&fragAccIn)[BlocksX][BlocksY]) { #pragma unroll for(int i = 0; i < BlocksX; i++) { #pragma unroll for(int j = 0; j < BlocksY; j++) { mfma(fragAccOut[i][j], fragA[i], fragB[j], fragAccIn[i][j]); } } } template __device__ inline void GemmDriver::globalReadC( MfmaFragC& fragC, GetDataType_t const* gAddrC, uint32_t ldc) { rocwmma::load_matrix_sync(fragC, gAddrC, ldc); } template template __device__ inline void GemmDriver::globalReadC(MfmaFragC (&fragC)[BlocksX][BlocksY], GetDataType_t const* gAddrC, uint32_t ldc) { auto blockStepX = MappingUtil::dataOffset(GlobalMapping::blockOffsetA(), ldc); auto blockStepY = MappingUtil::dataOffset(GlobalMapping::blockOffsetB(), ldc); #pragma unroll for(int i = 0; i < BlocksX; i++) { auto offsetY = 0u; #pragma unroll for(int j = 0; j < BlocksY; j++) { globalReadC(fragC[i][j], gAddrC + offsetY, ldc); offsetY += blockStepY; } gAddrC += blockStepX; } } template __device__ inline void GemmDriver::globalWriteD( GetDataType_t* gAddrD, MfmaFragD const& fragD, uint32_t ldd) { rocwmma::store_matrix_sync(gAddrD, fragD, ldd); } template template __device__ inline void GemmDriver::globalWriteD(GetDataType_t* gAddrD, MfmaFragD const (&fragsD)[BlocksX][BlocksY], uint32_t ldd) { auto blockStepX = MappingUtil::dataOffset(GlobalMapping::blockOffsetA(), ldd); auto blockStepY = MappingUtil::dataOffset(GlobalMapping::blockOffsetB(), ldd); #pragma unroll for(int i = 0; i < BlocksX; i++) { auto offsetY = 0u; #pragma unroll for(int j = 0; j < BlocksY; j++) { globalWriteD(gAddrD + offsetY, fragsD[i][j], ldd); offsetY += blockStepY; } gAddrD += blockStepX; } } template __device__ inline void GemmDriver::uniformFma(MfmaFragD& fragD, GetDataType_t alpha, MfmaFragAcc const& fragAcc, GetDataType_t beta, MfmaFragC const& fragC) { // Split into chunks to not overwhelm the register capacity constexpr uint32_t ChunkSize = min(8u, MfmaFragD::size()); for(int i = 0; i < MfmaFragD::size() / ChunkSize; i++) { for(int j = 0; j < ChunkSize; j++) { auto idx = i * ChunkSize + j; // Perform computation in ComputeT and cast back to OutputT fragD.x[idx] = static_cast>( alpha * fragAcc.x[idx] + beta * static_cast>(fragC.x[idx])); } } } template template __device__ inline void GemmDriver::uniformFma( MfmaFragD (&fragsD)[BlocksX][BlocksY], GetDataType_t alpha, MfmaFragAcc const (&fragsAcc)[BlocksX][BlocksY], GetDataType_t beta, MfmaFragC const (&fragsC)[BlocksX][BlocksY]) { #pragma unroll for(int i = 0; i < BlocksX; i++) { #pragma unroll for(int j = 0; j < BlocksY; j++) { uniformFma(fragsD[i][j], alpha, fragsAcc[i][j], beta, fragsC[i][j]); } } } template __device__ inline void GemmDriver::syncWorkgroup() { rocwmma::synchronize_workgroup(); } template template __device__ inline void GemmDriver::prioritize_wavefront() { using SetPrio = SetPrio; SetPrio::exec(); } template template __device__ inline void GemmDriver::sched_barrier() { using SchedBarrier = SchedBarrier; SchedBarrier::exec(); } template template __device__ inline void GemmDriver::mem_barrier() { using Waitcnt = Waitcnt; Waitcnt::exec(); } template template __device__ inline void GemmDriver::vector_mem_barrier() { using WaitVmcnt = WaitVmcnt; WaitVmcnt::exec(); } template template __device__ inline void GemmDriver::lds_mem_barrier() { using WaitLgkmcnt = WaitLgkmcnt; WaitLgkmcnt::exec(); } template __device__ inline void GemmDriver::scheduleKLoop() { // K-loop rough scheduling: // 1. GR // 2. LR // 3. Mma // 4. LW SchedBarrierVmemRead::exec(); SchedBarrierDsRead::exec(); SchedBarrierMma::exec(); SchedBarrierDsWrite::exec(); } #undef GemmDriverT #undef GemmDriverT_impl } // namespace CooperativeGemm } // namespace rocwmma #endif // GEMM_DRIVER_IMPL_HPP