Files
TheRock/rocm-libraries/projects/hipblaslt/tensilelite/Tensile/Components/GSU.py
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

2231 lines
137 KiB
Python

################################################################################
#
# Copyright (C) 2024-2026 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 cop-
# ies 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 IM-
# PLIED, 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 CONNE-
# CTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
################################################################################
from rocisa import countInstruction
from rocisa.code import Module, Label, RegSet, ValueSet
from rocisa.container import ContinuousRegister, EXEC, SMEMModifiers, MUBUFModifiers, FLATModifiers, vgpr, sgpr, replaceHolder, VCC
from rocisa.enum import CacheScope
from rocisa.instruction import SAddCU32, SAddU32, SAndB32, SLoadB32, SStoreB32, SBranch, \
SCBranchSCC0, SCBranchSCC1, SCMovB32, SCSelectB32, SCmpEQU32, SCmpLgU32, SCmpLtU32, SCmpGtI32, \
SLShiftLeftB64, SLShiftRightB32, SMovB32, SMovB64, SMulI32, SSubU32, SCmpEQI32, SEndpgm, \
SCmpLeI32, VCmpGEI32, SSubI32, SCBranchSCC0, VMovB32, SLShiftLeftB32, SWaitCnt, SWaitXCnt, SBarrier, \
SNop, SSleep, VAddF32, VAddI32, VReadfirstlaneB32, SMulHIU32, VAddPKF32, VCndMaskB32, SAtomicDec, \
SCmpEQU64, BufferStoreB32, BufferLoadB32, VMovB64, FlatAtomicDecU32, VAddCOU32, VAddCCOU32
from rocisa.functions import scalarStaticMultiply64, scalarUInt32DivideAndRemainder, vectorStaticMultiply
from ..Common import ceilDivide, log2, print2, INDEX_CHARS
from ..Component import Component
from ..AsmStoreState import StoreState, VectorDataTypes
from ..AsmAddressCalculation import AddrCalculation
import abc
from copy import deepcopy
from math import ceil
from ..KernelWriterModules import mapAcctoArchRegs
class GSU(Component):
"""
GSU block.
"""
@abc.abstractmethod
def graWorkGroup(self, writer, kernel):
pass
@abc.abstractmethod
def computeLoadSrd(self, writer, kernel, tP, stmp, tileStart):
pass
@abc.abstractmethod
def graIncrements(self, writer, kernel, loopIdx, tP):
pass
@abc.abstractmethod
def graIncrementsRestore(self, writer, kernel, loopCounterName):
pass
def graIncrementsCommon(self, writer, loopIdx, tc, stride, m):
module = Module("GSU Common graIncrements")
# multiply by stride, optimizing if unit stride
if writer.isConstUnitStride(stride):
if tc == "A":
abinfo = writer.states.a
elif tc == "B":
abinfo = writer.states.b
elif tc == "MXSA":
abinfo = writer.states.mxsa
elif tc == "MXSB":
abinfo = writer.states.mxsb
else:
abinfo = None
if abinfo != None and abinfo.useConstSgprGlobalReadIncs:
# useConstSgprGlobalReadIncs case, define value set for GlobalReadIncs here instead of initializing sgpr
module.add(ValueSet("GlobalReadIncs%s"%tc, m))
else:
module.add(SMovB32(dst=sgpr("GlobalReadIncs%s+%u"%(tc, loopIdx)), src=m, \
comment="incr%s (unrollIdx)"%(tc) ))
else:
module.add(SMulI32(dst=sgpr("GlobalReadIncs%s+%u"%(tc, loopIdx)), \
src0=m, src1=stride, \
comment="incr%s unrollIdx)"%(tc) ))
return module
@abc.abstractmethod
def calculateLoopNumIter(self, writer, kernel, loopCounterName, tmpSgprInfo):
pass
@abc.abstractmethod
def calculateIncrementMetadata(self, writer, kernel, sgprOut):
pass
@abc.abstractmethod
def computeStoreSrdStart(self, writer, kernel):
pass
def computeStoreSrdStartCommon(self, writer, kernel):
module = Module("GSU Common computeStoreSrdStart")
indices = list(range(0, kernel["ProblemType"]["NumIndicesC"]))
numDim = len(indices)
if kernel["_GlobalAccumulation"] == "MultipleBuffer" or kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel":
gsuLabel = Label(label=writer.labels.getNameInc("GSU"), comment="")
with writer.allocTmpSgpr(1, tag="computeStoreSrdStart_tmpSgprGSU") as tmpSgprGSU:
module.add(SAndB32(dst=sgpr(tmpSgprGSU.idx), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
module.add(SCmpEQU32(src0=sgpr(tmpSgprGSU.idx), src1=1, comment="GSU == 1 ?"))
module.add(SCBranchSCC1(labelName=gsuLabel.getLabelName(), comment="branch if GSU == 1"))
# GSU algorithm 2: adjust output buffer address to per GSU buffer
with writer.allocTmpSgpr(4, alignment=1, tag="computeStoreSrdStart_tmpSgprInfo") as tmpSgprInfo:
if tmpSgprInfo.idx % 2 == 0:
tmpSgprX2 = tmpSgprInfo.idx+0
tmpSgpr0 = tmpSgprInfo.idx+0
tmpSgpr1 = tmpSgprInfo.idx+1
tmpSgpr2 = tmpSgprInfo.idx+2
tmpSgpr3 = tmpSgprInfo.idx+3
else:
tmpSgprX2 = tmpSgprInfo.idx+1
tmpSgpr0 = tmpSgprInfo.idx+1
tmpSgpr1 = tmpSgprInfo.idx+2
tmpSgpr2 = tmpSgprInfo.idx+0
tmpSgpr3 = tmpSgprInfo.idx+3
module.addComment("GSU Output Buffer offset: Free0 + (Free1-1)*StrideC1J + (Free2-1)*StrideCK * GSUIdx * bpe%s")
module.addModuleAsFlatItems(writer.s_mul_u64_u32(sgpr(tmpSgpr0), sgpr(tmpSgpr1), sgpr("SizesFree+0"), sgpr("GSUSumIdx"), comment="Free0"))
for i in range(1, numDim):
module.add(SSubU32(dst=sgpr(tmpSgpr2), src0=sgpr("SizesFree+%u"%i), src1=1, comment="Free%u" % i))
module.add(SMulI32(dst=sgpr(tmpSgpr2), src0=sgpr(tmpSgpr2), src1=sgpr("GSUSumIdx"), comment="Free%u" % i))
module.addModuleAsFlatItems(writer.s_mul_u64_u32(sgpr(tmpSgpr2), sgpr(tmpSgpr3), sgpr(tmpSgpr2), sgpr("StrideC%s"%writer.states.indexChars[i]), comment="Free%u" % i))
module.add(SAddU32(dst=sgpr(tmpSgpr0), src0=sgpr(tmpSgpr0), src1=sgpr(tmpSgpr2), comment="Free%u" % i))
module.add(SAddCU32(dst=sgpr(tmpSgpr1), src0=sgpr(tmpSgpr1), src1=sgpr(tmpSgpr3), comment="Free%u" % i))
module.add(SLShiftLeftB64(dst=sgpr(tmpSgprX2,2), src=sgpr(tmpSgprX2,2), shiftHex=log2(writer.states.bpeCinternal), comment="scale by bpe"))
module.add(SAddU32(dst=sgpr("SrdD+0"), src0=sgpr("SrdD+0"), src1=sgpr(tmpSgprX2), comment="add lo GSU offset to SRD"))
module.add(SAddCU32(dst=sgpr("SrdD+1"), src0=sgpr("SrdD+1"), src1=sgpr(tmpSgpr1), comment="add hi GSU offset to SRD"))
module.add(gsuLabel)
return module
@abc.abstractmethod
def noLoadLoop(self, writer, kernel, tensorParametersA, tensorParametersB, pack, packPre):
pass
@abc.abstractmethod
def tailLoopNumIter(self, writer, kernel, loopCounter):
pass
@abc.abstractmethod
def setupNewTile(self, writer, kernel, tensorParametersA, tensorParametersB, tPM):
pass
def graIncrementsAB(self, writer, kernel, tensorParametersA, tensorParametersB, tPM):
module = Module("GSU Common graIncrementsAB")
module.addComment1("global read addresses: increments a")
for i in reversed(range(kernel["ProblemType"]["NumIndicesSummation"])):
module.add(writer.graIncrements(kernel, i, tensorParametersA))
if kernel["ProblemType"]["MXBlockA"]:
module.addComment1("global read addresses: increments mxsa")
for i in reversed(range(kernel["ProblemType"]["NumIndicesSummation"])):
module.add(writer.graIncrements(kernel, i, tensorParametersA["MX"]))
if kernel["ProblemType"]["MXBlockB"]:
module.addComment1("global read addresses: increments mxsb")
for i in reversed(range(kernel["ProblemType"]["NumIndicesSummation"])):
module.add(writer.graIncrements(kernel, i, tensorParametersB["MX"]))
if kernel["ProblemType"]["Sparse"] and not kernel["DirectToVgprSparseMetadata"]:
module.addComment1("global read addresses: increments metadata")
for i in reversed(range(kernel["ProblemType"]["NumIndicesSummation"])):
module.add(writer.graIncrements(kernel, i, tPM))
module.addComment1("global read addresses: increments b")
for i in reversed(range(kernel["ProblemType"]["NumIndicesSummation"])):
module.add(writer.graIncrements(kernel, i, tensorParametersB))
return module
@abc.abstractmethod
def globalWriteBatchProlog(self, writer, kernel, tmpVgpr, tmpVgprSize, tmpVgprDynamic, \
batchIdx, ss, gwvw, batchElements, \
beta, edge, sumIdxGSUSYNC, addrCalc):
pass
@abc.abstractmethod
def defineAndResources(self, writer, kernel, tmpSgpr0, tmpSgprM, tmpSgprN, tmpSgprNumWG0, tmpSgprAccumTiles):
pass
@abc.abstractmethod
def writeBiasToGlobal(self, writer, kernel, biasDataType, tP, tmpSgprRes, biasBpe):
pass
@abc.abstractmethod
def reductionBranches(self, writer, kernel, tPB, vectorWidths, elements, tmpVgpr, cvtVgprStruct, vectorDataTypes, factorDims, reductionEndLabel, endLabel):
pass
@abc.abstractmethod
def initializeSrd(self, writer, ArgTypeCheckLabel, GeneralBatchedGemmSrdInitiation_End, kernel, ch):
pass
@abc.abstractmethod
def routeToGeneralBatchedOrStridedBatched(self, stridedBatchedGemmLoad, multipleBufferChecks, generalBatchedGemmLoad, mat, kernel, tmpS1):
pass
class GSUOff(GSU):
kernel = {"GlobalSplitU": 0}
def __call__(self):
assert(0)
def graWorkGroup(self, writer, kernel):
module = Module("GSU Off graWorkGroup")
return module
def computeLoadSrd(self, writer, kernel, tP, stmp, tileStart):
module = Module("GSU Off computeLoadSrd")
return module
def graIncrements(self, writer, kernel, loopIdx, tP):
module = Module("GSU Off graIncrements")
tc = tP["tensorChar"]
tIdx: int = tP["idx"]
dimIdx = kernel["ProblemType"]["IndicesSummation"][loopIdx] # dimension index
loopChar = writer.states.indexChars[dimIdx]
stride = writer.strideRef(tc, dimIdx)
isMirrorIdx = dimIdx in kernel["ProblemType"]["MirrorDims%s"%tc]
m = int(kernel["_DepthU%s"%tc] * tP["bpeGR"])
if isMirrorIdx:
m = -m
if writer.states.globalReadIncsUseVgpr:
with writer.allocTmpSgpr(2, tag="GSUOff_graIncrements_tmpSgprInfo") as tmpSgprInfo:
tmpSgpr = tmpSgprInfo.idx
module.add(SMovB32(dst=sgpr(tmpSgpr+0), src="DepthU*%d"%(tP["bpeGR"]), comment="DepthU*Bpe"))
module.add(SMulI32(dst=sgpr(tmpSgpr+0), src0=sgpr(tmpSgpr+0), src1=stride, \
comment="incr%s%s = %s*DepthU*bpeGR (unrollIdx)"%(tc, loopChar, stride) ))
# TODO - this should be mul-H??
module.add(SMovB32(dst=sgpr(tmpSgpr+1), src=hex(0), comment="(carry)"))
module.add(VMovB32(dst=vgpr("GlobalReadIncs%s+%u+0"%(tc, 2*loopIdx)), src=sgpr(tmpSgpr+0)))
module.add(VMovB32(dst=vgpr("GlobalReadIncs%s+%u+1"%(tc, 2*loopIdx)), src=sgpr(tmpSgpr+1)))
else:
# MX scale unroll-step SRD increment, gated by MXScaleFormat:
# - Swizzled (HostPreSwizzle/InMemorySwizzle): each K-block of
# scales (DepthU/MXBlock K-scales per M) is laid out as a
# contiguous (M, K_inner) block, so a K-step of DepthU advances
# the SRD by Size{tile} * (DepthU/MXBlock * bpe).
# - NoSwizzle: canonical row/column layout with K-axis stride 1;
# the K-step of DepthU is just DepthU/MXBlock * bpe and goes
# through the standard graIncrementsCommon path.
mxFmt = kernel.get("MXScaleFormat", "NoSwizzle")
isMxSwizzledScale = ('MXS' in tc) and mxFmt in ("InMemorySwizzle", "HostPreSwizzle")
if isMxSwizzledScale and writer.isConstUnitStride(stride):
module.add(SMulI32(dst=sgpr("GlobalReadIncs%s+%u"%(tc, loopIdx)), \
src0=sgpr("Size%s"%INDEX_CHARS[tIdx]), src1=m, \
comment="incr%s = Size%s*DepthU*Bpe (unrollIdx, swizzled MX scale layout)"%(tc, INDEX_CHARS[tIdx])))
else:
module.add(self.graIncrementsCommon(writer, loopIdx, tc, stride, m))
return module
def graIncrementsRestore(self, writer, kernel, loopCounterName):
module = Module("GSU Off graIncrementsRestore")
return module
def calculateLoopNumIter(self, writer, kernel, loopCounterName, tmpSgprInfo):
module = Module("GSU Off calculateLoopNumIter")
return module
def calculateIncrementMetadata(self, writer, kernel, sgprOut):
module = Module("GSU Off calculateLoopNumIter")
module.add(SMovB32(dst=sgpr(sgprOut), src=kernel["DepthU"], comment="IncsMetadata = DepthU if GSUC == 1"))
module.add(SLShiftRightB32(dst=sgpr(sgprOut), shiftHex=hex(log2(8)), src=sgpr(sgprOut)))
return module
def computeStoreSrdStart(self, writer, kernel):
module = Module("GSU Off computeStoreSrdStart")
return module
def noLoadLoop(self, writer, kernel, tensorParametersA, tensorParametersB, pack, packPre):
module = Module("GSU Off noLoadLoop")
return module
def tailLoopNumIter(self, writer, kernel, loopCounter):
module = Module("GSU Off tailLoopNumIter")
return module
def setupNewTile(self, writer, kernel, tensorParametersA, tensorParametersB, tPM):
module = Module("GSU Off setupNewTile")
module.add(self.graIncrementsAB(writer, kernel, tensorParametersA, tensorParametersB, tPM))
return module
def reductionBranches(self, writer, kernel, tPB, vectorWidths, elements, tmpVgpr, cvtVgprStruct, vectorDataTypes, factorDims, reductionEndLabel, endLabel):
module = Module("GSU Off reductionBranches")
return module
def globalWriteBatchProlog(self, writer, kernel, tmpVgpr, tmpVgprSize, tmpVgprDynamic, \
batchIdx, ss, gwvw, batchElements, \
beta, edge, sumIdxGSUSYNC, addrCalc):
module = Module("GSU Off globalWriteBatchProlog")
return module
def defineAndResources(self, writer, kernel, tmpSgpr0, tmpSgprM, tmpSgprN, tmpSgprNumWG0, tmpSgprAccumTiles):
module = Module("GSU Off defineAndResources")
return module
def writeBiasToGlobal(self, writer, kernel, biasDataType, tP, tmpSgprRes, biasBpe):
module = Module("GSU Off writeBiasToGlobal")
return module
def initializeSrd(self, writer, ArgTypeCheckLabel, GeneralBatchedGemmSrdInitiation_End, kernel, ch):
module = Module("GSU Off initializeSrd")
return module
def routeToGeneralBatchedOrStridedBatched(self, stridedBatchedGemmLoad, multipleBufferChecks, generalBatchedGemmLoad, mat, kernel, tmpS1):
module = Module("GSU Off routeToGeneralBatchedOrStridedBatched")
return module
class GSUOn(GSU):
kernel = {"GlobalSplitUAlgorithm": ["MultipleBuffer", "MultipleBufferSingleKernel"]}
# if GSU <= gsuThreshold, last wg does the reduction and no R/W to WS
# else, atomic_dec chooses the wg to do the reduction
gsuThreshold = 2
@classmethod
def matches(cls, writer, debug=False):
return writer.states.kernel["GlobalSplitU"] > 0 or writer.states.kernel["GlobalSplitU"] == -1
def __call__(self):
assert(0)
def graWorkGroup(self, writer, kernel):
module = Module("GSU On graWorkGroup")
gsuLabel = Label(label=writer.labels.getNameInc("GSU"), comment="")
gsuLabelEnd = Label(label=writer.labels.getNameInc("GSU_End"), comment="")
with writer.allocTmpSgpr(1, tag="GSU On graWorkGroup_tmpSgprGSU") as tmpSgprGSU:
module.add(SAndB32(dst=sgpr(tmpSgprGSU.idx), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
module.add(SCmpEQU32(src0=sgpr(tmpSgprGSU.idx), src1=1, comment="GSU == 1 ?"))
module.add(SCBranchSCC1(labelName=gsuLabel.getLabelName(), comment="branch if GSU == 1"))
if (kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel" or kernel["AdaptiveGemmGSUA"] == 1):
extReadEpilogueLabeltmp = Label(label=writer.labels.getNameInc("LoadExternalEpilogueStruct"), comment="")
module.addComment0("Check if custom structure pointer is null")
if kernel["ProblemType"]["SupportUserArgs"]:
module.add(SCmpEQU32(src0=sgpr("ArgType"), src1=2, comment="ArgType == 2 ?"))
module.add(SCBranchSCC0(labelName=extReadEpilogueLabeltmp.getLabelName()))
with writer.allocTmpSgpr(2,2, tag="GSU On graWorkGroup_tmpSgprD") as tmpSgprD:
module.add(SMovB64(dst=sgpr(tmpSgprD.idx,2), src=sgpr("AddressD",2), comment="tmp=Output"))
module.add(SMovB64(dst=sgpr("AddressD",2), src=sgpr("AddressTD",2), comment="D=Workspace"))
module.add(SMovB64(dst=sgpr("AddressTD",2), src=sgpr(tmpSgprD.idx,2), comment="TD=Output"))
module.add(extReadEpilogueLabeltmp)
module.addComment("GSU-not-WGMapRR :nwg1 = (size%s + MT%s - 1) / MT%s;" \
% (writer.states.tileChar1, writer.states.tileChar1, writer.states.tileChar1))
tmpVgpr = writer.vgprPool.checkOut(2, tag="GSUOn_graWorkGroup_tmpVgpr")
tmpVgprRes = ContinuousRegister(idx=tmpVgpr, size=2)
gsuwgmrrLabel = Label(label=writer.labels.getNameInc("GSUWGMRR"), comment="")
gsuwgmrrLabelEnd = Label(label=writer.labels.getNameInc("GSUWGMRR_End"), comment="")
with writer.allocTmpSgpr(1, tag="GSU On graWorkGroup_tmpSgprInfo") as tmpSgprInfo:
module.add(SAndB32(dst=sgpr(tmpSgprInfo.idx), src0=sgpr("GSU"), src1=hex(0x4000), comment="SCC = (GSUWGMRR == 1) ?"))
module.add(SCBranchSCC1(labelName=gsuwgmrrLabel.getLabelName(), comment="branch if GSUWGMRR == 1"))
# wg1 = wg1 / GSU
# gsuSumIdx = wg1 % GSU
module.add(SAndB32(dst=sgpr(tmpSgprInfo.idx), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
module.add(scalarUInt32DivideAndRemainder("WorkGroup1", "WorkGroup1", tmpSgprInfo.idx, "GSUSumIdx", tmpVgprRes, kernel["WavefrontSize"]))
module.add(SBranch(gsuwgmrrLabelEnd.getLabelName()))
module.add(gsuwgmrrLabel)
# gsuSumIdx = wg1 / numWg1
# wg1 = wg1 % numWg1
module.add(scalarUInt32DivideAndRemainder("GSUSumIdx", "WorkGroup1", "NumWorkGroups1", "WorkGroup1", tmpVgprRes, kernel["WavefrontSize"]))
module.add(gsuwgmrrLabelEnd)
writer.vgprPool.checkIn(tmpVgpr)
module.add(SMovB32(dst=sgpr("GSULog2BpeC"), src=log2(int(writer.states.bpr * kernel["ProblemType"]["DestDataType"].numRegisters()))))
module.add(SMovB32(dst=sgpr("GSULog2BpeD"), src=log2(writer.states.bpeCinternal)))
module.add(SBranch(gsuLabelEnd.getLabelName()))
module.add(gsuLabel)
module.add(SMovB64(dst=sgpr("GSUSumIdx", 2), src=0, comment="Set GSUSumIdx to 0"))
module.add(SMovB32(dst=sgpr("GSULog2BpeC"), src=log2(writer.states.bpeCexternalGSU1)))
module.add(SMovB32(dst=sgpr("GSULog2BpeD"), src=log2(writer.states.bpeCexternalGSU1)))
module.add(gsuLabelEnd)
return module
def computeLoadSrd(self, writer, kernel, tP, stmp, tileStart):
module = Module("GSU On computeLoadSrd")
tc = tP["tensorChar"]
isgfx950 = kernel["ISA"][:2] == (9, 5)
# MX scales in a swizzled layout (HostPreSwizzle / InMemorySwizzle)
# use the per-tensor _DepthU{tc} sequencing because their effective
# K stride differs from A/B; NoSwizzle MX scales share _DepthU with A/B.
mxScaleFormat = kernel.get("MXScaleFormat", "NoSwizzle")
isMxSwizzledScaleLayout = ("MXS" in tc) and mxScaleFormat in ("InMemorySwizzle", "HostPreSwizzle")
depthU = kernel["DepthU"]
depthUDiv = kernel["_DepthU%s"%tc] if isMxSwizzledScaleLayout else kernel["_DepthU"]
# swizzle
if (tP["isSwizzled"] and tc == 'A'):
depthUDiv = kernel["DepthU"] * kernel["MatrixInstM"]
elif (tP["isSwizzled"] and tc == 'B'):
depthUDiv = kernel["DepthU"] * kernel["MatrixInstN"]
gsuOffsetStr = "gsuOffset = DepthU*bpeGR*GSUSumIdx"
divider = 1
if kernel["ProblemType"]["Sparse"]:
if (kernel["ProblemType"]["Sparse"] == 2 and tP["isB"]) or \
(kernel["ProblemType"]["Sparse"] == 1 and tP["isA"]) :
divider = 2
elif tP["isM"]:
divider = 8
if divider != 1:
depthUDiv = depthU // divider
gsuOffsetStr = "gsuOffset = DepthU/%s*bpeGR*GSUSumIdx"%(divider)
gsucLabel = Label(label=writer.labels.getNameInc(f"GSUC_{tc}"), comment="")
gsucLabelEnd = Label(label=writer.labels.getNameInc(f"GSUC_{tc}_End"), comment="")
module.add(SAndB32(dst=sgpr(stmp), src0=sgpr("GSU"), src1=hex(0x8000), comment="SCC = (GSUC == 1) ?"))
module.add(SCBranchSCC1(labelName=gsucLabel.getLabelName(), comment="branch if GSUC == 1"))
gsuOffsetStr = "gsuOffset = DepthU*GSUSumIdx"
module.addModuleAsFlatItems(writer.s_mul_u64_u32(sgpr(stmp+0), sgpr(stmp+1), depthUDiv, sgpr("GSUSumIdx"), comment=gsuOffsetStr))
module.add(SBranch(gsucLabelEnd.getLabelName()))
module.add(gsucLabel)
gsuOffsetStr = "gsuOffset = DepthU*accumulatedNumOfLoopCounterL"
loopCounterName = writer.loopCounterName(kernel, writer.states.unrollIdx)
module.add(SLShiftRightB32(dst=sgpr(loopCounterName), src=sgpr("SizesSum"), shiftHex=log2(depthU), \
comment="s[%s] = s[sgprSizesSum] / %s"%(loopCounterName, depthU)))
tmpSgprInfo = ContinuousRegister(idx=stmp, size=2)
module.add(writer.calculateLoopNumIterOffsetGsu(kernel, loopCounterName, tmpSgprInfo))
module.addModuleAsFlatItems(writer.s_mul_u64_u32(sgpr(stmp+0), sgpr(stmp+1), sgpr(stmp+0), depthUDiv, comment=gsuOffsetStr))
module.add(gsucLabelEnd)
unrollSummation = [ i for i in tP["ia"] if i in kernel["ProblemType"]["IndicesSummation"] ]
stride = writer.strideRef(tc, unrollSummation[-1])
if tP["tlu"] and not writer.isConstUnitStride(stride):
# non-transpose case, unroll is in perp dim and should be scaled by unroll Stride
module.addModuleAsFlatItems(writer.s_mul_u64_u32(sgpr(stmp), sgpr(stmp+1), sgpr(stmp+0), \
stride, comment="tlu=1, scaled unroll-offset by stride"))
module.add(SAddU32(dst=sgpr(tileStart+0), src0=sgpr(tileStart+0), src1=sgpr(stmp+0), comment="accum GsuOffset term to tilestart"))
module.add(SAddCU32(dst=sgpr(tileStart+1), src0=sgpr(tileStart+1), src1=sgpr(stmp+1), comment="accum GsuOffset term to tilestart"))
return module
def graIncrements(self, writer, kernel, loopIdx, tP):
module = Module("GSU On graIncrements")
tc = tP["tensorChar"]
tIdx: int = tP["idx"]
dimIdx = kernel["ProblemType"]["IndicesSummation"][loopIdx] # dimension index
loopChar = writer.states.indexChars[dimIdx]
stride = writer.strideRef(tc, dimIdx)
isMirrorIdx = dimIdx in kernel["ProblemType"]["MirrorDims%s"%tc]
if writer.states.globalReadIncsUseVgpr:
with writer.allocTmpSgpr(3, tag="GSUOn_graIncrements_tmpSgprInfo") as tmpSgprInfo:
tmpSgpr = tmpSgprInfo.idx
gsuSgpr = tmpSgpr + 2
du = kernel["_DepthU%s"%tc]
duBpe = int(du * tP["bpeGR"])
module.add(SAndB32(dst=sgpr(tmpSgpr), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
module.add(SMulI32(dst=sgpr(gsuSgpr), src0=sgpr(tmpSgpr), src1=duBpe, comment="GSU*DepthU*Bpe"))
module.add(SAndB32(dst=sgpr(tmpSgpr), src0=sgpr("GSU"), src1=hex(0x8000), comment="SCC = (GSUC == 1) ?"))
module.add(SCMovB32(dst=sgpr(gsuSgpr), src=duBpe, comment="DepthU*Bpe if GSUC = 1"))
module.add(SMulI32(dst=sgpr(tmpSgpr+0), src0=sgpr(gsuSgpr), src1=stride, \
comment="incr%s%s = %s*DepthU*bpeGR (unrollIdx)"%(tc, loopChar, stride) ))
# TODO - this should be mul-H??
module.add(SMovB32(
dst=sgpr(tmpSgpr+1), \
src=0, \
comment="(carry)"))
module.add(VMovB32(
dst=vgpr("GlobalReadIncs%s+%u+0"%(tc, 2*loopIdx)), \
src=sgpr(tmpSgpr+0)))
module.add(VMovB32(
dst=vgpr("GlobalReadIncs%s+%u+1"%(tc, 2*loopIdx)), \
src=sgpr(tmpSgpr+1)))
else:
with writer.allocTmpSgpr(2, tag="GSUOn_graIncrements_tmpSgprInfo2") as tmpSgprInfo:
incr = sgpr("GlobalReadIncs%s+%u"%(tc, loopIdx))
tmpSgpr = tmpSgprInfo.idx
gsuSgpr = tmpSgpr + 1
# GlobalReadIncs doubles as scratch for the intermediate duBpe value;
# final SCSelectB32/SMulI32 writes the result back to the same register.
incSgpr = incr
tcGR = tc if tc == "Metadata" else (tc + "GR")
# swizzle: resolve MI dimension to numeric value
mi_dim = 1
if tc == "A" and kernel["ProblemType"]["SwizzleTensorA"]:
mi_dim = kernel["MatrixInstM"]
elif tc == "B" and kernel["ProblemType"]["SwizzleTensorB"]:
mi_dim = kernel["MatrixInstN"]
du = kernel["_DepthU%s"%tc]
duBpe = int(du * tP["bpeGR"]) * mi_dim
module.add(SAndB32(dst=sgpr(gsuSgpr), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
# MX scale GSU SRD increment, gated by MXScaleFormat:
# - Swizzled (HostPreSwizzle/InMemorySwizzle): the K-block is
# M*K_inner contiguous, so the increment scales with Size{tile}.
# - NoSwizzle: canonical layout, same step as A/B (duBpe only).
mxFmt = kernel.get("MXScaleFormat", "NoSwizzle")
isMxSwizzledScale = ('MXS' in tc) and mxFmt in ("InMemorySwizzle", "HostPreSwizzle")
if isMxSwizzledScale:
module.add(SMulI32(dst=incSgpr, src0=sgpr("Size%s"%INDEX_CHARS[tIdx]), src1=duBpe, comment="GSU*DepthU*Bpe*MI_dim(%d) (swizzled MX scale layout)"%(mi_dim)))
elif kernel["enableTDMMetadata"] and tP["isM"] and kernel["ProblemType"]["MetadataLayout"]:
ia = kernel["ProblemType"]["IndexAssignmentsMetadata"]
metadataStrideSgpr = f"Stride{tc}{writer.states.indexChars[ia[1]]}"
module.add(SMovB32(dst=incSgpr, src=sgpr(metadataStrideSgpr), comment="incSgpr = stride"))
module.add(SMulI32(dst=incSgpr, src0=incSgpr, src1=(du), comment="incSgpr = stride * du"))
else:
module.add(SMovB32(dst=incSgpr, src=duBpe, comment="GSU*DepthU*Bpe*MI_dim(%d)"%(mi_dim)))
module.add(SMulI32(dst=sgpr(gsuSgpr), src0=sgpr(gsuSgpr), src1=incSgpr, comment="GSU*DepthU*Bpe*MI_dim(%d)"%(mi_dim)))
module.add(SAndB32(dst=sgpr(tmpSgpr), src0=sgpr("GSU"), src1=hex(0x8000), comment="SCC = (GSUC == 1) ?"))
m = sgpr(gsuSgpr)
if isMirrorIdx:
m.setMinus(True)
duBpe = int(du * tP["bpeGR"]) * mi_dim
# multiply by stride, optimizing if unit stride
if writer.isConstUnitStride(stride) or (kernel["enableTDMMetadata"] and tP["isM"]):
module.add(SCSelectB32(dst=incr, src0=incSgpr, src1=m, comment="incr%s (unrollIdx)"%(tc)))
else:
module.add(SCMovB32(dst=m, src=duBpe, comment="DepthU*Bpe if GSUC = 1"))
module.add(SMulI32(dst=incr, src0=m, src1=stride, comment="incr%s unrollIdx)"%(tc) ))
return module
def graIncrementsRestore(self, writer, kernel, loopCounterName):
module = Module("GSU On graIncrementsRestore")
with writer.allocTmpSgpr(1, tag="GSU On graIncrementsRestore_tmpSgprInfo") as tmpSgprInfo:
gsuSgpr = tmpSgprInfo.idx
module.add(SAndB32(dst=sgpr(gsuSgpr), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
module.add(SMulI32(dst=sgpr(gsuSgpr), src0=sgpr(gsuSgpr), src1=kernel["DepthU"]))
module.add(SMulI32(dst=sgpr(loopCounterName), src0=sgpr(loopCounterName), \
src1=sgpr(gsuSgpr), comment="=loopCounterName*DepthU"))
return module
def calculateLoopNumIter(self, writer, kernel, loopCounterName, tmpSgprInfo):
module = Module("GSU On calculateLoopNumIter")
tmpSgpr = tmpSgprInfo.idx
# if GSU numIter++ if gsuSumIdx < remainder
gsuLabel = Label(label=writer.labels.getNameInc("GSU"), comment="")
module.add(SAndB32(dst=sgpr(tmpSgpr), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
module.add(SCmpEQU32(src0=sgpr(tmpSgpr), src1=1, comment="GSU == 1 ?"))
module.add(SCBranchSCC1(labelName=gsuLabel.getLabelName(), comment="branch if GSU == 1"))
module.add(writer.calculateLoopNumIterGsu(kernel, loopCounterName, tmpSgprInfo))
module.add(gsuLabel)
return module
##############################################################################
# Emit code to compute loop iterations for GSU.
# See same function in KernelWriterSource.py for background explanation
# This function is used to compute number of loop iters and also
# for computing the global read increment for GSU case.
# For multiple summation, the number of loop iterations needs to be reset
# for each iteration so replicate the code in addr inc and at open of unroll loop
# tmpSgpr is allocation of at least 3 tmpSgpr
# Output: SGPR(destName) contains the number of unroll iterations for
# this workgroup.
##############################################################################
def calculateLoopNumIterGsu(self, writer, kernel, destName, tmpSgprRes: ContinuousRegister):
module = Module("calculateLoopNumIterGsu")
loopCounter = sgpr(destName)
quotient = destName
remainder = "GSUSumIdx+1" # numIterPerWgRemainder
dividend = destName
tmpVgpr = writer.vgprPool.checkOut(2, tag="GSUOn_calculateLoopNumIter_tmpVgpr")
tmpVgprRes = ContinuousRegister(idx=tmpVgpr, size=2)
module.add(scalarUInt32DivideAndRemainder(quotient, dividend, "GSU", remainder, tmpVgprRes, wavewidth=kernel["WavefrontSize"]))
writer.vgprPool.checkIn(tmpVgpr)
# if gsuSumIdx < numIterPerWgRemainder
module.add(SAddU32(dst=sgpr(tmpSgprRes.idx), src0=1, \
src1=loopCounter, comment="tmp<-numIterMyWg+" ))
module.add(SCmpLtU32(src0=sgpr("GSUSumIdx"), src1=sgpr("GSUSumIdx+1"), \
comment="gsuSumIdx < numIterPerWgRemainder" ))
module.add(SCMovB32(dst=loopCounter, src=sgpr(tmpSgprRes.idx), comment="numIterMyWg++ if needed"))
return module
def calculateIncrementMetadata(self, writer, kernel, sgprOut):
module = Module("GSU On calculateLoopNumIter")
with writer.allocTmpSgpr(1, tag="GSU On calculateIncrementMetadata_tmpSgprGSU") as tmpSgprGSU:
module.add(SAndB32(dst=sgpr(tmpSgprGSU.idx), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
module.add(SMulI32(dst=sgpr(sgprOut), src0=kernel["DepthU"], src1=sgpr(tmpSgprGSU.idx), comment="IncsMetadata = GSU*DepthU"))
module.add(SAndB32(dst=sgpr(tmpSgprGSU.idx), src0=sgpr("GSU"), src1=hex(0x8000), comment="SCC = (GSUC == 1) ?"))
module.add(SCMovB32(dst=sgpr(sgprOut), src=kernel["DepthU"], comment="IncsMetadata = DepthU if GSUC == 1"))
module.add(SLShiftRightB32(dst=sgpr(sgprOut), shiftHex=hex(log2(8)), src=sgpr(sgprOut)))
return module
def computeStoreSrdStart(self, writer, kernel):
module = Module("GSU On computeStoreSrdStart")
module.add(self.computeStoreSrdStartCommon(writer, kernel))
return module
def noLoadLoop(self, writer, kernel, tensorParametersA, tensorParametersB, pack, packPre):
module = Module("GSU On noLoadLoop")
isDTV = (kernel["DirectToVgprA"] or kernel["DirectToVgprB"])
needSecondNLL = isDTV # need 2 NLL for 2 buffers (PGR1/2)
NLLnum = 2 if needSecondNLL else 1
gsuLabel = Label(label=writer.labels.getNameInc("GSU"), comment="")
with writer.allocTmpSgpr(1, tag="GSU On noLoadLoop_tmpSgprGSU") as tmpSgprGSU:
module.add(SAndB32(dst=sgpr(tmpSgprGSU.idx), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
module.add(SCmpEQU32(src0=sgpr(tmpSgprGSU.idx), src1=1, comment="GSU == 1 ?"))
noLoadLoopModules = None
acclen = 0
gsuBackup = kernel["GlobalSplitU"]
gsuAccumBackup = kernel["_GlobalAccumulation"]
bpeCexternalBackup = writer.states.bpeCexternal
kernel["GlobalSplitU"] = 1
kernel["_GlobalAccumulation"] = None
writer.states.bpeCexternal = writer.states.bpeCexternalGSU1
if kernel["KernelLanguage"] == "Assembly" and kernel["OptNoLoadLoop"] and \
kernel["BufferLoad"] and kernel["BufferStore"] and writer.states.doShadowInit and \
kernel["LocalSplitU"]==1 and \
writer.states.actualSummationLoops==1:
# two different noLoadLoops:
# 1. OptNLL & PAP global-read interleaved (only for PAP=ON)
# (2. OptNLL : No PAP global-read (For PAP=OFF, or PAP=ON but the last tile))
# -> this is unified with 1. global-read is invalidated at the last tile.
# 3. OrdinaryNLL (Not Opt.)
noLoadLoopModules = Module("noLoadLoop")
for NLLindex in range(0, NLLnum):
writer.saveLocalPointers(kernel, tensorParametersA, tensorParametersB)
# copy pack
if NLLindex == NLLnum - 1 or (writer.states.packDTVA or writer.states.packDTVB or writer.states.convDTVA or writer.states.convDTVB):
# last NLL or pack DTV case, no deep copy for pack
# pack code for local prefetch is generated in noLoadLoopBody and used for DTV even
deepCopyPack = pack
deepCopyPackPre = packPre
else:
# deepCopy packCode for OptNLL noLoadLoop
deepCopyPack = deepcopy(pack)
deepCopyPackPre = deepcopy(packPre)
noLoadLoopModules.add(writer.noLoadLoop(kernel, tensorParametersA, tensorParametersB, isOptNLL=True, isNGLL=False, pack=deepCopyPack, packPre=deepCopyPackPre, NLLindex=NLLindex, NLLnum=NLLnum))
writer.restoreLocalPointers(kernel, tensorParametersA, tensorParametersB)
acclen = countInstruction(noLoadLoopModules)
kernel["GlobalSplitU"] = gsuBackup
kernel["_GlobalAccumulation"] = gsuAccumBackup
writer.states.bpeCexternal = bpeCexternalBackup
if acclen > 16384:
with writer.allocTmpSgpr(3, tag="GSU On noLoadLoop_tmpSgprInfo") as tmpSgprInfo:
module.add(writer.longBranchScc0(gsuLabel, posNeg=1, tmpSgprInfo=tmpSgprInfo, comment="branch if GSU != 1"))
else:
module.add(SCBranchSCC0(labelName=gsuLabel.getLabelName(), comment="branch if GSU != 1"))
if noLoadLoopModules != None:
module.add(noLoadLoopModules)
module.add(gsuLabel)
return module
def tailLoopNumIter(self, writer, kernel, loopCounter):
module = Module("GSU On tailLoopNumIter")
with writer.allocTmpSgpr(3, tag="GSU On tailLoopNumIter_tmpSgprInfo") as tmpSgprInfo:
tmpSgpr = tmpSgprInfo.idx
remainder = "GSUSumIdx+1" # numIterPerWgRemainder
gsucLabel = Label(label=writer.labels.getNameInc("GSUC_TL"), comment="")
gsucLabelEnd = Label(label=writer.labels.getNameInc("GSUC_TL_End"), comment="")
module.add(SAndB32(dst=sgpr(tmpSgpr), src0=sgpr("GSU"), src1=hex(0x8000), comment="SCC = (GSUC == 1) ?"))
module.add(SCBranchSCC1(labelName=gsucLabel.getLabelName(), comment="branch if GSUC == 1"))
# if GSU numIter=0 if gsuSumIdx != numIterPerWgRemainder
module.add(SCmpLgU32(src0=sgpr("GSUSumIdx"), src1=sgpr("GSUSumIdx+1"), comment="gsuSumIdx == numIterPerWgRemainder"))
module.add(SCMovB32(dst=loopCounter, src=0, comment="numIter=0 if gsuSimIdx != numIterPerWgRemainder"))
module.add(SBranch(gsucLabelEnd.getLabelName()))
module.add(gsucLabel)
# calculate the lastWg
tmpVgpr = writer.vgprPool.checkOut(2, tag="GSUOn_tailLoopNumIter_tmpVgpr")
tmpVgprRes = ContinuousRegister(idx=tmpVgpr, size=2)
module.add(SLShiftRightB32(dst=sgpr(tmpSgpr+1), src=sgpr("SizesSum"), shiftHex=log2(kernel["DepthU"]), \
comment="s%s = s[sgprSizesSum] / %s"%(tmpSgpr+1,kernel["DepthU"])))
module.add(SAndB32(dst=sgpr(tmpSgpr+2), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
module.add(scalarUInt32DivideAndRemainder(tmpSgpr, tmpSgpr+1, tmpSgpr+2, remainder, tmpVgprRes, kernel["WavefrontSize"]))
module.add(SSubU32(dst=sgpr(tmpSgpr+1), src0=sgpr(tmpSgpr+2), src1=1, comment="GSU-1"))
writer.vgprPool.checkIn(tmpVgpr)
module.add(SCmpEQU32(src0=sgpr(tmpSgpr), src1=0, comment="quotient == 0"))
module.add(SCSelectB32(dst=sgpr(tmpSgpr), src0=sgpr("GSUSumIdx+1"), src1=sgpr(tmpSgpr+1), \
comment="lastWg = (quotient==0) ? numIterPerWgRemainder : GSU-1"))
# if GSU numIter=0 if gsuSumIdx != lastWg
module.add(SCmpLgU32(src0=sgpr("GSUSumIdx"), src1=sgpr(tmpSgpr), comment="gsuSumIdx == lastWg"))
module.add(SCMovB32(dst=loopCounter, src=0, comment="numIter=0 if gsuSumIdx != lastWg"))
module.add(gsucLabelEnd)
return module
def setupNewTile(self, writer, kernel, tensorParametersA, tensorParametersB, tPM):
module = Module("GSU On setupNewTile")
addBranch = False
for i in reversed(range(kernel["ProblemType"]["NumIndicesSummation"])):
if i != writer.states.unrollIdx:
addBranch = True
break
if addBranch:
gsuBackup = kernel["GlobalSplitU"]
gsuLabel = Label(label=writer.labels.getNameInc("GSU"), comment="")
gsuLabelEnd = Label(label=writer.labels.getNameInc("GSU_End"), comment="")
with writer.allocTmpSgpr(1, tag="GSU On setupNewTile_tmpSgprGSU") as tmpSgprGSU:
module.add(SAndB32(dst=sgpr(tmpSgprGSU.idx), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
module.add(SCmpEQU32(src0=sgpr(tmpSgprGSU.idx), src1=1, comment="GSU == 1 ?"))
module.add(SCBranchSCC1(labelName=gsuLabel.getLabelName(), comment="branch if GSU == 1"))
module.addComment1("global read addresses: increments a")
kernel["GlobalSplitU"] = 2
module.add(self.graIncrementsAB(writer, kernel, tensorParametersA, tensorParametersB, tPM))
if addBranch:
module.add(SBranch(gsuLabelEnd.getLabelName()))
module.add(gsuLabel)
kernel["GlobalSplitU"] = 1
module.add(self.graIncrementsAB(writer, kernel, tensorParametersA, tensorParametersB, tPM))
kernel["GlobalSplitU"] = gsuBackup
module.add(gsuLabelEnd)
return module
def globalWriteBatchProlog(self, writer, kernel, tmpVgpr, tmpVgprSize, tmpVgprDynamic, \
batchIdx, ss, gwvw, batchElements, \
beta, edge, sumIdxGSUSYNC, addrCalc):
module = Module("GSU On globalWriteBatchProlog")
if kernel["_GlobalAccumulation"] == 'MultipleBufferSingleKernel':
if writer.states.serializedStore:
module.add(SNop(0, "1 wait state required when next inst writes vgprs held by previous dwordx4 store inst"))
return module
def defineAndResources(self, writer, kernel, tmpSgpr0, tmpSgprM, tmpSgprN, tmpSgprNumWG0, tmpSgprAccumTiles):
module = Module("GSU On defineAndResources")
if (kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel" or kernel["AdaptiveGemmGSUA"] == 1):
extReadEpilogueLabeltmp = Label(label=writer.labels.getNameInc("LoadExternalEpilogueStruct"), comment="")
module.addComment0("Check if custom structure pointer is null")
if kernel["ProblemType"]["SupportUserArgs"]:
module.add(SCmpEQU32(src0=sgpr("ArgType"), src1=2, comment="ArgType == 2 ?"))
module.add(SCBranchSCC0(labelName=extReadEpilogueLabeltmp.getLabelName()))
module.add(SMulI32(dst=sgpr(tmpSgpr0), src0=sgpr(tmpSgprAccumTiles), src1="MTOffset", comment="accumNumTiles*(MT0*MT1*bpeC)"))
module.add(SAddU32(dst=sgpr("AddressTD"), src0=sgpr("AddressTD"), src1=sgpr(tmpSgpr0)))
module.add(SAddCU32(dst=sgpr("AddressTD+1"), src0=sgpr("AddressTD+1"), src1=0))
module.add(SAddU32(dst=sgpr("Synchronizer"), src0=sgpr("Synchronizer"), src1=hex(1638400)))
module.add(SAddCU32(dst=sgpr("Synchronizer+1"), src0=sgpr("Synchronizer+1"), src1=0))
module.add(extReadEpilogueLabeltmp)
return module
def writeBiasToGlobal(self, writer, kernel, biasDataType, tP, tmpSgprRes, biasBpe):
module = Module("GSU On writeBiasToGlobal")
if (kernel["GlobalSplitU"] > 1 or kernel["GlobalSplitU"] == -1) and not (kernel["GlobalSplitUAlgorithm"] == "SingleBuffer" and kernel["ProblemType"]["ComputeDataType"] == biasDataType):
'''
We use num_records to save the bias data, so we have to shift the global pointer.
final offset = d_size * gsu + sizeI/J * gsuIdx
'''
assert tmpSgprRes.size >= 4
tmpSgpr = tmpSgprRes.idx
#Calculate tensor 2d size
module.add(SMovB64(dst=sgpr(tmpSgpr+0, 2), src=0x1, comment="Init tensor size"))
indices = [i for i in range(kernel["ProblemType"]["NumIndicesC"])]
numDim = len(indices)
for i in range(0, numDim):
idx = indices[i]
stride = writer.strideRef("D",idx)
size = writer.sizeRef(idx)
module.add(SSubU32(dst=sgpr(tmpSgpr+2), src0=size, src1=0x1, comment="(size-1)"))
module.addModuleAsFlatItems(writer.s_mul_u64_u32(sgpr(tmpSgpr+2), sgpr(tmpSgpr+3), stride, \
sgpr(tmpSgpr+2), comment="stride x (size-1)"))
module.add(SAddU32(dst=sgpr(tmpSgpr+0), src0=sgpr(tmpSgpr+0), src1=sgpr(tmpSgpr+2), comment="sum tensor size"))
module.add(SAddCU32(dst=sgpr(tmpSgpr+1), src0=sgpr(tmpSgpr+1), src1=sgpr(tmpSgpr+3), comment="sum tensor size"))
# SingleBuffer works on the same work space for every gsu
if kernel["_GlobalAccumulation"] == "MultipleBuffer":
module.add(SAndB32(dst=sgpr(tmpSgpr+2), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
module.addModuleAsFlatItems(writer.s_mul_u64_u32(sgpr(tmpSgpr+0), sgpr(tmpSgpr+1), sgpr(tmpSgpr+2), \
sgpr(tmpSgpr+0), comment="Recalculate gsu stride (size * gsu)"))
module.add(SMovB32(dst=sgpr(tmpSgpr+2), src=sgpr("GSUSumIdx"), comment="Init tensor size"))
module.add(SMovB32(dst=sgpr(tmpSgpr+3), src=0x0, comment="Init tensor size"))
module.addModuleAsFlatItems(writer.s_mul_u64_u32(sgpr(tmpSgpr+2), sgpr(tmpSgpr+3), writer.sizeRef(tP["idx"]), \
sgpr(tmpSgpr+2), comment="Reduction GSU offset *stride"))
module.add(SAddU32(dst=sgpr(tmpSgpr+0), src0=sgpr(tmpSgpr+0), src1=sgpr(tmpSgpr+2), comment="sum gsu offset"))
module.add(SAddCU32(dst=sgpr(tmpSgpr+1), src0=sgpr(tmpSgpr+1), src1=sgpr(tmpSgpr+3), comment="sum gsu offset"))
module.add(scalarStaticMultiply64(sgpr(tmpSgpr, 2), sgpr(tmpSgpr, 2), biasBpe, None, comment="stride * bpe"))
module.add(SAddU32(dst=sgpr("SrdBias+0"), src0=sgpr("SrdBias+0"), src1=sgpr(tmpSgpr), comment="Recalculate start address for GSU."))
module.add(SAddCU32(dst=sgpr("SrdBias+1"), src0=sgpr("SrdBias+1"), src1=sgpr(tmpSgpr+1), comment="Recalculate start address for GSU."))
return module
def reductionBranches(self, writer, kernel, tPB, vectorWidths, elements, tmpVgpr, cvtVgprStruct, vectorDataTypes, factorDims, reductionEndLabel, endLabel):
module = Module("GSU On reductionBranches")
edges = [False] # no edge variant
alphas = [False] # no ahpla variant
betas = [False] # no beta variant
edgeI = edges[0]
alpha = alphas[0]
beta = betas[0]
gwvw = vectorWidths[edgeI]
atomic = (kernel["GlobalSplitU"] > 1) and (kernel["_GlobalAccumulation"] != 'MultipleBuffer' and kernel["_GlobalAccumulation"] != 'MultipleBufferSingleKernel')
module.add(self.reductionProcedure(writer, kernel, elements, alpha, beta, edges, atomic, gwvw, tmpVgpr, cvtVgprStruct, vectorDataTypes, reductionEndLabel, endLabel))
return module
def reductionProcedure(self, writer, kernel, elements, alpha, beta, edges, atomic, gwvw, tmpVgpr, cvtVgprStruct, vectorDataTypes, reductionEndLabel, endLabel):
module = Module("GSU Common reductionProcedure")
reductionLabels = {}
for edge in edges:
reductionLabels[edge] = Label(writer.labels.getNameInc("Reduction_B%u_E%u" % (1 if beta else 0, 1 if edge else 0)), comment="")
for edge in edges:
# write label for batch case
module.add(reductionLabels[edge])
# PreLoopVmcntCaseStr = ""
# # not generate Case 2 if StoreCInUnroll with StoreVectorWidth==1 (Case 2 will be same as Case 3)
# if self.canOptimizePreLoopLWVmcnt:
# if edge or (kernel["StoreCInUnroll"] and kernel["StoreVectorWidth"]==1):
# self.currPreLoopVmcntCase = PreLoopVmcntCase.OrdNLL_E1_Store
# else:
# self.currPreLoopVmcntCase = PreLoopVmcntCase.OptNLL_Store
# PreLoopVmcntCaseStr = inst("s_mov_b32", sgpr("PreLoopLWVmcntCase"), hex(self.currPreLoopVmcntCase.value), \
# "for optimizing next PreLoop LW vmcnt, set to Case%u"%self.currPreLoopVmcntCase.value)
# # reset vmcnt if the dict has this key (OptNLL_Store, OrdNLL_E1_Store),
# # OrdNLL_B1_Store is excluded
# if self.currPreLoopVmcntCase in self.preLoopVmcntDict:
# self.preLoopVmcntDict[self.currPreLoopVmcntCase] = 0
edgeI = edge # TODO: remove?
########################################
# Calculate Vgprs for Write Batching
########################################
writer.vgprPool.resetOccupancyLimit()
writer.sgprPool.resetOccupancyLimit()
# Temporarily grow pool for sgpr
sgprList = []
if kernel["_GlobalAccumulation"] == 'MultipleBufferSingleKernel':
sgprList.append(writer.sgprPool.checkOut(1, tag="reductionProcedure_sgprList1", preventOverflow=False))
sgprList.append(writer.sgprPool.checkOut(1, tag="reductionProcedure_sgprList2", preventOverflow=False))
sgprList.append(writer.sgprPool.checkOut(1, tag="reductionProcedure_sgprList3", preventOverflow=False))
sgprList.append(writer.sgprPool.checkOutAligned(2, 2, tag="reductionProcedure_sgprList4", preventOverflow=False))
sgprList.append(writer.sgprPool.checkOutAligned(2, 2, tag="reductionProcedure_sgprList5", preventOverflow=False))
sgprList.append(writer.sgprPool.checkOutAligned(4, 4, tag="reductionProcedure_sgprList6", preventOverflow=False))
for s in sgprList:
writer.sgprPool.checkIn(s)
tmpVgprDynamic = None
tmpVgprDynamicSize = 0
tmpVgprDynamicAlign = 0
if kernel["_GlobalAccumulation"] == 'MultipleBufferSingleKernel':
GSUTotal = writer.getMBSKGSUTotal(kernel)
vgprMbsk = (GSUTotal-1) * gwvw * max(1, kernel["ProblemType"]["DestDataType"].numRegisters())
tmpVgprDynamicSize = vgprMbsk
tmpVgprDynamicAlign = 4
if tmpVgprDynamicSize > 0:
tmpVgprDynamic = ContinuousRegister(idx=writer.vgprPool.checkOutAligned(tmpVgprDynamicSize, tmpVgprDynamicAlign, tag="reductionProcedure_tmpVgprDynamic"), size=tmpVgprDynamicSize)
ss = StoreState(writer, kernel, gwvw, edge, True, atomic, elements[edgeI], vectorDataTypes, dim=0, isWorkspace=True)
# how many vgprs are needed for zero elements
# 2 for addressC in vgpr for addition - already checked out
# 2 for coord0,1 of thread - already checked out
# 2 for tmp - already checked out
# 5 = how many vgprs are needed per element (flat)
# - 2 for addr
# - 3 for GLOBAL_OFFSET_C calculation (can overlap below, therefore max)
# - if beta gwvw*rpe for new value
# - if atomic 2*rpe for old and cmp values
# print("numVgprsPerAddr=%u, numVgprsPerDataPerVI=%u, numVgprPerValuC=%u"%(ss.cfg.numVgprsPerAddr, ss.cfg.numVgprsPerDataPerVI, ss.cfg.numVgprPerValuC))
# numVgprsPerElement = ss.cfg.numVgprPerValuC*gwvw + ss.cfg.numVgprsPerAddr + int(ceil(ss.cfg.numVgprsPerDataPerVI * gwvw))
# if kernel["GroupLoadStore"] and kernel["ProblemType"]["UseBeta"]:
# numVgprsPerElement += ss.cfg.numVgprsPerAddr
# Get estimated numVgprAvailable
# print("Max vgprs =", maxVgprs, writer.vgprPool.size(), writer.vgprPool.availableBlock(ss.numVgprsPerElement, ss.align))
numVgprAvailable = writer.vgprPool.availableBlock(ss.numVgprsPerElement, ss.align)
# Grow the register pool if needed - we need enough regs for at least one element
# Unfortunate since this means the write logic is setting the VGPR requirement
# for the entire kernel but at least we have a functional kernel.
# Before growing the pool, see if we can shrink the write vector width instead?
# TODO: the vgprSerial is needed for-ever and if we grow here will split the
# range of the tmps. Maybe want to move vgprSerial to first vgpr?
# TODO: Minimum elems for StoreRemap
# TODO: Which of DataType or DestDataType is in a better sense? 0114: Check Using DestDataType + HSS
minElements = int(4 / kernel["ProblemType"]["ComputeDataType"].numBytes())
minNeeded = minElements * ss.numVgprsPerElement
gsuDebug = 0
if gsuDebug:
print("numVgprAvailable=", numVgprAvailable, "minElements=", minElements, "minNeeded=", minNeeded)
if numVgprAvailable < minNeeded:
gwvwOrig = gwvw
currentOccupancy = writer.getOccupancy(kernel["NumThreads"], writer.vgprPool.size(), \
writer.sgprPool.size(), writer.getLdsSize(kernel), writer.agprPool.size(), writer.states.doubleVgpr)
futureOccupancy = writer.getOccupancy(kernel["NumThreads"], writer.vgprPool.size() - numVgprAvailable + minNeeded, \
writer.sgprPool.size(), writer.getLdsSize(kernel), writer.agprPool.size(), writer.states.doubleVgpr)
if gsuDebug:
print("currentOccupancy=%u futureOccupancy=%u VGPRs=%u numVgprAvail=%u vgprPerElem=%u" \
% (currentOccupancy, futureOccupancy, writer.vgprPool.size(), \
numVgprAvailable, minNeeded))
if futureOccupancy > currentOccupancy:
if gsuDebug:
print("warning: %s growing VGPR for GlobalWrite batching - this may bloat VGPR usage" % \
(writer.states.kernelName))
print(" numVgprAvailable=", numVgprAvailable, \
"numVgprsPerElement=", ss.numVgprsPerElement, \
"gwvw=", gwvw)
elif gwvw != gwvwOrig:
ss.gwvw = gwvw # make both representations consistent
if gsuDebug:
print2("info: %s shrank gwvw from %u to %u but kept occupancy same=%u." \
% (writer.states.kernelName, gwvwOrig, gwvw, currentOccupancy))
if numVgprAvailable < minNeeded:
print2("info: growing pool += %d * %d for GlobalWrite\n" \
% (minElements,ss.numVgprsPerElement))
print2(writer.vgprPool.state())
writer.vgprPool.growPool(0, minElements, ss.numVgprsPerElement, \
"grow-pool for GlobalWrite")
numVgprAvailable = writer.vgprPool.available()
print2(writer.vgprPool.state())
# Use VGPR up to next occupancy threshold:
maxVgprs, occupancy = writer.getMaxRegsForOccupancy(kernel["NumThreads"], writer.vgprPool.size(), writer.sgprPool.size(), \
writer.getLdsSize(kernel), writer.agprPool.size(), writer.states.doubleVgpr)
# Set occupancy limit for register pools
# TODO: Support gfx12
if kernel["ISA"][0] != 12:
writer.vgprPool.setOccupancyLimit(writer.states.regCaps["MaxVgpr"], writer.states.regCaps["PhysicalMaxVgpr"] // occupancy)
writer.sgprPool.setOccupancyLimit(writer.states.regCaps["MaxSgpr"], writer.states.regCaps["PhysicalMaxSgpr"] // occupancy)
if ss.numVgprsPerElement:
numElementsPerBatch = numVgprAvailable // ss.numVgprsPerElement
else:
numElementsPerBatch = len(elements[edgeI]) # max, do 'em all
# assert(self.numVgprValuC % gwvw == 0) # sanity check
numElementsPerBatch = numElementsPerBatch if not kernel["NumElementsPerBatchStore"] else min(kernel["NumElementsPerBatchStore"],numElementsPerBatch)
if gsuDebug:
print("NumElementsPerBatch=", numElementsPerBatch, "LimitedBySgprs=", ss.cfg.numElementsPerBatchLimitedBySgprs, \
"WARNING" if ss.cfg.numElementsPerBatchLimitedBySgprs < numElementsPerBatch else "okay")
if ss.cfg.numElementsPerBatchLimitedBySgprs < numElementsPerBatch:
numElementsPerBatch = ss.cfg.numElementsPerBatchLimitedBySgprs
# TODO: Which of DataType or DestDataType is in a better sense? 0114: Check Using DestDataType + HSS
if (kernel["ProblemType"]["DataType"].isHalf() or kernel["ProblemType"]["DataType"].isBFloat16()):
# only do an even number of halves - since these share hi/lo pieces of some registers?
if numElementsPerBatch > 1:
numElementsPerBatch = int(numElementsPerBatch/2)*2
elif not kernel["EnableMatrixInstruction"]:
# (excluding MFMA+LSU case. It can work without an issue)
# The globalWriteBatch routine below can't handle odd elements per batch
# and 0 elements per batch is illegal.
# so if we don't have *GPR resources to handle a larger batch then need
# to mark overflowedResources rather than generate a kernel that won't work.
# It might be possible to fix globalWriteBatch to handle this case but these
# are likely to be low-performing so likely not worth optimizing.
print("WARNING: half requires at least two elements per batch")
self.overflowedResources = 3
# elif kernel["ProblemType"]["MacDataTypeA"].is8bitFloat():
# if numElementsPerBatch > 1:
# numElementsPerBatch = int(numElementsPerBatch/4)*4
assert numElementsPerBatch > 0, "numElementsPerBatch=0 for %s"%writer.states.kernelName
# if no atomics and no edge, then write whole vectors
# if not atomic and not edge:
# numVectorsPerBatch = numElementsPerBatch / kernel["GlobalWriteVectorWidth"]
# #print " NumVectorsPerBatch", numVectorsPerBatch
# numElementsPerBatch = numVectorsPerBatch * kernel["GlobalWriteVectorWidth"]
numBatches = max(1, ceilDivide(len(elements[edgeI]),numElementsPerBatch))
numSgprs = ss.cfg.numTempSgprPerBatch + ss.cfg.numMaskSgprPerBatch + ss.cfg.numMaskSgprPerElement * numElementsPerBatch
if writer.db["PrintStoreRegisterDb"]:
print("edgeI", edgeI, "NumBatches", numBatches, "NumElementsPerBatch", numElementsPerBatch, "numVgprsPerElement", ss.numVgprsPerElement, "len(elements[edgeI])", len(elements[edgeI]))
print ("numSgprs=", numSgprs, "sgprPool.size()=", writer.sgprPool.size(), "numTempSgprPerBatch=", ss.cfg.numTempSgprPerBatch,
"numMaskSgprPerBatch=", ss.cfg.numMaskSgprPerBatch, "numMaskSgprPerElement=", ss.cfg.numMaskSgprPerElement)
print(writer.sgprPool.state())
module.addComment1("edge=%d, allocate %u sgpr. perBatchTmpS=%u perBatchMaskS=%u perElementMaskS=%u elementsPerBatch=%u" %
(edgeI, numSgprs, ss.cfg.numTempSgprPerBatch, ss.cfg.numMaskSgprPerBatch, ss.cfg.numMaskSgprPerElement, numElementsPerBatch))
# GSU code below needs both tmpSgpr.idx (storeOffsetSgpr) and
# tmpSgpr.idx+1 (loadOffsetSgpr), so reserve at least 2 sgprs.
allocNumSgprs = max(numSgprs, 2)
with writer.allocTmpSgpr(allocNumSgprs, 2, tag="GSU On globalWriteBatch_tmpSgpr") as tmpSgpr:
elementSgprs = tmpSgpr.idx + ss.cfg.numTempSgprPerBatch
codeAccVgprRead = deepcopy(writer.codes.accVgprRead) if writer.states.serializedStore else None
codeAccVgprWrite = deepcopy(writer.codes.accVgprWrite) if writer.states.serializedStore else None
codeAccVgprReadBackup = deepcopy(codeAccVgprRead)
if kernel["MIArchVgpr"] and alpha and not kernel["_GlobalAccumulation"] == 'MultipleBufferSingleKernel':
codeAccVgprRead = None
# Only apply when 2 wave optimization features are enabled
if (kernel["StorePriorityOpt"] or kernel["StoreSyncOpt"]) and beta:
self.alphaBeforeLoadC = True
# calculate address in workspace
module.add(self.computeWorkspaceSrd(writer, kernel, tmpSgpr))
lastGsuWgBusyWaitingLabel = Label(writer.labels.getNameInc("last_gsu_wg_busy_waiting"), comment="")
reductionBodyLabel = Label(writer.labels.getNameInc("reduction_body"), comment="")
partialWriteLabel = Label(writer.labels.getNameInc("partial_write"), comment="")
module.add(self.syncOffsetPreparation(writer, kernel, tmpSgpr, partialWriteLabel))
for batchIdx in range(0, numBatches):
elementStartIdx = batchIdx * numElementsPerBatch
elementStopIdx = min(elementStartIdx + numElementsPerBatch, len(elements[edgeI]))
elementsThisBatch = elements[edgeI][elementStartIdx:elementStopIdx]
# print("BATCH[%u/%u]: elements[edgeI][%u:%u] VGPRs=%u" % (batchIdx, numBatches, elementStartIdx, elementStopIdx, ss.numVgprsPerElement ))
# elementVgprs can be large and should be perfectly tuned to the number of available
# VGPRS. We do not want to accidentally overflow and grow the pool here:
module.add(self.partialWriteBatch(writer, kernel, ss, batchIdx, beta, edge, gwvw, elementsThisBatch, tmpVgpr, elementSgprs, \
tmpSgpr, codeAccVgprRead, lastGsuWgBusyWaitingLabel, reductionBodyLabel, partialWriteLabel))
# synchronize GSUWG in reductionBatch. If is the last WG -> do reduction; else branch to GW_END
ss.firstBatch = True
for batchIdx in range(0, numBatches):
elementStartIdx = batchIdx * numElementsPerBatch
elementStopIdx = min(elementStartIdx + numElementsPerBatch, len(elements[edgeI]))
elementsThisBatch = elements[edgeI][elementStartIdx:elementStopIdx]
# print("BATCH[%u/%u]: elements[edgeI][%u:%u] VGPRs=%u" % (batchIdx, numBatches, elementStartIdx, elementStopIdx, ss.numVgprsPerElement ))
# elementVgprs can be large and should be perfectly tuned to the number of available
# VGPRS. We do not want to accidentally overflow and grow the pool here:
module.add(self.reductionBatch(writer, kernel, ss, batchIdx, alpha, beta, edge, atomic, \
gwvw, elementsThisBatch, tmpVgpr, tmpVgprDynamic, elementSgprs, tmpSgpr, \
codeAccVgprReadBackup, codeAccVgprWrite, reductionBodyLabel, endLabel))
module.add(SWaitCnt(vlcnt=0, comment="wait for buffer_load to finish"))
if kernel["MbskPrefetchMethod"] == 0:
module.add(SAndB32(dst=sgpr(tmpSgpr.idx), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
module.add(SCmpGtI32(src0=sgpr(tmpSgpr.idx), src1=self.gsuThreshold, comment="GSU > %u ?" % self.gsuThreshold))
module.add(SCBranchSCC1(labelName=reductionEndLabel.getLabelName(), comment="branch if true"))
module.addComment("GSU > %u, no need to reset synchronizer" % self.gsuThreshold)
module.add(SWaitCnt(kmcnt=0, comment="wait for reset synchronizer to finish"))
ss.resetState()
# Free after final vgpr vcalculation
if tmpVgprDynamic:
writer.vgprPool.checkIn(tmpVgprDynamic.idx)
return module
def syncOffsetPreparation(self, writer, kernel, tmpSgpr, partialWriteLabel):
module = Module("GSU Common syncOffsetPreparation")
tmpS01 = writer.sgprPool.checkOut(1, tag="syncOffsetPreparation_tmpS01", preventOverflow=False)
tmpS02 = writer.sgprPool.checkOut(1, tag="syncOffsetPreparation_tmpS02", preventOverflow=False)
tmpS03 = writer.sgprPool.checkOut(1, tag="syncOffsetPreparation_tmpS03", preventOverflow=False)
#####################################synchronizer offset cal and set synchronizer#####################################
#####################################WaveId+WgId*WaveNum+WgNum*WaveNum*Batch#####################################
#####################################WgId+WaveId*WgNum+WgNum*WaveNum*Batch#####################################
module.addComment("synchronizer offset cal")
module.add(SMulI32(dst=sgpr(tmpS03), src0=sgpr("NumWorkGroups1"), src1=sgpr("NumWorkGroups0"), comment=""))
module.add(SMulI32(dst=sgpr(tmpS02), src0=sgpr(tmpS03), src1=sgpr("WorkGroup2"), comment=""))
module.add(SMulI32(dst=sgpr(tmpS01), src0=sgpr("WorkGroup1"), src1=sgpr("NumWorkGroups0"), comment=""))
module.add(SAddU32(dst=sgpr(tmpS01), src0=sgpr(tmpS01), src1=sgpr("WorkGroup0")))
module.add(SAddU32(dst=sgpr(tmpS01), src0=sgpr(tmpS01), src1=sgpr(tmpS02)))
module.add(VReadfirstlaneB32(dst=sgpr(tmpS02), src=vgpr("Serial")))
module.add(SMulI32(dst=sgpr(tmpS03), src0=sgpr(tmpS03), src1=sgpr("SizeK"), comment="cal a wave offset"))
module.add(SLShiftRightB32(dst=sgpr(tmpS02), shiftHex=hex(log2(kernel["WavefrontSize"])), src=sgpr(tmpS02)))
module.add(SMulI32(dst=sgpr(tmpS02), src0=sgpr(tmpS03), src1=sgpr(tmpS02), comment="wave offset at batch")) # WaveId*WgNum
module.add(SAddU32(dst=sgpr(tmpS01), src0=sgpr(tmpS02), src1=sgpr(tmpS01))) # WaveId*WgNum+WgId
module.add(SLShiftLeftB32(dst=sgpr(tmpS01), src=sgpr(tmpS01), shiftHex=hex(2), comment="")) # atomic 32bits
#####################################set synchronizer#####################################
module.add(SAddU32(dst=sgpr("SrdSync+0"), src0=sgpr("Synchronizer+0"), src1=sgpr(tmpS01), comment="" ))
module.add(SAddCU32(dst=sgpr("SrdSync+1"), src0=sgpr("Synchronizer+1"), src1=hex(0), comment="" ))
if kernel["MbskPrefetchMethod"] == 0:
module.addSpaceLine()
module.add(SAndB32(dst=sgpr(tmpS02), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
module.add(SCmpGtI32(src0=sgpr(tmpS02), src1=self.gsuThreshold, comment="GSU > %u ?" % self.gsuThreshold))
module.add(SCBranchSCC1(labelName=partialWriteLabel.getLabelName(), comment="branch if true"))
module.addComment("GSU <= %u, last gsu wg do the reduction" % self.gsuThreshold)
module.add(SSubU32(dst=sgpr(tmpS02), src0=sgpr(tmpS02), src1=1, comment=""))
module.add(SCmpEQU32(src0=sgpr("GSUSumIdx"), src1=sgpr(tmpS02), comment="GSUSumIdx == GSU-1 ?"))
module.add(SCBranchSCC0(labelName=partialWriteLabel.getLabelName(), comment="branch if false"))
writer.sgprPool.checkIn(tmpS03)
writer.sgprPool.checkIn(tmpS02)
writer.sgprPool.checkIn(tmpS01)
return module
def lastGsuWgBusyWaiting(self, writer, kernel, ss, tmpSgpr, tmpVgpr, lastGsuWgBusyWaitingLabel, reductionBodyLabel):
module = Module("GSU Common lastGsuWgBusyWaiting")
tmpS01 = tmpSgpr.idx
if writer.states.asmCaps["HasScalarStore"]:
module.add(SLoadB32(dst=sgpr(tmpS01), base=sgpr("SrdSync",2), soffset=0, smem=SMEMModifiers(glc=True), comment="get atomic_dec value"))
module.add(SWaitCnt(kmcnt=0, comment="wait for atomic_dec value load"))
module.add(SCmpEQU32(src0=sgpr(tmpS01), src1=1, comment="last GSU WG?"))
module.add(SCBranchSCC0(labelName=lastGsuWgBusyWaitingLabel.getLabelName(), comment="branch if false"))
module.add(SMovB32(dst=sgpr(tmpS01), src=0, comment="reset synchronizer"))
module.add(SStoreB32(src=sgpr(tmpS01), base=sgpr("SrdSync", 2), soffset=0, smem=SMEMModifiers(glc=True), comment="reset synchronizer"))
else:
# Poll+clear via the same atomic channel as the producer's atomic_dec: a plain
# buffer load/store races with it on gfx1250's CU-partitioned L2. atomic_dec(wrap=0)
# always writes 0 and returns old, so it atomically reads-and-resets; spin until old==1.
addrVgpr = writer.vgprPool.checkOutAligned(2, 2, "syncDecAddr")
dataVgpr = writer.vgprPool.checkOut(1, "syncDecData") # wrap value = 0
dstVgpr = writer.vgprPool.checkOut(1, "syncDecDst") # atomic return (old)
# EXEC is wave-width: 1 sgpr on wave32, 2 on wave64
numExecSgpr = 1 if kernel["WavefrontSize"] == 32 else 2
SMovExec = SMovB32 if numExecSgpr == 1 else SMovB64
execSgpr = writer.sgprPool.checkOutAligned(numExecSgpr, numExecSgpr, preventOverflow=False)
module.add(VMovB64(dst=vgpr(addrVgpr, 2), src=sgpr("SrdSync", 2), comment="synchronizer atomic address"))
module.add(VMovB32(dst=vgpr(dataVgpr), src=0, comment="atomic_dec wrap value = 0 (forces slot to 0)"))
module.add(SMovExec(dst=sgpr(execSgpr, numExecSgpr), src=EXEC(), comment="save EXEC"))
module.add(SMovExec(dst=EXEC(), src=1, comment="only lane 0 active"))
atomicFlat = FLATModifiers(scope=CacheScope.SCOPE_DEV) \
if writer.states.archCaps["DefaultScopeIsCULocal"] else None
innerSpinLabel = Label(writer.labels.getNameInc("last_gsu_wg_busy_waiting_inner"), "")
module.add(innerSpinLabel)
if writer.states.archCaps["RequiresXCntForVolatileVMEM"]:
module.add(SWaitXCnt(xcnt=0, comment="drain in-flight VMEM before flat atomic"))
module.add(FlatAtomicDecU32(dst=vgpr(dstVgpr), addr=vgpr(addrVgpr, 2), data=vgpr(dataVgpr), \
modifier=atomicFlat, \
comment="atomic read+reset synchronizer (wrap=0 -> slot:=0)"))
module.add(SWaitCnt(vlcnt=0, comment="wait for atomic return"))
module.add(VReadfirstlaneB32(dst=sgpr(tmpS01), src=vgpr(dstVgpr), comment="read old synchronizer value"))
module.add(SCmpEQU32(src0=sgpr(tmpS01), src1=1, comment="last GSU WG? (producer signalled)"))
module.add(SCBranchSCC0(labelName=innerSpinLabel.getLabelName(), comment="branch if false (retry)"))
module.add(SMovExec(dst=EXEC(), src=sgpr(execSgpr, numExecSgpr), comment="restore EXEC"))
writer.vgprPool.checkIn(addrVgpr)
writer.vgprPool.checkIn(dataVgpr)
writer.vgprPool.checkIn(dstVgpr)
writer.sgprPool.checkIn(execSgpr)
module.add(SBranch(labelName=reductionBodyLabel.getLabelName(), comment=""))
return module
def partialWriteBatch(self, writer, kernel, ss, batchIdx, beta, edge, gwvw, batchElements, tmpVgpr, batchElementSgprs, tmpSgpr, codeAccVgprRead, \
lastGsuWgBusyWaitingLabel, reductionBodyLabel, partialWriteLabel):
module = Module("GSU Common partialWriteBatch")
# allow expanding vgpr pool for OptNLL
# preventOverflow = True #(not isOptNLL)
# ss.setupStoreElementsForBatch(kernel, gwvw, batchElements, batchElementSgprs, isOptNLL=False, factorDim=0, isWorkspace=True)
ss.setupStoreElementsForBatchWihoutVgprCheckOut(kernel, gwvw, batchElements, batchElementSgprs, isOptNLL=True, factorDim=0, isWorkspace=True)
if batchIdx == 0 and kernel["MbskPrefetchMethod"] == 0:
module.add(lastGsuWgBusyWaitingLabel)
module.add(self.lastGsuWgBusyWaiting(writer, kernel, ss, tmpSgpr, tmpVgpr, lastGsuWgBusyWaitingLabel, reductionBodyLabel))
module.add(partialWriteLabel)
module.addComment0("optSingleColVgpr=%u optSharedColVgpr=%u optSGPRUsage=%s optSrdIncForRow=%u" % \
(ss.optSingleColVgpr, ss.optSharedColVgpr, ss.optSGPRUsage, ss.optSrdIncForRow))
if kernel["StoreSyncOpt"]:
module.add(SSleep(kernel["StoreSyncOpt"] - 1, "optimization: sync and wait"))
module.add(SBarrier())
# comment tt1, tt0, vc1, vc0
# tt = thread tile, vc=vector component
commentStr = "Partial Write%s%s Batch #%u (d1,d0,vc1,vc0) =\n " \
% (" Beta" if beta else "", " Edge" if edge else "", batchIdx)
for elementIdx, element in enumerate(batchElements):
commentStr += "(%u,%u,%u,%u:vw%u)" % (element[0], element[1], element[2], element[3], gwvw)
if elementIdx < len(batchElements)-1:
commentStr += "; "
module.addComment2(commentStr)
storeOffsetSgpr = tmpSgpr.idx
loadOffsetSgpr = tmpSgpr.idx + 1
storeOffsetSgprRes = ContinuousRegister(storeOffsetSgpr, 1)
########################################
# On input, coord0 and coord1 are VGPRs computed in the pre-batch code, based
# on the thread and tid number. These are ELEMENT offsets from start of tensor C
# for the top-left corner this thread will write. These are not changed
# across all the store loop iters.
if writer.db["ConservativeWaitCnt"] & 0x10:
module.add(SBarrier(comment="debug"))
module.add(SWaitCnt(vlcnt=0, vscnt=0, comment="ConservativeWaitCnt"))
module.add(SBarrier(comment="debug"))
if not edge and writer.db["ForceEdgeStores"]>=2:
module.add(writer.getBomb()) # should not get here
if edge and writer.db["AssertNoEdge"]:
module.add(writer.getBomb()) # should not get here
if kernel["BufferStore"] and edge:
bufferOOB = tmpVgpr.idx + tmpVgpr.size - 1
module.add(VMovB32(dst=vgpr(bufferOOB), src="BufferOOB"))
else:
bufferOOB = None
if beta and kernel["StoreSyncOpt"]:
module.add(SSleep(kernel["StoreSyncOpt"] - 1, "optimization: sync and wait"))
module.add(SBarrier())
########################################
# accvgpr read
module.addComment("accvgpr read")
if batchIdx > 0:
module.add(SWaitCnt(vlcnt=0, vscnt=0, comment="Wait previous batch write over"))
if codeAccVgprRead is not None and kernel["LocalSplitU"] == 1:
regsPerScalar = writer.states.bpeCinternal // writer.states.bpr # register per scalar
# loop over store instructions within one batch
for elementIdx in range(0, len(batchElements)):
# loop over scalars within one store instruction
for vi in range(0, gwvw):
# loop over registers within one scalar
for rIdx in range(0, regsPerScalar):
codeAccVgprReadInst = codeAccVgprRead.popFirstItem() # v_accvgpr_read_b32
codeAccVgprReadInst.dst = vgpr(ss.elementSumIdx[elementIdx]*regsPerScalar + regsPerScalar*vi + rIdx)
module.add(codeAccVgprReadInst)
elif kernel["LocalSplitU"] > 1:
# read from LSU VGPRs
regsPerScalar = writer.states.bpeCinternal // writer.states.bpr # register per scalar
if kernel["MIArchVgpr"]:
tmpStartVgprValuC = writer.states.c.startVgprValu
writer.states.c.startVgprValu = 0
module.add(RegSet("v", "vgprValuC", 0))
if ss.lsuStartVgprOffset >= 0:
for elementIdx in range(0, len(batchElements)):
for vi in range(0, gwvw):
for rIdx in range(0, regsPerScalar):
codeAccVgprReadInst = codeAccVgprRead.popFirstItem() # v_mov_b32
codeAccVgprReadInst.dst = vgpr(ss.elementSumIdx[elementIdx]*regsPerScalar + regsPerScalar*vi + rIdx)
module.add(codeAccVgprReadInst)
ss.lsuStartVgprOffset += len(batchElements) * gwvw * regsPerScalar
if kernel["MIArchVgpr"]:
writer.states.c.startVgprValu = tmpStartVgprValuC
module.add(RegSet("v", "vgprValuC", tmpStartVgprValuC))
else:
module.add(SNop(1, "2 wait states required before reading vgpr"))
module.addSpaceLine()
########################################
# Write to workspace
module.addComment("write to workspace")
if kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel":
storeCodeGSUSK = Module("GroupLoadStore")
storeWidth = kernel["StoreVectorWidth"]
bps = kernel["StoreVectorWidth"] * writer.states.bpeCinternal
rpv = bps / writer.states.bpr
isGlc = True
isSlc = True
isNT = False #bool(kernel["NonTemporalD"] & 0x4)
useBuffer = kernel["BufferStore"]
prevAddrVgpr = None
for elementIdx in range(0, len(batchElements)):
addrCalc = ss.elementAddr[elementIdx]
data = ss.elementData[elementIdx]
addrDVgpr = addrCalc.addrDVgpr
if useBuffer:
addr0 = vgpr(addrDVgpr)
addr1 = sgpr("SrdD", 4)
else:
addr0 = vgpr(addrDVgpr, 2)
addr1 = ""
globalOffset = 0
# For buffer stores, only batch 0 / element 0 initializes the sgpr offset;
# subsequent batches keep incrementing. For flat stores, prevAddrVgpr is
# local to partialWriteBatch and resets each call, so every first element
# must recompute the base address.
if (batchIdx == 0 and elementIdx == 0) or (not useBuffer and elementIdx == 0):
storeCodeGSUSK.add(vectorStaticMultiply(vgpr(addrDVgpr), vgpr("Serial"), storeWidth * writer.states.bpeCinternal, storeOffsetSgprRes))
if useBuffer:
storeCodeGSUSK.add(SMovB32(dst=sgpr(storeOffsetSgpr), src=0, comment="Init sgpr offset for interleaved wave store"))
else:
# Flat store: build full 64-bit workspace address
storeCodeGSUSK.add(VMovB32(dst=vgpr(addrDVgpr+1), src=0, comment="zero hi addr bits"))
storeCodeGSUSK.add(VAddCOU32(dst=vgpr(addrDVgpr), dst1=VCC(), src0=sgpr("AddressD+0"), src1=vgpr(addrDVgpr), comment="add WS base lo"))
storeCodeGSUSK.add(VAddCCOU32(dst=vgpr(addrDVgpr+1), dst1=VCC(), src0=sgpr("AddressD+1"), src1=vgpr(addrDVgpr+1), src2=VCC(), comment="add WS base hi"))
else:
# Use "NumThreads" instead of "MIWaveGroup" because LSU will not show in "MIWaveGroup"
increment = kernel["NumThreads"] * storeWidth * writer.states.bpeCinternal
if useBuffer:
storeCodeGSUSK.add(SAddU32(dst=sgpr(storeOffsetSgpr), src0=sgpr(storeOffsetSgpr), src1=increment, comment="Increase sgpr offset for store"))
else:
# Flat store: compute address from previous element's addr + increment
storeCodeGSUSK.add(VAddCOU32(dst=vgpr(addrDVgpr), dst1=VCC(), src0=increment, src1=vgpr(prevAddrVgpr), comment="Inc flat WS addr lo"))
storeCodeGSUSK.add(VAddCCOU32(dst=vgpr(addrDVgpr+1), dst1=VCC(), src0=0, src1=vgpr(prevAddrVgpr+1), src2=VCC(), comment="Inc flat WS addr hi"))
prevAddrVgpr = addrDVgpr
sumIdx = ss.elementSumIdx[elementIdx]
if not kernel["StoreRemapVectorWidth"]:
# Only GSU>1 MBSK write to workspace (GSU1 MBSK will write to output buffer)
# so we need wsOffset to coalesced store to workspace buffer
wsOffset = sgpr(storeOffsetSgpr) if useBuffer else None
wsScope = CacheScope.SCOPE_DEV \
if writer.states.archCaps["DefaultScopeIsCULocal"] else CacheScope.SCOPE_NONE
storeCodeGSUSK.add(writer.chooseGlobalWrite(useBuffer, bps, sumIdx, rpv, \
addr0, addr1, globalOffset, soffset=wsOffset, \
glc=isGlc, slc=isSlc, nt=isNT, hi16=0, scope=wsScope, comment="store WS"))
else:
rpe = writer.states.bpeCinternal // writer.states.bpr
storeCodeGSUSK.add(writer.storeRemapAddLocalWrite(kernel, ss, addrCalc, sumIdx*rpe))
# Column Block Shape has been written to LDS
# Now read back and write out to global memory
module.add(storeCodeGSUSK)
# return registers to pool:
lastDataD = -1
for elementIdx in range(0, len(batchElements)):
if not ss.sharedColDVgprs:
addrCalc: AddrCalculation = ss.elementAddr[elementIdx]
addrDVgpr = addrCalc.addrDVgpr
addrGSUSyncVgprs = addrCalc.addrGSUSyncVgprs
addrCVgpr = addrCalc.addrCVgpr
writer.vgprPool.checkIn(addrDVgpr)
if addrCVgpr != addrDVgpr:
writer.vgprPool.checkIn(addrCVgpr)
if addrGSUSyncVgprs != None:
writer.vgprPool.checkIn(addrGSUSyncVgprs)
data = ss.elementData[elementIdx]
if data != 0:
if data != lastDataD:
writer.vgprPool.checkIn(data)
lastDataD = data
ss.firstBatch = False
ss.checkInTempVgprC()
return module
def reductionBatch(self, writer, kernel, ss, batchIdx, alpha, beta, edge, atomic, gwvw, batchElements, tmpVgpr, tmpVgprDynamic, \
batchElementSgprs, tmpSgpr, codeAccVgprRead, codeAccVgprWrite, reductionBodyLabel, endLabel):
module = Module("GSU Common reductionBatch")
module.addComment0("optSingleColVgpr=%u optSharedColVgpr=%u optSGPRUsage=%s optSrdIncForRow=%u" % \
(ss.optSingleColVgpr, ss.optSharedColVgpr, ss.optSGPRUsage, ss.optSrdIncForRow))
if kernel["StoreSyncOpt"]:
module.add(SSleep(kernel["StoreSyncOpt"] - 1, "optimization: sync and wait"))
module.add(SBarrier())
# comment tt1, tt0, vc1, vc0
# tt = thread tile, vc=vector component
commentStr = "Reduction%s%s Batch #%u (d1,d0,vc1,vc0) =\n " \
% (" Beta" if beta else "", " Edge" if edge else "", batchIdx)
for elementIdx, element in enumerate(batchElements):
commentStr += "(%u,%u,%u,%u:vw%u)" % (element[0], element[1], element[2], element[3], gwvw)
if elementIdx < len(batchElements)-1:
commentStr += "; "
module.addComment2(commentStr)
# allow expanding vgpr pool for OptNLL
# preventOverflow = True #(not isOptNLL)
# ss.setupStoreElementsForBatch(kernel, gwvw, batchElements, batchElementSgprs, isOptNLL=False, factorDim=0, isWorkspace=True)
ss.setupStoreElementsForBatchWihoutVgprCheckOut(kernel, gwvw, batchElements, batchElementSgprs, isOptNLL=True, factorDim=0, isWorkspace=True)
########################################
# On input, coord0 and coord1 are VGPRs computed in the pre-batch code, based
# on the thread and tid number. These are ELEMENT offsets from start of tensor C
# for the top-left corner this thread will write. These are not changed
# across all the store loop iters.
if writer.db["ConservativeWaitCnt"] & 0x10:
module.add(SBarrier(comment="debug"))
module.add(SWaitCnt(vlcnt=0, vscnt=0, comment="ConservativeWaitCnt"))
module.add(SBarrier(comment="debug"))
if not edge and writer.db["ForceEdgeStores"]>=2:
module.add(writer.getBomb()) # should not get here
if edge and writer.db["AssertNoEdge"]:
module.add(writer.getBomb()) # should not get here
if kernel["BufferStore"] and edge:
bufferOOB = tmpVgpr.idx + tmpVgpr.size - 1
module.add(VMovB32(dst=vgpr(bufferOOB), src="BufferOOB"))
else:
bufferOOB = None
if beta and kernel["StoreSyncOpt"]:
module.add(SSleep(kernel["StoreSyncOpt"] - 1, "optimization: sync and wait"))
module.add(SBarrier())
# do we really need this?
sumIdxGSUSYNC = ss.elementSumIdx[len(batchElements)-1]
addrCalc = ss.elementAddr[len(batchElements)-1]
accvgprWriteLabel = Label(writer.labels.getNameInc("accvgpr_write"), comment="")
if (kernel["_GlobalAccumulation"] == 'MultipleBufferSingleKernel'):
module.add(self.GSUSynccodegenOpt(kernel, writer, ss, batchIdx, tmpSgpr, tmpVgpr, tmpVgprDynamic, gwvw, batchElements,\
endLabel, sumIdxGSUSYNC, addrCalc.addrDVgpr, reductionBodyLabel))
module.addComment("synchronizer store end")
if kernel["MbskPrefetchMethod"] == 0:
module.add(SAndB32(dst=sgpr(tmpSgpr.idx), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
module.add(SCmpGtI32(src0=sgpr(tmpSgpr.idx), src1=self.gsuThreshold, comment="GSU > %u ?" % self.gsuThreshold))
module.add(SCBranchSCC1(labelName=accvgprWriteLabel.getLabelName(), comment="branch if true"))
module.addComment("GSU <= %u, do accvgpr_read for the last gsu wg" % self.gsuThreshold)
module.add(self.lastGsuWgReduction(kernel, writer, ss, batchIdx, tmpVgpr, tmpVgprDynamic, gwvw, batchElements, \
codeAccVgprRead, addrCalc.globalOffset, addrCalc.addrDVgpr))
########################################
# accvgpr write
module.add(accvgprWriteLabel)
module.addComment("accvgpr write")
if codeAccVgprWrite is not None:
regsPerScalar = writer.states.bpeCinternal // writer.states.bpr # register per scalar
if kernel["MIArchVgpr"] and kernel["LocalSplitU"] > 1:
tmpStartVgprValuC = writer.states.c.startVgprValu
writer.states.c.startVgprValu = 0
module.add(RegSet("v", "vgprValuC", 0))
# loop over store instructions within one batch
for elementIdx in range(0, len(batchElements)):
# loop over scalars within one store instruction
for vi in range(0, gwvw):
# loop over registers within one scalar
for rIdx in range(0, regsPerScalar):
codeAccVgprWriteInst = codeAccVgprWrite.popFirstItem()
codeAccVgprWriteInst.srcs[0] = vgpr(ss.elementSumIdx[elementIdx]*regsPerScalar + regsPerScalar*vi + rIdx)
module.add(codeAccVgprWriteInst)
# TODO: need 3 wait states if read accvgpr after write accvgpr?
# if not kernel["MIArchVgpr"]:
# module.add(SNop(1, "2 wait states required before reading vgpr"))
if kernel["MIArchVgpr"] and kernel["LocalSplitU"] > 1:
writer.states.c.startVgprValu = tmpStartVgprValuC
module.add(RegSet("v", "vgprValuC", tmpStartVgprValuC))
if edge and (not kernel["BufferStore"]): # atomic or
# subsequent batch must start with full exec mask
# BufferStore doesn't need exec since it used buffer range checking when
# possible
module.add(self.getEdgeMovInstType()(EXEC(), -1, "full mask -> exec"))
if writer.db["ConservativeWaitCnt"] & 0x40:
module.add(SBarrier(comment="debug"))
module.add(SWaitCnt(vlcnt=0, vscnt=0, comment="ConservativeWaitCnt"))
module.add(SBarrier(comment="debug"))
# return registers to pool:
lastDataD = -1
for elementIdx in range(0, len(batchElements)):
if not ss.sharedColDVgprs:
addrCalc: AddrCalculation = ss.elementAddr[elementIdx]
addrDVgpr = addrCalc.addrDVgpr
addrGSUSyncVgprs = addrCalc.addrGSUSyncVgprs
addrCVgpr = addrCalc.addrCVgpr
writer.vgprPool.checkIn(addrDVgpr)
if addrCVgpr != addrDVgpr:
writer.vgprPool.checkIn(addrCVgpr)
if addrGSUSyncVgprs != None:
writer.vgprPool.checkIn(addrGSUSyncVgprs)
data = ss.elementData[elementIdx]
if data != 0:
if data != lastDataD:
writer.vgprPool.checkIn(data)
lastDataD = data
ss.firstBatch = False
ss.checkInTempVgprC()
return module
def computeWorkspaceSrd(self, writer, kernel, tmpSgpr):
module = Module("GSU Common computeWorkspaceSrd")
tmpS01 = tmpSgpr.idx
tmpS02 = tmpSgpr.idx + 1
# WS address calculation
module.addComment("calculate the starting WG index of GSU WGs")
module.add(SMulI32(dst=sgpr(tmpS01), src0=sgpr("NumWorkGroups0"), src1=sgpr("WorkGroup1"), comment="NumWorkGroups0*wg1"))
module.add(SAndB32(dst=sgpr(tmpS02), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
module.add(SAddU32(dst=sgpr(tmpS01), src0=sgpr(tmpS01), src1=sgpr("WorkGroup0"), comment="NumWorkGroups0*wg1+wg0"))
module.add(SMulI32(dst=sgpr(tmpS01), src0=sgpr(tmpS01), src1=sgpr(tmpS02), comment="(NumWorkGroups0*wg1+wg0)*GSU"))
module.add(SMulI32(dst=sgpr("GSUStartWGIdx"), src0=sgpr("NumWorkGroups0"), src1=sgpr("NumWorkGroups1"), comment="NumWgPerBatch"))
module.add(SMulI32(dst=sgpr("GSUStartWGIdx"), src0=sgpr("GSUStartWGIdx"), src1=sgpr("WorkGroup2"), comment="NumWgPerBatch"))
module.add(SMulI32(dst=sgpr("GSUStartWGIdx"), src0=sgpr("GSUStartWGIdx"), src1=sgpr(tmpS02), comment="NumWgPerBatch"))
module.add(SAddU32(dst=sgpr("GSUStartWGIdx"), src0=sgpr("GSUStartWGIdx"), src1=sgpr(tmpS01), comment="starting WG index of each GSU WGs"))
module.add(SAddU32(dst=sgpr(tmpS01), src0=sgpr("GSUStartWGIdx"), src1=sgpr("GSUSumIdx"), comment="(NumWorkGroups0*wg1+wg0)*GSU+NumWgPerBatch+GSUSumIdx"))
if kernel["BufferStore"]:
module.addComment("add offset to the base address of workspace buffer")
module.add(SMulHIU32(dst=sgpr(tmpS02), src0=sgpr(tmpS01), src1="MTOffset", comment="(MT0*MT1*bpeC)*WGIdx"))
module.add(SMulI32(dst=sgpr(tmpS01), src0=sgpr(tmpS01), src1="MTOffset", comment="(MT0*MT1*bpeC)*WGIdx"))
module.add(SAddU32(dst=sgpr("SrdD+0"), src0=sgpr("AddressD+0"), src1=sgpr(tmpS01), comment="add lo to SRD"))
module.add(SAddCU32(dst=sgpr("SrdD+1"), src0=sgpr("AddressD+1"), src1=sgpr(tmpS02), comment="add hi to SRD"))
else:
# Flat store: compute workspace base into AddressD SGPRs (base + tile offset)
module.addComment("flat store: add tile offset to workspace base address")
module.add(SMulHIU32(dst=sgpr(tmpS02), src0=sgpr(tmpS01), src1="MTOffset", comment="(MT0*MT1*bpeC)*WGIdx"))
module.add(SMulI32(dst=sgpr(tmpS01), src0=sgpr(tmpS01), src1="MTOffset", comment="(MT0*MT1*bpeC)*WGIdx"))
module.add(SAddU32(dst=sgpr("AddressD+0"), src0=sgpr("AddressD+0"), src1=sgpr(tmpS01), comment="add lo tile offset"))
module.add(SAddCU32(dst=sgpr("AddressD+1"), src0=sgpr("AddressD+1"), src1=sgpr(tmpS02), comment="add hi tile offset"))
module.addSpaceLine()
return module
def _generateAtomicDec(self, writer, kernel, dst, base):
module = Module("atomicDec")
if writer.states.asmCaps["HasSAtomic"]:
module.add(SAtomicDec(dst=dst, base=base, smem=SMEMModifiers(glc=True)))
else:
# For architectures without scalar atomic (e.g., gfx1250), use flat atomic with EXEC mask
addrVgpr = writer.vgprPool.checkOutAligned(2, 2, "addrVgpr")
dataVgpr = writer.vgprPool.checkOut(1)
dstVgpr = writer.vgprPool.checkOut(1)
# EXEC is wave-width: 1 sgpr on wave32, 2 sgprs on wave64
numSgpr = 1 if kernel["WavefrontSize"] == 32 else 2
SMovExec = SMovB32 if numSgpr == 1 else SMovB64
tmpSgpr = writer.sgprPool.checkOutAligned(numSgpr, numSgpr, preventOverflow=False)
# dst contains GSU-1 (the wrap value), move it to VGPR
module.add(VMovB32(dst=vgpr(dataVgpr), src=dst, comment="wrap value = GSU-1"))
module.add(VMovB64(dst=vgpr(addrVgpr, 2), src=base, comment="atomic address"))
# Save EXEC and set only lane 0 active
module.add(SMovExec(dst=sgpr(tmpSgpr, numSgpr), src=EXEC(), comment="save EXEC"))
module.add(SMovExec(dst=EXEC(), src=1, comment="only lane 0 active"))
# Arches that mark RequiresXCntForVolatileVMEM (e.g. gfx1250) need
# an explicit XNACK-replay drain before a volatile/atomic VMEM op.
if writer.states.archCaps["RequiresXCntForVolatileVMEM"]:
module.add(SWaitXCnt(xcnt=0, comment="drain in-flight VMEM before flat atomic"))
atomicFlat = FLATModifiers(scope=CacheScope.SCOPE_DEV) \
if writer.states.archCaps["DefaultScopeIsCULocal"] else None
module.add(FlatAtomicDecU32(dst=vgpr(dstVgpr), addr=vgpr(addrVgpr, 2), data=vgpr(dataVgpr),
modifier=atomicFlat))
module.add(SMovExec(dst=EXEC(), src=sgpr(tmpSgpr, numSgpr), comment="restore EXEC"))
module.add(SWaitCnt(vlcnt=0, comment="wait for atomic return"))
# Read return value to destination SGPR
module.add(VReadfirstlaneB32(dst=dst, src=vgpr(dstVgpr), comment="read atomic return value"))
writer.vgprPool.checkIn(addrVgpr)
writer.vgprPool.checkIn(dataVgpr)
writer.vgprPool.checkIn(dstVgpr)
writer.sgprPool.checkIn(tmpSgpr)
return module
def GSUSynccodegenOpt(self, kernel, writer, ss, batchIdx, tmpSgpr, tmpVgpr, tmpVgprDynamic, gwvw, batchElements, \
labelend, vgprstart, vgproffset, reductionBodyLabel):
module = Module("GSUSYNC")
reductionSkipLabel = Label(writer.labels.getNameInc("reduction_skip"), comment="")
reductionAllGsuWgLabel = Label(writer.labels.getNameInc("reduction_all_gsu_wg"), comment="")
tmpS01 = writer.sgprPool.checkOut(1, tag="GSUSynccodegenOpt_tmpS01", preventOverflow=False)
tmpS02 = writer.sgprPool.checkOut(1, tag="GSUSynccodegenOpt_tmpS02", preventOverflow=False)
tmpS05 = writer.sgprPool.checkOutAligned(2,2, tag="GSUSynccodegenOpt_tmpS05", preventOverflow=False)
tmpS06 = writer.sgprPool.checkOutAligned(4,4, tag="GSUSynccodegenOpt_tmpS06", preventOverflow=False)
tmpS06Res = ContinuousRegister(idx=tmpS06, size=4)
bufferOOB = tmpVgpr.idx + tmpVgpr.size - 1
storeOffsetSgpr = tmpSgpr.idx
loadOffsetSgpr = tmpSgpr.idx + 1
storeOffsetSgprRes = ContinuousRegister(storeOffsetSgpr, 1)
useBuffer = kernel["BufferStore"]
addr1 = sgpr(tmpS06, 4)
addr0 = vgpr(vgproffset)
bps = kernel["ProblemType"]["ComputeDataType"].numBytes() * gwvw
storeWidth = kernel["StoreVectorWidth"]
increment = kernel["NumThreads"] * storeWidth * writer.states.bpeCinternal
# On gfx1250 the reducer reads workspace data that other GSU WGs wrote
# from a different CU; SCOPE_DEV forces the load to bypass L1 and match
# the SCOPE_DEV partial-write store + flat_atomic_dec_u32 ordering.
gsuReadScope = CacheScope.SCOPE_DEV \
if writer.states.archCaps["DefaultScopeIsCULocal"] else CacheScope.SCOPE_NONE
if batchIdx == 0:
# wait for write to ws and do atomic dec to synchronizer
module.addComment("check done start")
module.add(SWaitCnt(waitAll=True, comment="wait store done before synchronizer start load and add"))
module.add(SAndB32(dst=sgpr(tmpS02), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
module.add(SSubU32(dst=sgpr(tmpS02), src0=sgpr(tmpS02), src1=1, comment=""))
module.add(self._generateAtomicDec(writer, kernel, dst=sgpr(tmpS02), base=sgpr("SrdSync", 2)))
if kernel["MbskPrefetchMethod"] == 0:
module.add(SAndB32(dst=sgpr(tmpS01), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
module.add(SCmpGtI32(src0=sgpr(tmpS01), src1=self.gsuThreshold, comment="GSU > %u ?" % self.gsuThreshold))
module.add(SCBranchSCC0(labelName=reductionSkipLabel.getLabelName(), comment="branch if false"))
module.addComment("GSU > %u, atomic_dec selects the gsu wg to do the reduction" % self.gsuThreshold)
# wait for synchronizer and check whether to branch or not
module.addComment("check synchronizer done")
module.add(SWaitCnt(kmcnt=0, comment="Wait for synchronizer"))
module.add(SCmpEQU32(src0=sgpr(tmpS02), src1=hex(1), comment=""))
module.add(SCBranchSCC1(labelName=reductionBodyLabel.getLabelName(), comment="branch if true"))
if kernel["MbskPrefetchMethod"] == 0:
module.add(reductionSkipLabel)
module.add(SEndpgm())
module.addComment("check done end")
module.add(reductionBodyLabel)
# calculate the address for read from ws
addrCalc = ss.elementAddr[0]
addrDVgpr = addrCalc.addrDVgpr
if useBuffer:
module.add(SMovB32(sgpr(loadOffsetSgpr), 0, "Init sgpr offset for interleaved wave load"))
module.add(vectorStaticMultiply(vgpr(addrDVgpr), vgpr("Serial"), storeWidth * writer.states.bpeCinternal, storeOffsetSgprRes))
if useBuffer:
module.add(VMovB32(dst=vgpr(bufferOOB), src="BufferOOB"))
module.addComment("synchronizer sum offset is equal to MTOffset (=MT0*MT1*bpeC)")
module.add(SMulHIU32(dst=sgpr(tmpS06+1), src0="MTOffset", src1=sgpr("GSUStartWGIdx"), comment="(MT0*MT1*bpeC)*WGIdx"))
module.add(SMulI32(dst=sgpr(tmpS06), src0="MTOffset", src1=sgpr("GSUStartWGIdx"), comment="(MT0*MT1*bpeC)*WGIdx"))
module.add(SAddU32(dst=sgpr(tmpS06+0), src0=sgpr("AddressD+0"), src1=sgpr(tmpS06), comment="add lo to SRD"))
module.add(SAddCU32(dst=sgpr(tmpS06+1), src0=sgpr("AddressD+1"), src1=sgpr(tmpS06+1), comment="add hi to SRD"))
if useBuffer:
module.add(SMovB64(sgpr(tmpS06+2, 2), sgpr("SrdD+2", 2), ""))
else:
# Flat mode: build full 64-bit address in VGPR pair = sgpr base + per-thread offset
module.add(VMovB32(dst=vgpr(addrDVgpr+1), src=0, comment="zero hi addr bits"))
module.add(VAddCOU32(dst=vgpr(addrDVgpr), dst1=VCC(), src0=sgpr(tmpS06+0), src1=vgpr(addrDVgpr), comment="add WS base lo"))
module.add(VAddCCOU32(dst=vgpr(addrDVgpr+1), dst1=VCC(), src0=sgpr(tmpS06+1), src1=vgpr(addrDVgpr+1), src2=VCC(), comment="add WS base hi"))
# for GSU < self.gsuThreshold, GSU-1 is passed to the reduction body
module.add(SAndB32(dst=sgpr(tmpS02), src0=sgpr("GSU"), src1=writer.gsuMaskHex(kernel), comment="Restore GSU"))
if kernel["MbskPrefetchMethod"] == 0:
module.add(SCmpGtI32(src0=sgpr(tmpS02), src1=self.gsuThreshold, comment="GSU > %u ?" % self.gsuThreshold))
module.add(SCBranchSCC1(labelName=reductionAllGsuWgLabel.getLabelName(), comment="branch if true"))
module.addComment("GSU <= %u, so we minus 1 from GSUSync at the beginning" % self.gsuThreshold)
module.add(SSubI32(dst=sgpr(tmpS02), src0=sgpr(tmpS02), src1=1, comment="Use GSU-1"))
module.add(reductionAllGsuWgLabel)
if not kernel["MbskPrefetchMethod"]:
for elementIdx in range(0, len(batchElements)):
addrCalc: AddrCalculation = ss.elementAddr[elementIdx]
if useBuffer:
addr0 = vgpr(addrCalc.addrDVgpr)
else:
addr0 = vgpr(addrCalc.addrDVgpr, 2)
# pre-load
SyncloadedData = 0
SynchronizerAddSkiplabelString = "Synchronizer_read_add_skip"
SynchronizerAddSkipComment = "Synchronizer read add skip"
SynchronizerAddSkiplabel = Label(writer.labels.getNameInc(SynchronizerAddSkiplabelString), SynchronizerAddSkipComment)
GSUtotal = writer.getMBSKGSUTotal(kernel)
SynchronizerAddEndlabel = [""] * GSUtotal
for idx in range(0, GSUtotal):
SynchronizerAddEndlabelString = "Synchronizer_read_add_end_"+str(idx+1)
SynchronizerAddEndComment = "Synchronizer read add end_"+str(idx+1)
SynchronizerAddEndlabel[idx] = Label(writer.labels.getNameInc(SynchronizerAddEndlabelString), SynchronizerAddEndComment)
#####################################load buffer#####################################
module.addComment("buffer load start")
for times in range(elementIdx, elementIdx+1):
if batchIdx != 0 or elementIdx != 0:
if useBuffer:
module.add(SAddU32(dst=sgpr(loadOffsetSgpr), src0=sgpr(loadOffsetSgpr), src1=increment, comment="Increase sgpr offset for load"))
module.add(SMulHIU32(dst=sgpr(tmpS06+1), src0="MTOffset", src1=sgpr("GSUStartWGIdx"), comment="(MT0*MT1*bpeC)*WGIdx"))
module.add(SMulI32(dst=sgpr(tmpS06), src0="MTOffset", src1=sgpr("GSUStartWGIdx"), comment="(MT0*MT1*bpeC)*WGIdx"))
module.add(SAddU32(dst=sgpr(tmpS06+0), src0=sgpr("AddressD+0"), src1=sgpr(tmpS06), comment="add lo to SRD"))
module.add(SAddCU32(dst=sgpr(tmpS06+1), src0=sgpr("AddressD+1"), src1=sgpr(tmpS06+1), comment="add hi to SRD"))
if not useBuffer:
# Flat mode: rebuild VGPR addr from new sgpr base + per-thread offset
addrDVgpr = addrCalc.addrDVgpr
module.add(vectorStaticMultiply(vgpr(addrDVgpr), vgpr("Serial"), storeWidth * writer.states.bpeCinternal, storeOffsetSgprRes))
module.add(VMovB32(dst=vgpr(addrDVgpr+1), src=0, comment="zero hi addr bits"))
module.add(VAddCOU32(dst=vgpr(addrDVgpr), dst1=VCC(), src0=sgpr(tmpS06+0), src1=vgpr(addrDVgpr), comment="add WS base lo"))
module.add(VAddCCOU32(dst=vgpr(addrDVgpr+1), dst1=VCC(), src0=sgpr(tmpS06+1), src1=vgpr(addrDVgpr+1), src2=VCC(), comment="add WS base hi"))
vgprstart = ss.elementSumIdx[elementIdx]
dataType = kernel["ProblemType"]["DestDataType"]
if dataType.isDouble() or dataType.isSingleComplex():
vgprstart = vgprstart*2
if useBuffer:
module.add(writer.chooseGlobalRead(useBuffer, bps, vgprstart, \
addr0, addr1, soffset=sgpr(loadOffsetSgpr), offset=0, glc=True, slc=True, scope=gsuReadScope,\
comment="load GSU WG %d element %d" % (SyncloadedData, elementIdx)))
else:
module.add(writer.chooseGlobalRead(useBuffer, bps, vgprstart, \
addr0, addr1, soffset=0, offset=0, glc=True, slc=True, scope=gsuReadScope,\
comment="load GSU WG %d element %d" % (SyncloadedData, elementIdx)))
SyncloadedData += 1
# Init GSUSync for different batch
module.add(SMovB32(dst=sgpr("GSUSync"), src=sgpr(tmpS02), comment="Init GSUSync to GSU for batch %u" % batchIdx))
SynchronizerlabelString = "Synchronizer_read_add"
SynchronizerComment = "Synchronizer read add"
Synchronizerlabel = Label(writer.labels.getNameInc(SynchronizerlabelString), SynchronizerComment)
tmpVAdd = tmpVgprDynamic.idx
GSUMvgpr = tmpVgpr.idx
GSUP1 = GSUtotal-1
for i in range(0, GSUP1):
module.add(SSubI32(dst=sgpr("GSUSync"), src0=sgpr("GSUSync"), src1=1, comment="%u" % i))
module.add(SAddU32(dst=sgpr(tmpS06+0), src0=sgpr(tmpS06+0), src1="MTOffset", comment="" ))
module.add(SAddCU32(dst=sgpr(tmpS06+1), src0=sgpr(tmpS06+1), src1="MTOffsetH32", comment="" ))
module.add(SCmpEQI32(src0=sgpr("GSUSync"), src1=0, comment=""))#GSUSync+GSUP1==GSU
module.add(SCBranchSCC1(labelName=SynchronizerAddEndlabel[i].getLabelName(), comment="SyncAddbranchhere"))
if not useBuffer:
# Flat mode: rebuild VGPR addr from updated sgpr base
addrDVgpr = addrCalc.addrDVgpr
module.add(vectorStaticMultiply(vgpr(addrDVgpr), vgpr("Serial"), storeWidth * writer.states.bpeCinternal, storeOffsetSgprRes))
module.add(VMovB32(dst=vgpr(addrDVgpr+1), src=0, comment="zero hi addr bits"))
module.add(VAddCOU32(dst=vgpr(addrDVgpr), dst1=VCC(), src0=sgpr(tmpS06+0), src1=vgpr(addrDVgpr), comment="add WS base lo"))
module.add(VAddCCOU32(dst=vgpr(addrDVgpr+1), dst1=VCC(), src0=sgpr(tmpS06+1), src1=vgpr(addrDVgpr+1), src2=VCC(), comment="add WS base hi"))
if(kernel["ProblemType"]["DestDataType"].numRegisters() > 1):
module.add(writer.chooseGlobalRead(useBuffer, bps, tmpVAdd+gwvw*kernel["ProblemType"]["DestDataType"].numRegisters()*i, \
addr0, addr1, soffset=0, offset=0, glc=True, slc=True, scope=gsuReadScope, \
comment="load GSU WG %d element %d" % (SyncloadedData, elementIdx)))
else:
if useBuffer:
module.add(writer.chooseGlobalRead(useBuffer, bps, tmpVAdd+gwvw*i, \
addr0, addr1, soffset=sgpr(loadOffsetSgpr), offset=0, glc=True, slc=True, scope=gsuReadScope, \
comment="load GSU WG %d element %d" % (SyncloadedData, elementIdx)))
else:
module.add(writer.chooseGlobalRead(useBuffer, bps, tmpVAdd+gwvw*i, \
addr0, addr1, soffset=0, offset=0, glc=True, slc=True, scope=gsuReadScope, \
comment="load GSU WG %d element %d" % (SyncloadedData, elementIdx)))
SyncloadedData += 1
module.addComment("buffer load end")
#####################################> GSUtotal reduction start#####################################
module.addComment("buffer add start")
module.add(Synchronizerlabel)
vlcnt = SyncloadedData - 1
for i in range(0, GSUP1):
vlcnt = vlcnt - 1 if vlcnt > 0 else 0
module.add(SWaitCnt(vlcnt=vlcnt, comment="(wait for buffer ready)"))
if kernel["ProblemType"]["DataType"].isInt8() or kernel["ProblemType"]["DataType"].isInt32():
for j in range(0, int(gwvw)):
module.add(VAddI32(dst=vgpr(vgprstart+j), src0=vgpr(vgprstart+j), src1=vgpr(tmpVAdd+0+gwvw*i+j), \
comment="buffer add"))
else:
if ((gwvw % 2) == 1):
for j in range(0, int(gwvw)):
module.add(VAddF32(dst=vgpr(vgprstart+j), src0=vgpr(vgprstart+j), src1=vgpr(tmpVAdd+0+gwvw*i+j), \
comment="buffer add"))
else:
for j in range(0, int(gwvw/2)):
module.add(VAddPKF32(dst=vgpr(vgprstart+j*2, 2), src0=vgpr(vgprstart+j*2, 2), \
src1=vgpr(tmpVAdd+0+gwvw*i+j*2, 2), comment="buffer pk"))
module.add(SSubI32(dst=sgpr("GSUSync"), src0=sgpr("GSUSync"), src1=1, comment="%u" % i))
module.add(SCmpLeI32(src0=sgpr("GSUSync"), src1=0-(GSUP1-1), comment=""))#GSUSync+GSUP1==GSU
module.add(SCBranchSCC1(labelName=SynchronizerAddSkiplabel.getLabelName(), comment="SyncAddbranch"))
module.add(SAddU32(dst=sgpr(tmpS06+0), src0=sgpr(tmpS06+0), src1="MTOffset", comment=""))
module.add(SAddCU32(dst=sgpr(tmpS06+1), src0=sgpr(tmpS06+1), src1="MTOffsetH32", comment=""))
numSgpr = 1 if kernel["WavefrontSize"] == 32 else 2
if useBuffer:
module.add(VCmpGEI32(dst=sgpr(tmpS05,numSgpr), src0=0, src1=sgpr("GSUSync"), comment=""))
module.add(VCndMaskB32(dst=vgpr(GSUMvgpr), src1=vgpr(bufferOOB), src0=addr0, src2=sgpr(tmpS05,numSgpr), comment="protect if OOB"))
if(kernel["ProblemType"]["DestDataType"].numRegisters() > 1):
module.add(writer.chooseGlobalRead(useBuffer, bps, tmpVAdd+gwvw*kernel["ProblemType"]["DestDataType"].numRegisters()*i, \
vgpr(GSUMvgpr), addr1, soffset=0, offset=0, glc=True, slc=True, scope=gsuReadScope, \
comment="prefetch GSU WG element %d" % elementIdx))
else:
module.add(writer.chooseGlobalRead(useBuffer, bps, tmpVAdd+gwvw*i, \
vgpr(GSUMvgpr), addr1, soffset=sgpr(loadOffsetSgpr), offset=0, glc=True, slc=True, scope=gsuReadScope, \
comment="prefetch GSU WG element %d" % elementIdx))
else:
# Flat mode: skip prefetch and zero dest vgprs if OOB (GSUSync <= 0)
# Buffer loads return 0 for OOB; flat must explicitly zero to match.
numRegisters = kernel["ProblemType"]["DestDataType"].numRegisters()
flatOobSkipLabel = Label(writer.labels.getNameInc("flat_oob_skip"), "skip flat prefetch if OOB")
flatOobDoneLabel = Label(writer.labels.getNameInc("flat_oob_done"), "flat OOB done")
module.add(SCmpLeI32(src0=sgpr("GSUSync"), src1=0, comment="GSUSync <= 0?"))
module.add(SCBranchSCC1(labelName=flatOobSkipLabel.getLabelName(), comment="skip flat prefetch if OOB"))
addrDVgpr = addrCalc.addrDVgpr
module.add(vectorStaticMultiply(vgpr(addrDVgpr), vgpr("Serial"), storeWidth * writer.states.bpeCinternal, storeOffsetSgprRes))
module.add(VMovB32(dst=vgpr(addrDVgpr+1), src=0, comment="zero hi addr bits"))
module.add(VAddCOU32(dst=vgpr(addrDVgpr), dst1=VCC(), src0=sgpr(tmpS06+0), src1=vgpr(addrDVgpr), comment="add WS base lo"))
module.add(VAddCCOU32(dst=vgpr(addrDVgpr+1), dst1=VCC(), src0=sgpr(tmpS06+1), src1=vgpr(addrDVgpr+1), src2=VCC(), comment="add WS base hi"))
if numRegisters > 1:
module.add(writer.chooseGlobalRead(useBuffer, bps, tmpVAdd+gwvw*numRegisters*i, \
addr0, addr1, soffset=0, offset=0, glc=True, slc=True, scope=gsuReadScope, \
comment="prefetch GSU WG element %d" % elementIdx))
else:
module.add(writer.chooseGlobalRead(useBuffer, bps, tmpVAdd+gwvw*i, \
addr0, addr1, soffset=0, offset=0, glc=True, slc=True, scope=gsuReadScope, \
comment="prefetch GSU WG element %d" % elementIdx))
module.add(SBranch(labelName=flatOobDoneLabel.getLabelName(), comment="skip OOB zeroing"))
module.add(flatOobSkipLabel)
if numRegisters > 1:
for j in range(gwvw * numRegisters):
module.add(VMovB32(dst=vgpr(tmpVAdd+gwvw*numRegisters*i+j), src=0, comment="zero OOB prefetch vgpr"))
else:
for j in range(gwvw):
module.add(VMovB32(dst=vgpr(tmpVAdd+gwvw*i+j), src=0, comment="zero OOB prefetch vgpr"))
module.add(flatOobDoneLabel)
vlcnt += 1
module.addComment("buffer add end")
module.add(SCmpGtI32(src0=sgpr("GSUSync"), src1=hex(1-(GSUP1)), comment=""))
module.add(SCBranchSCC1(labelName=Synchronizerlabel.getLabelName(), comment="Syncbranchhere"))
#####################################< GSUtotal reduction start#####################################
for k in range(GSUtotal-2, -1, -1):
module.addSpaceLine()
module.add(SynchronizerAddEndlabel[k])
vlcnt = k
for i in range(0, k):
vlcnt = vlcnt - 1 if vlcnt > 0 else 0
module.add(SWaitCnt(vlcnt=vlcnt, comment="(wait for buffer ready)"))
if kernel["ProblemType"]["DataType"].isInt8() or kernel["ProblemType"]["DataType"].isInt32():
for j in range(0, int(gwvw)):
module.add(VAddI32(dst=vgpr(vgprstart+j), src0=vgpr(vgprstart+j), src1=vgpr(tmpVAdd+0+gwvw*i+j), \
comment="buffer add"))
else:
if ((gwvw % 2) == 1):
for j in range(0, int(gwvw)):
module.add(VAddF32(dst=vgpr(vgprstart+j), src0=vgpr(vgprstart+j), src1=vgpr(tmpVAdd+0+gwvw*i+j), \
comment="buffer add"))
else:
for j in range(0, int(gwvw/2)):
module.add(VAddPKF32(dst=vgpr(vgprstart+j*2, 2), src0=vgpr(vgprstart+j*2, 2), \
src1=vgpr(tmpVAdd+0+gwvw*i+j*2, 2), comment="buffer pk"))
if i == k-1:
module.add(SBranch(labelName=SynchronizerAddSkiplabel.getLabelName(), comment="SyncAddbranch"))
module.add(SynchronizerAddSkiplabel)
module.addComment("buffer add end2")
else:
tmpWSD = writer.sgprPool.checkOutAligned(4, 4, tag="GSUSynccodegenOpt_tmpWSD", preventOverflow=False)
GSUtotal = writer.getMBSKGSUTotal(kernel)-1
loadWidth = gwvw * int(max(1, kernel["ProblemType"]["DestDataType"].numRegisters()))
unrolledWGs = GSUtotal // len(batchElements)
tmpVidx = tmpVgprDynamic.idx
tmpVAdd = [[0] * len(batchElements) for _ in range(unrolledWGs)]
for i in range(0, unrolledWGs):
for j in range(0, len(batchElements)):
tmpVAdd[i][j] = tmpVidx
tmpVidx += loadWidth
SynchronizerAddEndlabel = [""] * (unrolledWGs+1)
for uidx in range(0, unrolledWGs+1):
SynchronizerAddEndlabelString = "Synchronizer_read_add_end_"+str(uidx+1)
SynchronizerAddEndComment = "Synchronizer read add end_"+str(uidx+1)
SynchronizerAddEndlabel[uidx] = Label(writer.labels.getNameInc(SynchronizerAddEndlabelString), SynchronizerAddEndComment)
# set buffer load address for WG0
module.add(SMulHIU32(dst=sgpr(tmpWSD+1), src0="MTOffset", src1=sgpr("GSUStartWGIdx"), comment="(MT0*MT1*bpeC)*WGIdx"))
module.add(SMulI32(dst=sgpr(tmpWSD), src0="MTOffset", src1=sgpr("GSUStartWGIdx"), comment="(MT0*MT1*bpeC)*WGIdx"))
module.add(SAddU32(dst=sgpr(tmpWSD), src0=sgpr("AddressD+0"), src1=sgpr(tmpWSD), comment="add lo to SRD"))
module.add(SAddCU32(dst=sgpr(tmpWSD+1), src0=sgpr("AddressD+1"), src1=sgpr(tmpWSD+1), comment="add hi to SRD WSD"))
module.add(SMovB64(sgpr(tmpWSD+2, 2), sgpr("SrdD+2", 2), ""))
# set buffer load address for WG1
module.add(SAddU32(dst=sgpr(tmpS06+0), src0=sgpr(tmpWSD+0), src1="MTOffset", comment="" ))
module.add(SAddCU32(dst=sgpr(tmpS06+1), src0=sgpr(tmpWSD+1), src1="MTOffsetH32", comment="" ))
module.add(SMovB64(sgpr(tmpS06+2, 2), sgpr("SrdD+2", 2), ""))
accumulationStartlabel = Label(writer.labels.getNameInc("Accumulation_Start"), "Accumulation Start")
accumulationEndlabel = Label(writer.labels.getNameInc("Accumulation_End"), "Accumulation End")
# Init GSUSync for different batch
module.add(SMovB32(dst=sgpr("GSUSync"), src=sgpr(tmpS02), comment="Init GSUSync to GSU for batch %u" % batchIdx))
# pre-load
SyncloadedData = 0
# first 2 WGs: read same element first for earlier reduction
for elementIdx in range(0, len(batchElements)):
addrCalc: AddrCalculation = ss.elementAddr[elementIdx]
addr0 = vgpr(addrCalc.addrDVgpr)
if batchIdx != 0 or elementIdx != 0:
module.add(SAddU32(dst=sgpr(loadOffsetSgpr), src0=sgpr(loadOffsetSgpr), src1=increment, comment="Increase sgpr offset for load"))
if elementIdx == 0:
module.add(SMovB32(dst=sgpr(tmpS01), src=sgpr(loadOffsetSgpr), comment="save first element offset"))
for uidx in range(0, 2):
if uidx == 0:
data = ss.elementSumIdx[elementIdx]
tmpAddr1 = tmpWSD
else:
data = tmpVAdd[-1][elementIdx]
tmpAddr1 = tmpS06
module.add(writer.chooseGlobalRead(True, bps, data, \
addr0, sgpr(tmpAddr1, 4), soffset=sgpr(loadOffsetSgpr), offset=0, glc=True, slc=True, scope=gsuReadScope,\
comment="load GSU WG %d element %d " % (uidx, elementIdx)))
SyncloadedData += 1
module.add(SSubI32(dst=sgpr("GSUSync"), src0=sgpr("GSUSync"), src1=2))
module.add(SCmpLeI32(src0=sgpr("GSUSync"), src1=0, comment="GSUSync <= 0?"))
module.add(SCBranchSCC1(labelName=SynchronizerAddEndlabel[1].getLabelName(), comment=""))
module.add(SAddU32(dst=sgpr(tmpS06+0), src0=sgpr(tmpS06+0), src1="MTOffset", comment="" ))
module.add(SAddCU32(dst=sgpr(tmpS06+1), src0=sgpr(tmpS06+1), src1="MTOffsetH32", comment="" ))
# other WGs: read all elements together
for uidx in range(2, unrolledWGs+1):
module.add(SMovB32(dst=sgpr(loadOffsetSgpr), src=sgpr(tmpS01), comment="restore offset for element0"))
for elementIdx in range(0, len(batchElements)):
addrCalc: AddrCalculation = ss.elementAddr[elementIdx]
addr0 = vgpr(addrCalc.addrDVgpr)
data = tmpVAdd[uidx-2][elementIdx]
if elementIdx != 0:
module.add(SAddU32(dst=sgpr(loadOffsetSgpr), src0=sgpr(loadOffsetSgpr), src1=increment, comment="Increase sgpr offset for load"))
module.add(writer.chooseGlobalRead(True, bps, data, \
addr0, addr1, soffset=sgpr(loadOffsetSgpr), offset=0, glc=True, slc=True, scope=gsuReadScope,\
comment="load GSU WG %d element %d " % (uidx, elementIdx)))
SyncloadedData += 1
module.add(SSubI32(dst=sgpr("GSUSync"), src0=sgpr("GSUSync"), src1=1))
module.add(SCmpLeI32(src0=sgpr("GSUSync"), src1=0, comment="GSUSync <= 0?"))
module.add(SCBranchSCC1(labelName=SynchronizerAddEndlabel[uidx].getLabelName(), comment=""))
module.add(SAddU32(dst=sgpr(tmpS06+0), src0=sgpr(tmpS06+0), src1="MTOffset", comment="" ))
module.add(SAddCU32(dst=sgpr(tmpS06+1), src0=sgpr(tmpS06+1), src1="MTOffsetH32", comment="" ))
module.addComment("buffer load end\n")
##################################### reduction start #####################################
module.addComment("buffer add start")
vlcnt = SyncloadedData
# reduce first 2 WGs
module.add(SMovB32(dst=sgpr(loadOffsetSgpr), src=sgpr(tmpS01), comment="restore offset for element0"))
for elementIdx in range(0, len(batchElements)):
addrCalc: AddrCalculation = ss.elementAddr[elementIdx]
addr0 = vgpr(addrCalc.addrDVgpr)
data = tmpVAdd[-1][elementIdx]
vgprstart = ss.elementSumIdx[elementIdx]
vlcnt = vlcnt - 2 if vlcnt > 0 else 0
module.add(SWaitCnt(vlcnt=vlcnt, comment="(wait for buffer ready)"))
if kernel["ProblemType"]["DataType"].isInt8() or kernel["ProblemType"]["DataType"].isInt32():
for j in range(0, int(gwvw)):
module.add(VAddI32(dst=vgpr(vgprstart+j), src0=vgpr(vgprstart+j), src1=vgpr(data+j), \
comment="buffer add"))
else:
if ((gwvw % 2) == 1):
for j in range(0, int(gwvw)):
module.add(VAddF32(dst=vgpr(vgprstart+j), src0=vgpr(vgprstart+j), src1=vgpr(data+j), \
comment="buffer add"))
else:
for j in range(0, int(gwvw/2)):
module.add(VAddPKF32(dst=vgpr(vgprstart+j*2, 2), src0=vgpr(vgprstart+j*2, 2), \
src1=vgpr(data+j*2, 2), comment="buffer pk"))
# prefetch
if elementIdx != 0:
module.add(SAddU32(dst=sgpr(loadOffsetSgpr), src0=sgpr(loadOffsetSgpr), src1=increment, comment="Increase sgpr offset for load"))
module.add(writer.chooseGlobalRead(True, bps, data, \
addr0, addr1, soffset=sgpr(loadOffsetSgpr), offset=0, glc=True, slc=True, scope=gsuReadScope,\
comment="prefetch element %d " % (elementIdx)))
vlcnt += 1
module.add(SSubI32(dst=sgpr("GSUSync"), src0=sgpr("GSUSync"), src1=1))
module.add(SCmpLeI32(src0=sgpr("GSUSync"), src1=0-unrolledWGs, comment=""))
module.add(SCBranchSCC1(labelName=accumulationEndlabel.getLabelName(), comment="Accumulation finished"))
module.add(SAddU32(dst=sgpr(tmpS06+0), src0=sgpr(tmpS06+0), src1="MTOffset", comment="" ))
module.add(SAddCU32(dst=sgpr(tmpS06+1), src0=sgpr(tmpS06+1), src1="MTOffsetH32", comment="" ))
module.add(SCmpLeI32(src0=sgpr("GSUSync"), src1=0, comment="disable buffer load if GSUSync <= 0"))
module.add(SCSelectB32(sgpr(tmpS06+2), 0, sgpr(tmpS06+2), ""))
module.add(accumulationStartlabel)
for uidx in range(0, unrolledWGs):
module.add(SMovB32(dst=sgpr(loadOffsetSgpr), src=sgpr(tmpS01), comment="restore offset for element0"))
for elementIdx in range(0, len(batchElements)):
addrCalc: AddrCalculation = ss.elementAddr[elementIdx]
addr0 = vgpr(addrCalc.addrDVgpr)
data = tmpVAdd[uidx][elementIdx]
vgprstart = ss.elementSumIdx[elementIdx]
vlcnt = vlcnt - 1 if vlcnt > 0 else 0
module.add(SWaitCnt(vlcnt=vlcnt, comment="(wait for buffer ready)"))
if kernel["ProblemType"]["DataType"].isInt8() or kernel["ProblemType"]["DataType"].isInt32():
for j in range(0, int(gwvw)):
module.add(VAddI32(dst=vgpr(vgprstart+j), src0=vgpr(vgprstart+j), src1=vgpr(data+j), \
comment="buffer add"))
else:
if ((gwvw % 2) == 1):
for j in range(0, int(gwvw)):
module.add(VAddF32(dst=vgpr(vgprstart+j), src0=vgpr(vgprstart+j), src1=vgpr(data+j), \
comment="buffer add"))
else:
for j in range(0, int(gwvw/2)):
module.add(VAddPKF32(dst=vgpr(vgprstart+j*2, 2), src0=vgpr(vgprstart+j*2, 2), \
src1=vgpr(data+j*2, 2), comment="buffer pk"))
# prefetch
if elementIdx != 0:
module.add(SAddU32(dst=sgpr(loadOffsetSgpr), src0=sgpr(loadOffsetSgpr), src1=increment, comment="Increase sgpr offset for load"))
module.add(writer.chooseGlobalRead(True, bps, data, \
addr0, addr1, soffset=sgpr(loadOffsetSgpr), offset=0, glc=True, slc=True, scope=gsuReadScope,\
comment="prefetch element %d " % (elementIdx)))
vlcnt += 1
module.add(SSubI32(dst=sgpr("GSUSync"), src0=sgpr("GSUSync"), src1=1))
module.add(SCmpLeI32(src0=sgpr("GSUSync"), src1=0-unrolledWGs, comment=""))
module.add(SCBranchSCC1(labelName=accumulationEndlabel.getLabelName(), comment="Accumulation finished"))
module.add(SAddU32(dst=sgpr(tmpS06+0), src0=sgpr(tmpS06+0), src1="MTOffset", comment="" ))
module.add(SAddCU32(dst=sgpr(tmpS06+1), src0=sgpr(tmpS06+1), src1="MTOffsetH32", comment="" ))
module.add(SCmpLeI32(src0=sgpr("GSUSync"), src1=0, comment="disable buffer load if GSUSync <= 0"))
module.add(SCSelectB32(sgpr(tmpS06+2), 0, sgpr(tmpS06+2), ""))
module.add(SBranch(labelName=accumulationStartlabel.getLabelName(), comment=""))
for k in range(unrolledWGs, 0, -1):
module.addSpaceLine()
module.add(SynchronizerAddEndlabel[k])
vlcnt = (k+1) * len(batchElements)
# reduce first 2 WGs
for elementIdx in range(0, len(batchElements)):
vlcnt = vlcnt-2 if vlcnt > 0 else 0
vgprstart = ss.elementSumIdx[elementIdx]
data = tmpVAdd[-1][elementIdx]
module.add(SWaitCnt(vlcnt=vlcnt, comment="(wait for buffer ready)"))
if kernel["ProblemType"]["DataType"].isInt8() or kernel["ProblemType"]["DataType"].isInt32():
for j in range(0, int(gwvw)):
module.add(VAddI32(dst=vgpr(vgprstart+j), src0=vgpr(vgprstart+j), src1=vgpr(data+j), \
comment="buffer add"))
else:
if ((gwvw % 2) == 1):
for j in range(0, int(gwvw)):
module.add(VAddF32(dst=vgpr(vgprstart+j), src0=vgpr(vgprstart+j), src1=vgpr(data+j), \
comment="buffer add"))
else:
for j in range(0, int(gwvw/2)):
module.add(VAddPKF32(dst=vgpr(vgprstart+j*2, 2), src0=vgpr(vgprstart+j*2, 2), \
src1=vgpr(data+j*2, 2), comment="buffer pk"))
for i in range(1, k):
for elementIdx in range(0, len(batchElements)):
vlcnt = vlcnt-1 if vlcnt > 0 else 0
vgprstart = ss.elementSumIdx[elementIdx]
data = tmpVAdd[i-1][elementIdx]
module.add(SWaitCnt(vlcnt=vlcnt, comment="(wait for buffer ready)"))
if kernel["ProblemType"]["DataType"].isInt8() or kernel["ProblemType"]["DataType"].isInt32():
for j in range(0, int(gwvw)):
module.add(VAddI32(dst=vgpr(vgprstart+j), src0=vgpr(vgprstart+j), src1=vgpr(data+j), \
comment="buffer add"))
else:
if ((gwvw % 2) == 1):
for j in range(0, int(gwvw)):
module.add(VAddF32(dst=vgpr(vgprstart+j), src0=vgpr(vgprstart+j), src1=vgpr(data+j), \
comment="buffer add"))
else:
for j in range(0, int(gwvw/2)):
module.add(VAddPKF32(dst=vgpr(vgprstart+j*2, 2), src0=vgpr(vgprstart+j*2, 2), \
src1=vgpr(data+j*2, 2), comment="buffer pk"))
module.add(SBranch(labelName=accumulationEndlabel.getLabelName(), comment="Accumulation End"))
module.addComment("buffer add end\n")
module.add(accumulationEndlabel)
writer.sgprPool.checkIn(tmpWSD)
writer.sgprPool.checkIn(tmpS06)
writer.sgprPool.checkIn(tmpS05)
writer.sgprPool.checkIn(tmpS02)
writer.sgprPool.checkIn(tmpS01)
return module
def lastGsuWgReduction(self, kernel, writer, ss, batchIdx, tmpVgpr, tmpVgprDynamic, gwvw, batchElements, codeAccVgprRead, vgproffset, soffset):
module = Module("lastGsuWgReduction")
accvgprReadLabel = Label(writer.labels.getNameInc("last_gsu_wg_accvgpr_read"), comment="")
module.add(accvgprReadLabel)
module.add(SWaitCnt(vlcnt=0, comment="wait for buffer_load to finish"))
tmpVAdd = tmpVgprDynamic.idx
# last gsu wg accvgpr read
if codeAccVgprRead is not None and kernel["LocalSplitU"] == 1:
regsPerScalar = writer.states.bpeCinternal // writer.states.bpr # register per scalar
# loop over store instructions within one batch
batchNumAccRegs = len(batchElements) * gwvw * regsPerScalar
vmcntForSummation = int(batchNumAccRegs / gwvw)
for elementIdx in range(0, len(batchElements)):
# loop over scalars within one store instruction
for vi in range(0, gwvw):
# loop over registers within one scalar
for rIdx in range(0, regsPerScalar):
codeAccVgprReadInst = codeAccVgprRead.popFirstItem()
codeAccVgprReadInst.dst = vgpr(tmpVAdd + regsPerScalar*vi + rIdx) # replace dst with temp sgpr
module.add(codeAccVgprReadInst)
vgprstart = ss.elementSumIdx[elementIdx]
dataType = kernel["ProblemType"]["DestDataType"]
if dataType.isDouble() or dataType.isSingleComplex():
vgprstart = vgprstart*2
vmcntForSummation = vmcntForSummation - 1
# module.add(SWaitCnt(vlcnt=vmcntForSummation, comment="wait for buffer_load to finish"))
GSUP1 = 1 # do 1 element at a time
for i in range(0, GSUP1):
if kernel["ProblemType"]["DataType"].isInt8() or kernel["ProblemType"]["DataType"].isInt32():
for j in range(0, int(gwvw)):
module.add(VAddI32(dst=vgpr(vgprstart+j), src0=vgpr(vgprstart+j), src1=vgpr(tmpVAdd+0+gwvw*i+j), \
comment="buffer add"))
else:
if ((gwvw % 2) == 1):
for j in range(0, int(gwvw)):
module.add(VAddF32(dst=vgpr(vgprstart+j), src0=vgpr(vgprstart+j), src1=vgpr(tmpVAdd+0+gwvw*i+j), \
comment="buffer add"))
else:
for j in range(0, int(gwvw/2)):
module.add(VAddPKF32(dst=vgpr(vgprstart+j*2, 2), src0=vgpr(vgprstart+j*2, 2), \
src1=vgpr(tmpVAdd+0+gwvw*i+j*2, 2), comment="buffer pk"))
elif kernel["LocalSplitU"] > 1:
# read from LSU VGPRs
regsPerScalar = writer.states.bpeCinternal // writer.states.bpr # register per scalar
if kernel["MIArchVgpr"]:
tmpStartVgprValuC = writer.states.c.startVgprValu
writer.states.c.startVgprValu = 0
module.add(RegSet("v", "vgprValuC", 0))
if ss.lsuStartVgprOffset > 0:
for elementIdx in range(0, len(batchElements)):
for vi in range(0, gwvw):
for rIdx in range(0, regsPerScalar):
codeAccVgprReadInst = codeAccVgprRead.popFirstItem()
codeAccVgprReadInst.dst = vgpr(tmpVAdd + regsPerScalar*vi + rIdx) # replace dst with temp sgpr
codeAccVgprReadInst.comment = "copy acc to vreg[%u]" % (tmpVAdd + regsPerScalar*vi + rIdx)
module.add(codeAccVgprReadInst)
vgprstart = ss.elementSumIdx[elementIdx]
dataType = kernel["ProblemType"]["DestDataType"]
if dataType.isDouble() or dataType.isSingleComplex():
vgprstart = vgprstart*2
GSUP1 = 1 # do 1 element at a time
for i in range(0, GSUP1):
if kernel["ProblemType"]["DataType"].isInt8() or kernel["ProblemType"]["DataType"].isInt32():
for j in range(0, int(gwvw)):
module.add(VAddI32(dst=vgpr(vgprstart+j), src0=vgpr(vgprstart+j), src1=vgpr(tmpVAdd+0+gwvw*i+j), \
comment="buffer add"))
else:
if ((gwvw % 2) == 1):
for j in range(0, int(gwvw)):
module.add(VAddF32(dst=vgpr(vgprstart+j), src0=vgpr(vgprstart+j), src1=vgpr(tmpVAdd+0+gwvw*i+j), \
comment="buffer add"))
else:
for j in range(0, int(gwvw/2)):
module.add(VAddPKF32(dst=vgpr(vgprstart+j*2, 2), src0=vgpr(vgprstart+j*2, 2), \
src1=vgpr(tmpVAdd+0+gwvw*i+j*2, 2), comment="buffer pk"))
ss.lsuStartVgprOffset += len(batchElements) * gwvw * regsPerScalar
if kernel["MIArchVgpr"]:
writer.states.c.startVgprValu = tmpStartVgprValuC
module.add(RegSet("v", "vgprValuC", tmpStartVgprValuC))
else:
module.add(SNop(1, "2 wait states required before reading vgpr"))
module.addSpaceLine()
return module
def initializeSrd(self, writer, ArgTypeCheckLabel, GeneralBatchedGemmSrdInitiation_End, kernel, ch):
module = Module("initializeSrd")
# Special handling for "MultipleBuffer" and "MultipleBufferSingleKernel" for General Batched GEMM
# ArgType == 3 (General Batched GEMM) but GSU == 1, then SrdC/D will be initialized to correct batch matrix address from pointer array (AddressC/D)
# ArgType == 3 (General Batched GEMM) but GSU > 1, then SrdC/D will be initialized with workspace.
# "MultipleBuffer" means both SrdC and SrdD are workspace pointers
# "MultipleBufferSingleKernel" means only SrdD will be workspace pointer while SrdC will be initialized to correct batch matrix address from pointer array (AddressC)
if((kernel["_GlobalAccumulation"] == 'MultipleBuffer') or (kernel["_GlobalAccumulation"] == 'MultipleBufferSingleKernel')):
with writer.allocTmpSgpr(1, tag="initializeSrd_tmpSgprGSU") as tmpSgprGSU:
module.add(SAndB32(dst=sgpr(tmpSgprGSU.idx), src0=sgpr("GSU"), src1=hex(0x3FFF), comment="Restore GSU"))
module.add(SCmpEQU32(src0=sgpr(tmpSgprGSU.idx), src1=1, comment="GSU == 1 ?"))
module.add(SCBranchSCC1(labelName=ArgTypeCheckLabel.getLabelName(), comment="Handling General Batched GEMM SRD initialization"))
if((kernel["_GlobalAccumulation"] == 'MultipleBuffer') or (kernel["_GlobalAccumulation"] == 'MultipleBufferSingleKernel' and ch == "D")):
module.add(SMovB64(dst=sgpr("Srd%s+0"%ch, 2), src=sgpr("Address%s+0"%ch, 2), comment="init SRD base address" ))
module.add(SBranch(labelName=GeneralBatchedGemmSrdInitiation_End.getLabelName(), comment="End of handling General Batched GEMM SRD initialization"))
module.add(ArgTypeCheckLabel)
return module
# GSU = 1, then all C and D will have pointer to device memory holding pointer array to batch matrices.
# So we need logic to get the correct batch matrix address into Srd.
# GSU > 1, then AddressA/B will be pointer to device memory holding pointer array to batch matrices but AddressC/D will further depend on the following:
# a) MultiBuffer case: AddressC/D will be pointer to workspace and will be handled similar to Strided Batched case.
# b) MultiBufferSingleKernel case: AddressC will be pointer to device memory holding pointer array to batch matrices while AddressD will be pointer to workspace.
def routeToGeneralBatchedOrStridedBatched(self, stridedBatchedGemmLoad, argTypeChecks, generalBatchedGemmLoad, mat, kernel, tmpS1):
module = Module("routeToGeneralBatchedOrStridedBatched")
module.add(SCmpEQU32(src0=sgpr(tmpS1), src1=1, comment="GSU == 1 ?"))
if(kernel["_GlobalAccumulation"] == 'MultipleBufferSingleKernel' and mat == "C"):
module.add(SCBranchSCC0(labelName=argTypeChecks.getLabelName()))
else:
module.add(SCBranchSCC0(labelName=stridedBatchedGemmLoad.getLabelName()))
if kernel["ProblemType"]["SupportUserArgs"]:
module.add(SCmpEQU32(src0=sgpr("ArgType"), src1=3, comment="ArgType == 3 for General Batched GEMM"))
module.add(SCBranchSCC1(labelName=generalBatchedGemmLoad.getLabelName()))
if(kernel["_GlobalAccumulation"] == 'MultipleBufferSingleKernel' and mat == "C"):
module.add(SBranch(labelName=stridedBatchedGemmLoad.getLabelName()))
module.add(argTypeChecks)
if kernel["ProblemType"]["SupportUserArgs"]:
module.add(SCmpEQU32(src0=sgpr("ArgType"), src1=3, comment="ArgType == 3 for General Batched GEMM"))
module.add(SCBranchSCC1(labelName=generalBatchedGemmLoad.getLabelName()))
module.add(SBranch(labelName=stridedBatchedGemmLoad.getLabelName()))
return module