Files
TheRock/rocm-libraries/projects/hipblaslt/tensilelite/Tensile/AsmAddressCalculation.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

1035 lines
66 KiB
Python

################################################################################
#
# Copyright (C) 2022-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.code import Module
from rocisa.container import EXEC, VCC, vgpr, sgpr
from rocisa.instruction import MacroInstruction, SAddCU32, SAddU32, SAndB32, \
SAndB64, SLShiftLeftB32, SMovB32, SMovB64, SMulI32, SSubBU32, SSubU32, \
VAddCCOU32, VAddCOU32, VAddI32, VAddU32, VAndB32, VBfiB32, \
VCmpEQU32, VCmpGtU32, VCmpLtU32, VCmpXNeU32, VCndMaskB32, VLShiftLeftB32, \
VMadI32I24, VMovB32, VMulLOU32, VSubI32, VSubU32
from rocisa.functions import vectorAddMultiplyBpe, vectorMultiply64Bpe
from .Common import INDEX_CHARS, DataDirection, log2
##############################################################################
# Fields associated with computing address
##############################################################################
class AddrCalculation:
# rowInc is number of rows to add to the base address
# coord0Vgpr : This is VGPR that holds coord0. Coord0 is element-space
# packed index for the 0 coordinate of the C/D matrix.
# coord1Vgpr : VGPR which tracks the last coord1 calculation.
# If this is new coord1, just overwrite it with latest calc.
def __init__(self, kernelWriter, ss, addrCVgpr, addrDVgpr, addrGSUSyncVgprs, addrEVgpr, addrBiasVgpr, addrScaleAVecVgpr, addrScaleBVecVgpr, addrScaleAlphaVecVgpr, element, \
coordOffset0, coord1Vgpr, coordOffset1, rowInc, newCoord1, vectorDataTypes, addrGateVgpr=None):
self.kernelWriter = kernelWriter
# vgprs for address, could be more than one (for flat)
self.addrEVgpr = addrEVgpr
self.addrDVgpr = addrDVgpr
self.addrGSUSyncVgprs = addrGSUSyncVgprs
self.addrCVgpr = addrCVgpr
self.addrBiasVgpr = addrBiasVgpr
self.addrGateVgpr = addrGateVgpr
self.addrScaleAVecVgpr = addrScaleAVecVgpr
self.addrScaleBVecVgpr = addrScaleBVecVgpr
self.addrScaleAlphaVecVgpr = addrScaleAlphaVecVgpr
self.coord1Vgpr = coord1Vgpr # vgpr that stores coord1Vgpr
self.element = element
self.coordOffset0 = coordOffset0
self.coordOffset1 = coordOffset1
self.rowInc = rowInc
self.rowIncDirtyRowPtr = 0 # rowInc was used to modify rowPtr, need to recompute addr
self.newCoord1 = newCoord1 # vgpr that stores newCoord1
self.vectorDataTypes = vectorDataTypes
self.biasOffset = [0, 0]
self.scaleAVecOffset = 0
self.scaleBVecOffset = 0
self.scaleAlphaVecOffset = [0, 0]
if ss.optSingleColVgpr:
# optimized stores use the load offset for coordOffset0 calculations.
self.biasOffset[0] = coordOffset0 * kernelWriter.states.bpeCinternal + self.vectorDataTypes.bias(0).ldsOffset
self.biasOffset[1] = coordOffset1 * kernelWriter.states.bpeCinternal + self.vectorDataTypes.bias(1).ldsOffset
self.scaleAVecOffset = coordOffset0 * kernelWriter.states.bpeCinternal + self.vectorDataTypes.scaleA.ldsOffset
self.scaleBVecOffset = coordOffset1 * kernelWriter.states.bpeCinternal + self.vectorDataTypes.scaleB.ldsOffset
self.scaleAlphaVecOffset[0] = coordOffset0 * kernelWriter.states.bpeCinternal + self.vectorDataTypes.scaleAlpha(0).ldsOffset
self.scaleAlphaVecOffset[1] = coordOffset1 * kernelWriter.states.bpeCinternal + self.vectorDataTypes.scaleAlpha(1).ldsOffset
self.globalOffset = coordOffset0 * kernelWriter.states.bpeCexternal
self.globalOffsetE = coordOffset0 * kernelWriter.states.bpeE
self.globalOffsetGate = coordOffset0 * kernelWriter.states.bpeGate
self.globalOffsetInternal = coordOffset0 * kernelWriter.states.bpeCinternal
else:
# else non-opt stores include the coord0 offset into VGPR address calcs
self.biasOffset[0] = self.vectorDataTypes.bias(0).ldsOffset
self.biasOffset[1] = self.vectorDataTypes.bias(1).ldsOffset
self.scaleAVecOffset = self.vectorDataTypes.scaleA.ldsOffset
self.scaleBVecOffset = self.vectorDataTypes.scaleB.ldsOffset
self.scaleAlphaVecOffset[0] = self.vectorDataTypes.scaleAlpha(0).ldsOffset
self.scaleAlphaVecOffset[1] = self.vectorDataTypes.scaleAlpha(1).ldsOffset
self.globalOffset = 0
self.globalOffsetE = 0
self.globalOffsetGate = 0
self.globalOffsetInternal = 0
def addScaled(self, destV, src0, src1, scale1, tmpS01, comment="", comment1="", comment2="scale stride"):
"""
Use minimally efficient instructions to add stride*scale
"""
module = Module("addScaled")
if scale1 == 1:
module.add(VAddU32(dst=destV, src0=src0, src1=src1, comment=comment1 if comment1!="" else comment))
else:
module.add(SMulI32(dst=sgpr(tmpS01), src0=src1, src1=scale1, comment=comment2))
if comment1!="":
module.add(VAddU32(dst=destV, src0=src0, src1=sgpr(tmpS01), comment=comment))
else:
module.add(VAddI32(dst=destV, src0=src0, src1=sgpr(tmpS01), comment=comment))
return module
def emitCoord1Advance(self, rowInc, tmpS01, d0=0, vc0=0, comment=None, scomment=None):
"""
Advance the shared coord1 VGPR (kernelWriter.vgprs.coord1) in place by
`rowInc` rows. This is the single canonical "coord1 += rowInc" emitter,
shared by the per-element cross-row advance (emitAddressCoordIncrement)
and the CLS look-ahead at batch end (GlobalWriteBatch.emit()).
rowInc == 0 -> nothing
0 < rowInc <= 64 -> v_add_co_u32 with immediate
rowInc > 64 -> s_mov + v_add_co_u32
rowInc < 0 -> s_mov + v_add_nc_i32
`comment`/`scomment` override the v_add / s_mov comments (used by the CLS
look-ahead to keep its "coord1.la ... (look-ahead)" marker); when None the
canonical per-element comments (coord1.1/.2/.3) are emitted so the
per-element / non-CLS .s stays byte-identical.
"""
module = Module("coord1Advance")
coord1 = self.kernelWriter.vgprs.coord1
if scomment is None:
scomment = "rowInc d1=%u vc1=%u"%(d0, vc0)
if rowInc == 0:
pass
elif 0 < rowInc <= 64:
module.add(VAddCOU32(dst=vgpr(coord1), dst1=VCC(), src0=vgpr(coord1), \
src1=rowInc, comment=comment if comment else "coord1.1: coord1Vgpr += d1*sg1*VW + vc1"))
elif rowInc > 64:
module.add(SMovB32(dst=sgpr(tmpS01), src=rowInc, comment=scomment))
module.add(VAddCOU32(dst=vgpr(coord1), dst1=VCC(), src0=vgpr(coord1), \
src1=sgpr(tmpS01), comment=comment if comment else "coord1.2: coord1 += d1*sg1*VW + vc1"))
else: # rowInc < 0
module.add(SMovB32(dst=sgpr(tmpS01), src=rowInc, comment=scomment))
module.add(VAddI32(dst=vgpr(coord1), src0=vgpr(coord1), \
src1=sgpr(tmpS01), comment=comment if comment else "coord1.3: coord1 += d1*sg1*VW + vc1"))
return module
def emitRowPtrAdvance(self, kernel, ss, tmpS01, rowInc, lookahead=False):
"""
Advance the row-pointer VGPRs (cinRowPtr / coutRowPtrD / coutRowPtrE /
coutRowPtrBias, or packed-C1 extract) by `rowInc` rows. This is the single
canonical row-pointer advance, shared by:
- the per-element cross-row advance in emitAddressSetupCode (lookahead=False)
- the CLS look-ahead at batch end in GlobalWriteBatch.emit() (lookahead=True)
Same spirit as the delayed-primer reuse of incrementToNextRow: the look-ahead
does NOT re-implement the advance, it produces it from the same source -- so it
automatically covers E / Bias / packed-C1 with the exact same conditions.
`lookahead` only switches comment text (.la markers) and skips the per-element
bookkeeping (rowIncDirtyRowPtr + the "Fix for UseInitialStridesCD" banner). The
emitted instructions are otherwise identical, so non-CLS / per-element .s stays
byte-identical.
"""
module = Module("emitRowPtrAdvance")
kw = self.kernelWriter
if not lookahead:
self.rowIncDirtyRowPtr = 1
#assert (not kernel["ProblemType"]["UseInitialStridesCD"])
module.addComment1("Fix for UseInitialStridesCD, emitAddressSetupCode")
if len(kernel["PackedC1IndicesX"]) == 1:
strideChar = self.kernelWriter.states.indexChars[kernel["PackedC1IndicesX"][0]]
if lookahead:
module.add(self.addScaled(vgpr(kw.vgprs.cinRowPtr), vgpr(kw.vgprs.cinRowPtr), \
sgpr("StrideC%s"%strideChar), rowInc, tmpS01, \
comment="cinRowPtr.la: += StrideC%s*rowInc"%strideChar, \
comment1="cinRowPtr.la: += StrideC%s"%strideChar, \
comment2="scale StrideC%s for look-ahead"%strideChar))
module.add(self.addScaled(vgpr(kw.vgprs.coutRowPtrD), vgpr(kw.vgprs.coutRowPtrD), \
sgpr("StrideD%s"%strideChar), rowInc, tmpS01, \
comment="coutRowPtrD.la: += StrideD%s*rowInc"%strideChar, \
comment1="coutRowPtrD.la: += StrideD%s"%strideChar, \
comment2="scale StrideD%s for look-ahead"%strideChar))
else:
module.add(self.addScaled(vgpr(kw.vgprs.cinRowPtr), vgpr(kw.vgprs.cinRowPtr), \
sgpr("StrideC%s"%strideChar), rowInc, tmpS01, "ROWINC- Move cinRowPtr to next row"))
module.add(self.addScaled(vgpr(kw.vgprs.coutRowPtrD), vgpr(kw.vgprs.coutRowPtrD), \
sgpr("StrideD%s"%strideChar), rowInc, tmpS01, "Move coutRowPtrD to next row"))
if kernel["ProblemType"]["UseE"] and (kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1):
ecomment = "coutRowPtrE.la: += StrideE%s*rowInc"%strideChar if lookahead else "Move coutRowPtrE to next row"
module.add(self.addScaled(vgpr(kw.vgprs.coutRowPtrE), vgpr(kw.vgprs.coutRowPtrE), \
sgpr("StrideE%s"%strideChar), rowInc, tmpS01, ecomment))
if kw.vgprs.coutRowPtrBias != -1:
index = kernel["PackedC1IndicesX"][0] - 1
strideW1 = "Size%s" % "I" if index == 0 else ("J" if index == 1 else (kw.states.indexChars[index]))
bcomment = "coutRowPtrBias.la: += %s*rowInc"%strideW1 if lookahead else "Move coutRowPtrBias to next row"
module.add(self.addScaled(vgpr(kw.vgprs.coutRowPtrBias), vgpr(kw.vgprs.coutRowPtrBias), \
sgpr(strideW1), rowInc, tmpS01, bcomment))
if kw.vgprs.coutRowPtrGate != -1:
module.add(self.addScaled(vgpr(kw.vgprs.coutRowPtrGate), vgpr(kw.vgprs.coutRowPtrGate), \
sgpr("GateStride+0"), self.rowInc, tmpS01, "Move coutRowPtrGate to next row"))
elif len(kernel["PackedC1IndicesX"]) > 1:
module.add(kw.extractPackedCoord1ToRowStart(kernel, kernel["PackedC1IndicesX"] , self.coord1Vgpr, 'D'))
return module
def emitAddressCoordIncrement(self, kernel, ss, tmpVgpr, tmpS01, updateCoord1,
skipCrossBatchCoord1Inc=False):
"""
Emit code that computes the coord0 and coord1 for this element
sets self.coord0Vgpr with the address that holds the coord0 value for this element.
Input:
- tmpVgpr is a 1 temporary VGPR used for coord0 calculation on edges
"""
module = Module("emitAddressCoordIncrement")
kw = self.kernelWriter
(d1,d0,vc1,vc0) = self.element
self.coord0Vgpr = None # will set below
# module.addComment0("store addr=v%u coordOffset0=%u"% \
# (self.addr, self.coordOffset0))
module.addComment0("(d1,vc1,d0,vc0)=(%u,%u,%u,%u)"\
% (d1,vc1,d0,vc0))
if ss.optSingleColVgpr:
self.coord0Vgpr = kw.vgprs.coord0
elif not ss.optSharedColVgpr or (d1 == vc1 == 0):
# not share mode or first row always does the address calc math:
if self.coordOffset0 == 0:
self.coord0Vgpr = kw.vgprs.coord0
elif self.coordOffset0 <= 64:
self.coord0Vgpr = tmpVgpr
module.add(VAddCOU32(dst=vgpr(self.coord0Vgpr), dst1=VCC(), src0=vgpr(kw.vgprs.coord0), src1=self.coordOffset0, \
comment="coord0.1: coord0 += d0*sg0*VW + vc0"))
else:
self.coord0Vgpr = tmpVgpr
module.add(SMovB32(dst=sgpr(tmpS01), src=self.coordOffset0, comment="coordOffset0 d0=%u vc0=%u"%(d0, vc0)))
module.add(VAddCOU32(dst=vgpr(self.coord0Vgpr), dst1=VCC(), src0=vgpr(kw.vgprs.coord0), src1=sgpr(tmpS01), \
comment="coord0.2: coord0 += d0*sg0*VW + vc0"))
if self.newCoord1:
if not kernel["BufferStore"] or updateCoord1:
if self.rowInc== 0:
None
elif skipCrossBatchCoord1Inc:
module.addComment0("coord1Vgpr cross-batch inc skipped (CLS look-ahead)")
else:
# coord1 += rowInc (canonical, shared with CLS look-ahead).
# self.coord1Vgpr == kernelWriter.vgprs.coord1 (set in
# AsmStoreState), so this is an in-place advance.
module.add(self.emitCoord1Advance(self.rowInc, tmpS01, d0, vc0))
return module
def getRowPtr(self, kw, tc):
if tc == 'C':
return kw.vgprs.cinRowPtr
elif tc == 'E':
return kw.vgprs.coutRowPtrE
elif tc == 'Bias':
return kw.vgprs.coutRowPtrBias
elif tc == 'Gate':
return kw.vgprs.coutRowPtrGate
else:
return kw.vgprs.coutRowPtrD
def getAddrVgpr(self, kw, tc):
if tc == 'C':
return self.addrCVgpr
elif tc == 'E':
return self.addrEVgpr
elif tc == 'Bias':
return self.addrBiasVgpr
elif tc == 'Gate':
return self.addrGateVgpr
elif tc == 'TD':
return self.addrGSUSyncVgprs
else:
return self.addrDVgpr
# storeChar is 'C' or 'D'
# elementVgpr is coord0Vgpr*strideCD0, or optimized to just coord0Vgpr if strideCD0 is unit const
def emitExtractAndScalePackedDims(self, kernel, ss, tmpVgpr, storeChar):
module = Module("emitExtractAndScalePackedDims")
kw = self.kernelWriter
packedIndices = kernel["PackedC0IndicesX"]
packedBits = self.coord0Vgpr # start with coord0, will move to temp below
rowPtr = self.getRowPtr(kw, storeChar)
addrVgpr = self.getAddrVgpr(kw, storeChar)
bpe = kw.states.bpeCinternal if (tc == 'Bias') else (kw.states.bpeE if (tc == 'E') else kw.states.bpeCexternal)
for i,idx in enumerate(packedIndices[:-1]):
# vgprTmp assignments:
# - tmp+0 may be the incoming packed coordinate 0, used on replay too
# - tmp+1 is DIV output
# - tmp+2 is scratch
idxChar= INDEX_CHARS[idx]
module.addComment0("extract %s"%kw.sizeRef(idx))
assert(tmpVgpr+1 != packedBits) # bad since we still need packedBits below for remainder (can't overwrite here)
if kw.states.asmCaps["HasVgprMSB"]:
module.add(VMagicDiv(kernel["MagicDivAlg"], tmpVgpr+1, vgpr(packedBits), sgpr("MagicNumberSize%s"%idxChar), \
sgpr("MagicShiftSize%s"%idxChar), sgpr("MagicAbitSize%s"%idxChar)))
else:
module.add(MacroInstruction("V_MAGIC_DIV", \
args=[tmpVgpr+1, vgpr(packedBits), sgpr("MagicNumberSize%s"%idxChar), \
sgpr("MagicShiftSize%s"%idxChar), sgpr("MagicAbitSize%s"%idxChar) if kernel["MagicDivAlg"]==2 else "0"]))
# tmpVgpr+1 returns the quotient, tmpVgpr+2 is overwritten
# compute remainder, packedBits % sizeIdx - this is the 'extracted' index that must be scaled
# remainder is mul and sub
module.add(VMulLOU32(dst=vgpr(tmpVgpr+2), src0=vgpr(tmpVgpr+1), src1=kw.sizeRef(idx), \
comment="remainder part 1"))
module.add(VSubU32(dst=vgpr(tmpVgpr+2), src0=vgpr(packedBits), src1=vgpr(tmpVgpr+2),
comment="remainder part 2"))
if i==0:
module.add(VMulLOU32(dst=vgpr(addrVgpr), src0=vgpr(tmpVgpr+2), \
src1=kw.strideRef(storeChar, idx), comment="addrCalc <- scaled extracted dim"))
else:
module.add(VMulLOU32(dst=vgpr(tmpVgpr+2), src0=vgpr(tmpVgpr+2), \
src1=kw.strideRef(storeChar, idx), comment="scale extracted dim"))
module.add(VAddU32(dst=vgpr(addrVgpr), src0=vgpr(addrVgpr), \
src1=vgpr(tmpVgpr+2), comment="addrCalc += scaled extracted dim "))
if i < len(packedIndices)-2:
# TODO - might be able to eliminate this
module.add(VMovB32(dst=vgpr(tmpVgpr+0), src=vgpr(tmpVgpr+1), \
comment="Copy remaining bits for next divide"))
packedBits = tmpVgpr+0
if len(packedIndices)>1:
# if we unpacked something, then scale it to BPE
module.addComment0("extract final %s"%kw.sizeRef(packedIndices[-1]))
module.add(VMulLOU32(dst=vgpr(tmpVgpr+2), src0=vgpr(tmpVgpr+1), \
src1=kw.strideRef(storeChar, packedIndices[-1]), comment="scale final extracted dim"))
module.add(VAddU32(dst=vgpr(addrVgpr), src0=vgpr(addrVgpr), \
src1=vgpr(tmpVgpr+2), comment="addrCalc += scaled extracted dim "))
module.add(vectorAddMultiplyBpe(addrVgpr, rowPtr, addrVgpr, bpe, \
comment="packed: add rowPtr and scaleToBpe"))
return module
def emitScaleToBpe(self, kernel, ss, tmpVgpr, tmpSgpr, singleUpdate, tc, dim):
"""
Needs 3 temporary VGPRs
"""
module = Module("emitScaleToBpe")
kw = self.kernelWriter
(d1,d0,vc1,vc0) = self.element
rowPtr = self.getRowPtr(kw, tc)
addrVgpr = self.getAddrVgpr(kw, tc)
if tc == 'Gate' and addrVgpr is None:
addrVgpr = self.addrDVgpr
if tc == 'Bias':
bpe = kw.states.bpeCinternal
elif tc == 'E':
bpe = kw.states.bpeE
elif tc == 'Gate':
bpe = kw.states.bpeGate
else:
bpe = kw.states.bpeCexternal
if (tc == 'C' or tc == 'TD'):
bpe = bpe if (kernel["_GlobalAccumulation"] != "MultipleBufferSingleKernel") else kw.states.bpr * kernel["ProblemType"]["DestDataType"].numRegisters()
# set when we generate code that updates the address
# optSingleColVgpr and optSharedColVgpr attempt to minimize these updates
updatedAddr = False
# scale and set final address:
# Gate uses D's stride for col-direction (gate is per-element along col like D).
if tc == 'Gate':
stride0 = kw.strideRef('D', 0)
elif (tc == 'Bias') or (tc == 'ScaleAlphaVec') or (tc == 'ScaleAVec') or (tc == 'ScaleBVec'):
stride0 = kw.strideRef('D', 0)
else:
stride0 = kw.strideRef(tc, 0)
if kw.isConstUnitStride(stride0):
elementVgpr = self.coord0Vgpr
else:
module.add(VMulLOU32(dst=vgpr(addrVgpr), \
src0=vgpr(self.coord0Vgpr), \
src1=stride0, \
comment="scale element by non-unit stride"))
elementVgpr = addrVgpr
isSingleKernel = ((kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1) or kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel") or kernel["StreamK"] > 0
if ss.optSingleColVgpr:
# This is first element in the first batch, create a byte address that will
# be re-used by subsequent elements:
# if this element is firstInBatch - may need to set up a bpe-scaled row pointer for the batch:
# - need row-ptr start of each batch
assert (kw.vgprs.coord0 == self.coord0Vgpr) # elementAddr assignment above assumes these are the same
if singleUpdate:
updatedAddr = True
if tc == 'C':
singleColAddrUpdated = ss.singleColCAddrUpdated
elif tc == 'E':
singleColAddrUpdated = ss.singleColEAddrUpdated
elif tc == 'Bias':
singleColAddrUpdated = ss.singleColBiasAddrUpdated
elif tc == 'TD':
singleColAddrUpdated = ss.singleColTDAddrUpdated
elif tc == 'Gate':
singleColAddrUpdated = ss.singleColGateAddrUpdated
else:
singleColAddrUpdated = ss.singleColDAddrUpdated
if not singleColAddrUpdated or not ss.optSrdIncForRow:
if tc == 'Bias' and kw.states.useBias == DataDirection.READ:
coordVgpr = self.coord0Vgpr if dim == 0 else self.coord1Vgpr
module.add(SMulI32(dst=sgpr(tmpSgpr), src0=kernel["MacroTile%u"%dim], src1=sgpr("WorkGroup%u"%dim), comment="wgp%u * MT%u"%(dim, dim)))
module.add(VSubU32(dst=vgpr(self.addrBiasVgpr), src0=vgpr(coordVgpr), src1=sgpr(tmpSgpr)))
module.add(VLShiftLeftB32(dst=vgpr(self.addrBiasVgpr), \
shiftHex=hex(log2(kw.states.bpeCinternal)), \
src=vgpr(self.addrBiasVgpr), \
comment="Bias address scaled by BPE"))
if kernel["LdsOffsetBias"] != 0:
module.add(VAddU32(dst=vgpr(self.addrBiasVgpr), \
src0=(kernel["LdsOffsetBias"]), \
src1=vgpr(self.addrBiasVgpr), \
comment="add lds offset"))
ss.singleColBiasAddrUpdated = True
return module
if tc == 'ScaleAlphaVec' and kernel["ProblemType"]["UseScaleAlphaVec"] and isSingleKernel:
if self.addrScaleAlphaVecVgpr:
module.add(SMulI32(dst=sgpr(tmpSgpr), src0=kernel["MacroTile%u"%dim], src1=sgpr("WorkGroup%u"%dim), comment="wgp%u * MT%u"%(dim, dim)))
coordVgpr = self.coord0Vgpr if dim == 0 else self.coord1Vgpr
module.add(VSubU32(dst=vgpr(self.addrScaleAlphaVecVgpr), src0=vgpr(coordVgpr), src1=sgpr(tmpSgpr)))
module.add(VLShiftLeftB32(dst=vgpr(self.addrScaleAlphaVecVgpr), \
shiftHex=hex(log2(self.kernelWriter.states.bpeCinternal)), \
src=vgpr(self.addrScaleAlphaVecVgpr), \
comment="ScaleAlpha address scaled by BPE"))
if kernel["LdsOffsetBias"] != 0:
module.add(VAddU32(dst=vgpr(self.addrScaleAlphaVecVgpr), \
src0=(kernel["LdsOffsetBias"]), \
src1=vgpr(self.addrScaleAlphaVecVgpr), \
comment="add lds offset"))
return module
if tc == 'ScaleAVec' and (kernel["ProblemType"]["UseScaleAB"] == "Vector") and isSingleKernel:
if self.addrScaleAVecVgpr:
module.add(SMulI32(dst=sgpr(tmpSgpr), src0=kernel["MacroTile0"], src1=sgpr("WorkGroup0"), comment="wgp0 * MT0"))
module.add(VSubU32(dst=vgpr(self.addrScaleAVecVgpr), src0=vgpr(self.coord0Vgpr), src1=sgpr(tmpSgpr)))
module.add(VLShiftLeftB32(dst=vgpr(self.addrScaleAVecVgpr), \
shiftHex=hex(log2(kw.states.bpeCinternal)), \
src=vgpr(self.addrScaleAVecVgpr), \
comment="ScaleAVec address scaled by BPE"))
if kernel["LdsOffsetBias"] != 0:
module.add(VAddU32(dst=vgpr(self.addrScaleAVecVgpr), \
src0=(kernel["LdsOffsetBias"]), \
src1=vgpr(self.addrScaleAVecVgpr), \
comment="add lds offset"))
return module
if tc == 'ScaleBVec' and (kernel["ProblemType"]["UseScaleAB"] == "Vector") and isSingleKernel:
if self.addrScaleBVecVgpr:
module.add(SMulI32(dst=sgpr(tmpSgpr), src0=kernel["MacroTile1"], src1=sgpr("WorkGroup1"), comment="wgp1 * MT1"))
module.add(VSubU32(dst=vgpr(self.addrScaleBVecVgpr), src0=vgpr(self.coord1Vgpr), src1=sgpr(tmpSgpr)))
module.add(VLShiftLeftB32(dst=vgpr(self.addrScaleBVecVgpr), \
shiftHex=hex(log2(kw.states.bpeCinternal)), \
src=vgpr(self.addrScaleBVecVgpr), \
comment="ScaleBVec address scaled by BPE"))
if kernel["LdsOffsetBias"] != 0:
module.add(VAddU32(dst=vgpr(self.addrScaleBVecVgpr), \
src0=(kernel["LdsOffsetBias"]), \
src1=vgpr(self.addrScaleBVecVgpr), \
comment="add lds offset"))
return module
if tc == 'C':
ss.singleColCAddrUpdated = True
elif tc == 'E':
ss.singleColEAddrUpdated = True
elif tc == 'Bias':
ss.singleColBiasAddrUpdated = True
elif tc == 'TD':
ss.singleColTDAddrUpdated = True
elif tc == 'Gate':
ss.singleColGateAddrUpdated = True
else:
ss.singleColDAddrUpdated = True
module.add(vectorAddMultiplyBpe(addrVgpr, rowPtr, elementVgpr, bpe, \
comment="optSingleColVgpr scaleToBpe: sharedAddrVgpr <- cinRowPtr + coord0, scaled by BPE. BSHERE:coord0=%d, coord0Vgpr=%d"%(kw.vgprs.coord0, self.coord0Vgpr)))
elif ss.optSharedColVgpr:
# Need an address calculation for the first address in each row:
if d1==0 and vc1==0:
if tc == 'Bias' and kw.states.useBias == DataDirection.READ:
coordVgpr = self.coord0Vgpr if dim == 0 else self.coord1Vgpr
module.add(SMulI32(dst=sgpr(tmpSgpr), src0=kernel["MacroTile%u"%dim], src1=sgpr("WorkGroup%u"%dim), comment="wgp%u * MT%u"%(dim, dim)))
module.add(VSubU32(dst=vgpr(self.addrBiasVgpr), src0=vgpr(coordVgpr), src1=sgpr(tmpSgpr)))
module.add(VLShiftLeftB32(dst=vgpr(self.addrBiasVgpr), \
shiftHex=hex(log2(kw.states.bpeCinternal)), \
src=vgpr(self.addrBiasVgpr), \
comment="Bias address scaled by BPE"))
if kernel["LdsOffsetBias"] != 0:
module.add(VAddU32(dst=vgpr(self.addrBiasVgpr), \
src0=(kernel["LdsOffsetBias"]), \
src1=vgpr(self.addrBiasVgpr), \
comment="add lds offset"))
return module
if tc == 'ScaleAlphaVec' and kernel["ProblemType"]["UseScaleAlphaVec"] and isSingleKernel:
if self.addrScaleAlphaVecVgpr:
module.add(SMulI32(dst=sgpr(tmpSgpr), src0=kernel["MacroTile%u"%dim], src1=sgpr("WorkGroup%u"%dim), comment="wgp%u * MT%u"%(dim, dim)))
coordVgpr = self.coord0Vgpr if dim == 0 else self.coord1Vgpr
module.add(VSubU32(dst=vgpr(self.addrScaleAlphaVecVgpr), src0=vgpr(coordVgpr), src1=sgpr(tmpSgpr)))
module.add(VLShiftLeftB32(dst=vgpr(self.addrScaleAlphaVecVgpr), \
shiftHex=hex(log2(self.kernelWriter.states.bpeCinternal)), \
src=vgpr(self.addrScaleAlphaVecVgpr), \
comment="ScaleAlpha address scaled by BPE"))
if kernel["LdsOffsetBias"] != 0:
module.add(VAddU32(dst=vgpr(self.addrScaleAlphaVecVgpr), \
src0=(kernel["LdsOffsetBias"]), \
src1=vgpr(self.addrScaleAlphaVecVgpr), \
comment="add lds offset"))
return module
if tc == 'ScaleAVec' and (kernel["ProblemType"]["UseScaleAB"] == "Vector") and isSingleKernel:
if self.addrScaleAVecVgpr:
module.add(SMulI32(dst=sgpr(tmpSgpr), src0=kernel["MacroTile0"], src1=sgpr("WorkGroup0"), comment="wgp0 * MT0"))
module.add(VSubU32(dst=vgpr(self.addrScaleAVecVgpr), src0=vgpr(self.coord0Vgpr), src1=sgpr(tmpSgpr)))
module.add(VLShiftLeftB32(dst=vgpr(self.addrScaleAVecVgpr), \
shiftHex=hex(log2(kw.states.bpeCinternal)), \
src=vgpr(self.addrScaleAVecVgpr), \
comment="ScaleAVec address scaled by BPE"))
if kernel["LdsOffsetBias"] != 0:
module.add(VAddU32(dst=vgpr(self.addrScaleAVecVgpr), \
src0=(kernel["LdsOffsetBias"]), \
src1=vgpr(self.addrScaleAVecVgpr), \
comment="add lds offset"))
return module
if tc == 'ScaleBVec' and (kernel["ProblemType"]["UseScaleAB"] == "Vector") and isSingleKernel:
if self.addrScaleBVecVgpr:
module.add(SMulI32(dst=sgpr(tmpSgpr), src0=kernel["MacroTile1"], src1=sgpr("WorkGroup1"), comment="wgp1 * MT1"))
module.add(VSubU32(dst=vgpr(self.addrScaleBVecVgpr), src0=vgpr(self.coord1Vgpr), src1=sgpr(tmpSgpr)))
module.add(VLShiftLeftB32(dst=vgpr(self.addrScaleBVecVgpr), \
shiftHex=hex(log2(kw.states.bpeCinternal)), \
src=vgpr(self.addrScaleBVecVgpr), \
comment="ScaleBVec address scaled by BPE"))
if kernel["LdsOffsetBias"] != 0:
module.add(VAddU32(dst=vgpr(self.addrScaleBVecVgpr), \
src0=(kernel["LdsOffsetBias"]), \
src1=vgpr(self.addrScaleBVecVgpr), \
comment="add lds offset"))
return module
packedIndices = kernel["PackedC0IndicesX"]
if len(packedIndices) > 1:
updatedAddr = True
module.add(self.emitExtractAndScalePackedDims(kernel, ss, tmpVgpr, tc))
else:
updatedAddr = True
module.add(vectorAddMultiplyBpe(addrVgpr, rowPtr, elementVgpr, bpe,
comment="optSharedColVgpr scaleToBpe for first row: col addr <- cinRowPtr + coord0, scaled by BPE"))
else:
# Generate final address calculation (to bytes) for each element
# The unpacking takes 8-10 instructions so could be worth optimizing someday :
# each col has same offset so could create a class to hold column-specific state including
# the byte address offset for that col and the mask in/out.
if tc == 'Bias' and kw.states.useBias == DataDirection.READ:
coordVgpr = self.coord0Vgpr if dim == 0 else self.coord1Vgpr
module.add(SMulI32(dst=sgpr(tmpSgpr), src0=kernel["MacroTile%u"%dim], src1=sgpr("WorkGroup%u"%dim), comment="wgp%u * MT%u"%(dim, dim)))
module.add(VSubU32(dst=vgpr(self.addrBiasVgpr), src0=vgpr(coordVgpr), src1=sgpr(tmpSgpr)))
module.add(VLShiftLeftB32(dst=vgpr(self.addrBiasVgpr), \
shiftHex=hex(log2(kw.states.bpeCinternal)), \
src=vgpr(self.addrBiasVgpr), \
comment="Bias address scaled by BPE"))
if kernel["LdsOffsetBias"] != 0:
module.add(VAddU32(dst=vgpr(self.addrBiasVgpr), \
src0=(kernel["LdsOffsetBias"]), \
src1=vgpr(self.addrBiasVgpr), \
comment="add lds offset"))
return module
if tc == 'ScaleAlphaVec' and kernel["ProblemType"]["UseScaleAlphaVec"] and isSingleKernel:
if self.addrScaleAlphaVecVgpr:
module.add(SMulI32(dst=sgpr(tmpSgpr), src0=kernel["MacroTile%u"%dim], src1=sgpr("WorkGroup%u"%dim), comment="wgp%u * MT%u"%(dim, dim)))
coordVgpr = self.coord0Vgpr if dim == 0 else self.coord1Vgpr
module.add(VSubU32(dst=vgpr(self.addrScaleAlphaVecVgpr), src0=vgpr(coordVgpr), src1=sgpr(tmpSgpr)))
module.add(VLShiftLeftB32(dst=vgpr(self.addrScaleAlphaVecVgpr), \
shiftHex=hex(log2(self.kernelWriter.states.bpeCinternal)), \
src=vgpr(self.addrScaleAlphaVecVgpr), \
comment="ScaleAlpha address scaled by BPE"))
if kernel["LdsOffsetBias"] != 0:
module.add(VAddU32(dst=vgpr(self.addrScaleAlphaVecVgpr), \
src0=(kernel["LdsOffsetBias"]), \
src1=vgpr(self.addrScaleAlphaVecVgpr), \
comment="add lds offset"))
return module
if tc == 'ScaleAVec' and (kernel["ProblemType"]["UseScaleAB"] == "Vector") and isSingleKernel:
if self.addrScaleAVecVgpr:
module.add(SMulI32(dst=sgpr(tmpSgpr), src0=kernel["MacroTile0"], src1=sgpr("WorkGroup0"), comment="wgp0 * MT0"))
module.add(VSubU32(dst=vgpr(self.addrScaleAVecVgpr), src0=vgpr(self.coord0Vgpr), src1=sgpr(tmpSgpr)))
module.add(VLShiftLeftB32(dst=vgpr(self.addrScaleAVecVgpr), \
shiftHex=hex(log2(kw.states.bpeCinternal)), \
src=vgpr(self.addrScaleAVecVgpr), \
comment="ScaleAVec address scaled by BPE"))
if kernel["LdsOffsetBias"] != 0:
module.add(VAddU32(dst=vgpr(self.addrScaleAVecVgpr), \
src0=(kernel["LdsOffsetBias"]), \
src1=vgpr(self.addrScaleAVecVgpr), \
comment="add lds offset"))
return module
if tc == 'ScaleBVec' and (kernel["ProblemType"]["UseScaleAB"] == "Vector") and isSingleKernel:
if self.addrScaleBVecVgpr:
module.add(SMulI32(dst=sgpr(tmpSgpr), src0=kernel["MacroTile1"], src1=sgpr("WorkGroup1"), comment="wgp1 * MT1"))
module.add(VSubU32(dst=vgpr(self.addrScaleBVecVgpr), src0=vgpr(self.coord1Vgpr), src1=sgpr(tmpSgpr)))
module.add(VLShiftLeftB32(dst=vgpr(self.addrScaleBVecVgpr), \
shiftHex=hex(log2(kw.states.bpeCinternal)), \
src=vgpr(self.addrScaleBVecVgpr), \
comment="ScaleBVec address scaled by BPE"))
if kernel["LdsOffsetBias"] != 0:
module.add(VAddU32(dst=vgpr(self.addrScaleBVecVgpr), \
src0=(kernel["LdsOffsetBias"]), \
src1=vgpr(self.addrScaleBVecVgpr), \
comment="add lds offset"))
return module
packedIndices = kernel["PackedC0IndicesX"]
if len(packedIndices) > 1:
updatedAddr = True
module.add(self.emitExtractAndScalePackedDims(kernel, ss, tmpVgpr, tc))
else:
updatedAddr = True
module.add(vectorAddMultiplyBpe(addrVgpr, rowPtr, elementVgpr, bpe, \
comment="scaleToBpe: accumulate d0 lower and *= bpe into Cin addr"))
# if not optSrdIncForRow then we may have moved the row pointer
# and depending on paths above may not have refreshed addrVgpr already.
# if so - do it here:
if self.rowIncDirtyRowPtr and not updatedAddr:
module.add(vectorAddMultiplyBpe(addrVgpr, rowPtr, kw.vgprs.coord0, bpe, \
comment="scaleToBpe: Update address with new rowPtr"))
return module
def edgeProtectCode(self, kernel, edge, beta, atomic, mask, tmpSgpr):
"""
Generate code to protect address offset in edge case
"""
module = Module("edgeProtectCode")
kw = self.kernelWriter
tmpS01 = tmpSgpr
tmpS23 = tmpSgpr+self.kernelWriter.states.laneSGPRCount
laneSGPRCount = self.kernelWriter.states.laneSGPRCount
wavefrontSize = kernel["WavefrontSize"]
# Now do the edge check and compute the address in bytes:
if kernel["BufferStore"]:
if edge and (not kernel["StoreRemapVectorWidth"] or (kernel["StoreRemapVectorWidth"] and (beta or kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel"))):
# Set address to -1 if OOB on either dimension
# and only check the x/coord0 index here, save a couple inst
sizeBoundary = [0,0]
sizeBoundary[0] = \
sgpr("PackedSize0") if len(kernel["PackedC0IndicesX"]) > 1 \
else kw.sizeRef(kernel["ProblemType"]["Index0"])
sizeBoundary[1] = \
sgpr("PackedSize1") if len(kernel["PackedC1IndicesX"]) > 1 \
else kw.sizeRef(kernel["ProblemType"]["Index1"])
module.add(VCmpLtU32(dst=sgpr(tmpS01,laneSGPRCount), src0=vgpr(self.coord0Vgpr), src1=sizeBoundary[0], comment="coord0 < size0" ))
module.add(VCmpLtU32(dst=sgpr(mask,laneSGPRCount), src0=vgpr(self.coord1Vgpr), src1=sizeBoundary[1], comment="coord1 < size1" ))
SAndX = SAndB64 if wavefrontSize == 64 else SAndB32
module.add(SAndX(dst=sgpr(mask,laneSGPRCount), src0=sgpr(tmpS01,laneSGPRCount), src1=sgpr(mask,laneSGPRCount), comment="in0 && in1" ))
else:
module.add(VCmpLtU32(dst=sgpr(tmpS01,laneSGPRCount), src0=vgpr(self.coord0Vgpr), src1=sgpr("SizesFree+0"), comment="coord0 < size0" ))
module.add(VCmpLtU32(dst=sgpr(tmpS23,laneSGPRCount), src0=vgpr(self.coord1Vgpr), src1=sgpr("SizesFree+1"), comment="coord1 < size1" ))
SAndX = SAndB64 if wavefrontSize == 64 else SAndB32
module.add(SAndX(dst=sgpr(mask,laneSGPRCount), src0=sgpr(tmpS01,laneSGPRCount), src1=sgpr(tmpS23,laneSGPRCount), comment="in0 && in1" ))
if (beta or atomic):
SMovX = SMovB64 if wavefrontSize == 64 else SMovB32
module.add(SMovX(dst=EXEC(), src=sgpr(mask,laneSGPRCount), comment="sgprs -> exec" ))
return module
# TODO - mask should be part of AddrCalc state not passed as parm
def emitAddressSetupCode(self, kernel, tPB, ss, tmpVgpr, tmpS01, edge, beta, atomic, elementIdx, addrVgpr,
skipCrossBatchAdvance=False):
"""
Generate code to set up the address vgpr
Input:
tmpVgpr : two temp vgprs
Output:
Returns kStr with appropriate setup code
Sets self.coord0Vgpr with vgpr that contains the coord0 for this element. This enables
optimization - if no setup code is required the coord0 can be the input.
"""
module = Module("emitAddressSetupCode")
kw = self.kernelWriter
updateCoord1 = (edge or len(kernel["PackedC1IndicesX"]) > 1)
module.add(self.emitAddressCoordIncrement(kernel, ss, tmpVgpr, tmpS01, updateCoord1,
skipCrossBatchCoord1Inc=skipCrossBatchAdvance))
# calculate flat load offset
if not kernel["BufferStore"]:
# flat: in-bounds exec mask
# global offset macro (requires 3 tmpVgpr)
# final address = C + index*bytes
params = ["%u" % addrVgpr]
for i in range(0, kernel["ProblemType"]["NumIndicesC"]):
if i == kernel["ProblemType"]["Index0"]:
params.append("%s" % (self.coord0Vgpr))
elif i == kernel["ProblemType"]["Index1"]:
params.append("%s" % (self.coord1Vgpr))
else: # just a group index
params.append("sgprWorkGroup%u"%i)
params.append("%s" % (tmpVgpr+2))
if kw.states.asmCaps["HasVgprMSB"]:
module.add(kw.globalOffset(kernel, None, "C", params))
else:
module.add(MacroInstruction(name="GLOBAL_OFFSET_C", args=params))
module.add(vectorMultiply64Bpe(addrVgpr, addrVgpr, kw.states.bpeCexternal, tmpVgpr))
module.add(VMovB32(dst=vgpr(tmpVgpr+2), src=vgpr(addrVgpr+0), comment="temp store offset 0"))
module.add(VMovB32(dst=vgpr(tmpVgpr+3), src=vgpr(addrVgpr+1), comment="temp store offset 1"))
# Move the row ptr VGPR
# optSrdIncForRow moves the SRD so don't move here
if not ss.optSrdIncForRow and kernel["BufferStore"]:
if self.rowInc != 0 and skipCrossBatchAdvance:
self.rowIncDirtyRowPtr = 1
module.addComment1("rowPtr cross-batch advance skipped (CLS look-ahead)")
elif self.rowInc != 0:
# Per-element cross-row advance via the canonical single-source helper
# (also used by the CLS look-ahead in GlobalWriteBatch.emit()).
module.add(self.emitRowPtrAdvance(kernel, ss, tmpS01, self.rowInc, lookahead=False))
# Shift Pointer for MFMA:
# For MFMA shift pointer, correct data is stored in another thread.
# Therefore, MFMA cannot use v_mov to amend store data
# It needs to modify the coord1 of thread directly.
# (Use ShiftVectorComponentsMFMA now)
if 0 and (not kernel["SourceSwap"]) and (not kernel["GuaranteeNoPartialB"]) and tPB["rtv"] and kernel["EnableMatrixInstruction"] and edge:
(d1,d0,vc1,vc0) = self.element
if (d1 == vc1 == d0 == vc0 == 0) or self.newCoord1:
sgprCnt = self.kernelWriter.states.laneSGPRCount
waveSize = kernel["WavefrontSize"]
packedC1 = kernel["PackedC1IndicesX"]
strideC1 = "StrideC%s" % (kw.states.indexChars[packedC1[0]])
strideD1 = "StrideD%s" % (kw.states.indexChars[packedC1[0]])
strideE1 = "StrideE%s" % (kw.states.indexChars[packedC1[0]])
index = packedC1[0] - 1
strideW1 = "Size%s" % "I" if index == 0 else ("J" if index == 1 else (kw.states.indexChars[index]))
module.addComment1("shift vector components d1")
vw = kernel["GlobalReadVectorWidthB"]
vTmp1 = tmpVgpr
vTmp2 = tmpVgpr+1
sTmp1 = tmpS01
sTmp2 = tmpS01+sgprCnt
# check conditions
module.add(VBfiB32(dst=vgpr(vTmp1), src0=vw-1, src1=0, src2=vgpr(self.coord1Vgpr), comment="coord1 & ~(vw-1)"))
module.add(VBfiB32(dst=vgpr(vTmp2), src0=vw-1, src1=0, src2=sgpr("SizesFree+%u"%tPB["idx"]), comment="sizeFree & ~(vw-1)"))
module.add(VCmpEQU32(dst=sgpr(sTmp1,sgprCnt), src0=vgpr(vTmp1), src1=vgpr(vTmp2), comment="if coord1 is in edge glvw"))
module.add(VAndB32(dst=vgpr(vTmp2), src0=sgpr("SizesFree+%u"%tPB["idx"]), src1=vw-1, comment="sizeFree mod VW"))
module.add(VCmpGtU32(dst=sgpr(sTmp2,sgprCnt), src0=vgpr(vTmp2), src1=0, comment="this problem is not multiple size of glvw"))
SAndBX = SAndB64 if waveSize == 64 else SAndB32
module.add(SAndBX(dst=sgpr(sTmp1,sgprCnt), src0=sgpr(sTmp1,sgprCnt), src1=sgpr(sTmp2,sgprCnt), comment="AND both conditions"))
# calculate new coord
module.add(VAddU32(dst=vgpr(vTmp1), src0=vgpr(self.coord1Vgpr), src1=vgpr(vTmp2), comment="shift coord1"))
module.add(VBfiB32(dst=vgpr(vTmp1), src0=vw-1, src1=vgpr(vTmp1), src2=sgpr("SizesFree+%u"%tPB["idx"]), comment="new coord1 = (shift coord1 & (vw-1)) | (sizeFree & ~(vw-1))"))
module.add(VSubI32(dst=vgpr(vTmp2), src0=vgpr(vTmp1), src1=vgpr(self.coord1Vgpr), comment="shift how many column"))
module.add(VCndMaskB32(dst=vgpr(self.coord1Vgpr), src0=vgpr(self.coord1Vgpr), src1=vgpr(vTmp1), \
src2=sgpr(sTmp1,sgprCnt), comment="set new coord1 if meet conditions" ))
module.add(VMadI32I24(dst=vgpr(vTmp1), src0=sgpr(strideC1), src1=vgpr(vTmp2), src2=vgpr(kw.vgprs.cinRowPtr), \
comment="new rowStart address += shift column * StridesC"))
module.add(VCndMaskB32(dst=vgpr(kw.vgprs.cinRowPtr), src0=vgpr(kw.vgprs.cinRowPtr), src1=vgpr(vTmp1), src2=sgpr(sTmp1,sgprCnt), \
comment="set new rowStart if meet conditions" ))
module.add(VMadI32I24(dst=vgpr(vTmp1), src0=sgpr(strideD1), src1=vgpr(vTmp2), src2=vgpr(kw.vgprs.coutRowPtrD), \
comment="new rowStart address += shift column * StridesD"))
module.add(VCndMaskB32(dst=vgpr(kw.vgprs.coutRowPtrD), src0=vgpr(kw.vgprs.coutRowPtrD), src1=vgpr(vTmp1), src2=sgpr(sTmp1,sgprCnt), \
comment="set new rowStart if meet conditions" ))
if kernel["ProblemType"]["UseE"] and (kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1):
module.add(VMadI32I24(dst=vgpr(vTmp1), src0=sgpr(strideE1), src1=vgpr(vTmp2), src2=vgpr(kw.vgprs.coutRowPtrE), \
comment="new rowStart address += shift column * StridesE"))
module.add(VCndMaskB32(dst=vgpr(kw.vgprs.coutRowPtrE), src0=vgpr(kw.vgprs.coutRowPtrE), src1=vgpr(vTmp1), src2=sgpr(sTmp1,sgprCnt), \
comment="set new rowStart if meet conditions" ))
if kw.vgprs.coutRowPtrBias != -1:
module.add(VMadI32I24(dst=vgpr(vTmp1), src0=sgpr(strideW1), src1=vgpr(vTmp2), src2=vgpr(kw.vgprs.coutRowPtrBias), \
comment="new rowStart address += shift column * StridesW"))
module.add(VCndMaskB32(dst=vgpr(kw.vgprs.coutRowPtrBias), src0=vgpr(kw.vgprs.coutRowPtrBias), src1=vgpr(vTmp1), src2=sgpr(sTmp1,sgprCnt), \
comment="set new rowStart if meet conditions" ))
if kernel["StoreRemapVectorWidth"]:
ldsPad = max(kernel["StoreRemapVectorWidth"],kernel["MIOutputVectorWidth"])
module.add(VMovB32(dst=vgpr(vTmp1), src=hex((kernel["MacroTile0"]+ldsPad)*kw.states.bpeCexternal), \
comment="lds byte stride = (MT0 + PAD) * bpe"))
module.add(VMadI32I24(dst=vgpr(vTmp1), src0=vgpr(vTmp1), src1=vgpr(vTmp2), src2=vgpr(kw.vgprs.storeRemapLW), \
comment="new lds write address += shift column * Lds byte Stride"))
module.add(VCndMaskB32(dst=vgpr(kw.vgprs.storeRemapLW), src0=vgpr(kw.vgprs.storeRemapLW), src1=vgpr(vTmp1), \
src2=sgpr(sTmp1,sgprCnt), comment="set new rowStart if meet conditions" ))
if kernel["ProblemType"]["UseE"] and (kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1):
printExit("Output E does not support StoreRemapVectorWidth")
if kw.vgprs.coutRowPtrBias != -1:
printExit("Bias reduction does not support StoreRemapVectorWidth")
module.addSpaceLine()
return module
def emitLdChange(self, kernel, ss, tc, edge, beta, mask, bufferOOB, singleUpdate, tmpVgpr, tmpSgpr, addrVgpr, BufAddr, dim):
"""
Generate code for final C read/D write address
"""
laneSGPRCount = self.kernelWriter.states.laneSGPRCount
module = Module("emitLdChange")
isSingleKernel = ((kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1) or kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel") or kernel["StreamK"] > 0
if kernel["BufferStore"]:
module.add(self.emitScaleToBpe(kernel, ss, tmpVgpr, tmpSgpr, singleUpdate, tc, dim))
if edge and (not kernel["StoreRemapVectorWidth"] or (kernel["StoreRemapVectorWidth"] and (beta or kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel"))) and \
(tc != 'ScaleAlphaVec' and (not (tc == 'Bias' and self.kernelWriter.states.useBias == DataDirection.READ)) and tc != 'ScaleAVec' and tc != 'ScaleBVec'):
module.add(VCndMaskB32(dst=vgpr(addrVgpr), src0=vgpr(bufferOOB), src1=vgpr(addrVgpr), \
src2=sgpr(mask,laneSGPRCount), comment="LD%s clip if OOB. offset" % tc ))
# dot2: for every NumWaveSplitK contiguous threads, only the rightmost (with the largest threadIdx) one need to write value to D matrix.
if kernel["NumWaveSplitK"] > 1 and (tc == 'D' or tc == 'TD'):
kidx = bufferOOB-1
module.add(VAndB32(dst=vgpr(kidx), src0=(kernel["NumWaveSplitK"]-1), src1=vgpr("Serial"), comment="kidx = Serial % NumWaveSplitK"))
module.add(VCmpXNeU32(dst=EXEC(), src0=(kernel["NumWaveSplitK"]-1), src1=vgpr(kidx), comment="kidx == NumWaveSplitK-1" ))
module.add(VMovB32(dst=vgpr(addrVgpr), src=vgpr(bufferOOB), comment="LD%s clip if kidx != NumWaveSplitK-1." % tc))
module.add(SMovB64(dst=EXEC(), src=-1, comment="Reset exec mask"))
else:
if tc == 'Bias' and kernel["ProblemType"]["UseBias"] and isSingleKernel:
module.add(SMulI32(dst=sgpr(tmpSgpr), src0=kernel["MacroTile%u"%dim], src1=sgpr("WorkGroup%u"%dim), comment="wgp%u * MT%u"%(dim, dim)))
coordVgpr = self.coord0Vgpr if dim == 0 else self.coord1Vgpr
module.add(VSubU32(dst=vgpr(self.addrBiasVgpr), src0=vgpr(coordVgpr), src1=sgpr(tmpSgpr)))
module.add(VLShiftLeftB32(dst=vgpr(self.addrBiasVgpr), \
shiftHex=hex(log2(self.kernelWriter.states.bpeCinternal)), \
src=vgpr(self.addrBiasVgpr), \
comment="Bias address scaled by BPE"))
if kernel["LdsOffsetBias"] != 0:
module.add(VAddU32(dst=vgpr(self.addrBiasVgpr), \
src0=(kernel["LdsOffsetBias"]), \
src1=vgpr(self.addrBiasVgpr), \
comment="add lds offset"))
elif tc == 'ScaleAlphaVec' and kernel["ProblemType"]["UseScaleAlphaVec"] and isSingleKernel:
if self.addrScaleAlphaVecVgpr:
module.add(SMulI32(dst=sgpr(tmpSgpr), src0=kernel["MacroTile%u"%dim], src1=sgpr("WorkGroup%u"%dim), comment="wgp%u * MT%u"%(dim, dim)))
coordVgpr = self.coord0Vgpr if dim == 0 else self.coord1Vgpr
module.add(VSubU32(dst=vgpr(self.addrScaleAlphaVecVgpr), src0=vgpr(coordVgpr), src1=sgpr(tmpSgpr)))
module.add(VLShiftLeftB32(dst=vgpr(self.addrScaleAlphaVecVgpr), \
shiftHex=hex(log2(self.kernelWriter.states.bpeCinternal)), \
src=vgpr(self.addrScaleAlphaVecVgpr), \
comment="ScaleAlpha address scaled by BPE"))
if kernel["LdsOffsetBias"] != 0:
module.add(VAddU32(dst=vgpr(self.addrScaleAlphaVecVgpr), \
src0=(kernel["LdsOffsetBias"]), \
src1=vgpr(self.addrScaleAlphaVecVgpr), \
comment="add lds offset"))
elif tc == 'ScaleAVec' and (kernel["ProblemType"]["UseScaleAB"] == "Vector") and isSingleKernel:
if self.addrScaleAVecVgpr:
module.add(SMulI32(dst=sgpr(tmpSgpr), src0=kernel["MacroTile0"], src1=sgpr("WorkGroup0"), comment="wgp0 * MT0"))
module.add(VSubU32(dst=vgpr(self.addrScaleAVecVgpr), src0=vgpr(self.coord0Vgpr), src1=sgpr(tmpSgpr)))
module.add(VLShiftLeftB32(dst=vgpr(self.addrScaleAVecVgpr), \
shiftHex=hex(log2(self.kernelWriter.states.bpeCinternal)), \
src=vgpr(self.addrScaleAVecVgpr), \
comment="ScaleAVec address scaled by BPE"))
if kernel["LdsOffsetBias"] != 0:
module.add(VAddU32(dst=vgpr(self.addrScaleAVecVgpr), \
src0=(kernel["LdsOffsetBias"]), \
src1=vgpr(self.addrScaleAVecVgpr), \
comment="add lds offset"))
elif tc == 'ScaleBVec' and (kernel["ProblemType"]["UseScaleAB"] == "Vector") and isSingleKernel:
if self.addrScaleBVecVgpr:
module.add(SMulI32(dst=sgpr(tmpSgpr), src0=kernel["MacroTile1"], src1=sgpr("WorkGroup1"), comment="wgp1 * MT1"))
module.add(VSubU32(dst=vgpr(self.addrScaleBVecVgpr), src0=vgpr(self.coord1Vgpr), src1=sgpr(tmpSgpr)))
module.add(VLShiftLeftB32(dst=vgpr(self.addrScaleBVecVgpr), \
shiftHex=hex(log2(self.kernelWriter.states.bpeCinternal)), \
src=vgpr(self.addrScaleBVecVgpr), \
comment="ScaleBVec address scaled by BPE"))
if kernel["LdsOffsetBias"] != 0:
module.add(VAddU32(dst=vgpr(self.addrScaleBVecVgpr), \
src0=(kernel["LdsOffsetBias"]), \
src1=vgpr(self.addrScaleBVecVgpr), \
comment="add lds offset"))
else:
# store a copy of the offset in 2 of the tmpVgpr for D
module.add(VAddCOU32(dst=vgpr(addrVgpr+0), dst1=VCC(), src0=vgpr(BufAddr+0), src1=vgpr(tmpVgpr+2), \
comment="addrVgpr = C(D) + index*bytes (lo)" ))
module.add(VAddCCOU32(dst=vgpr(addrVgpr+1), dst1=VCC(), src0=vgpr(BufAddr+1), src1=vgpr(tmpVgpr+3), \
src2=VCC(), comment="addrVgpr = C(D) + index*bytes (hi)"))
return module
@staticmethod
def incrementSrdMultipleRows(srcDstBaseSgpr: str, strideSgpr: str, tmpSgpr: str, numRows: int, bpe: int) -> Module:
module = Module("incrementToNextRows")
if numRows > 1:
module.add(SMulI32(dst=sgpr(tmpSgpr), \
src0=sgpr(strideSgpr), \
src1=numRows*bpe, \
comment="scale %s *= numRows(%u) * bpe"%(strideSgpr, numRows)))
elif numRows < 0:
module.add(SMulI32(dst=sgpr(tmpSgpr), \
src0=sgpr(strideSgpr), \
src1=(-numRows)*bpe, \
comment="scale %s *= numRows(%u) * bpe"%(strideSgpr, numRows)))
else:
module.add(SLShiftLeftB32(dst=sgpr(tmpSgpr), \
src=sgpr(strideSgpr), \
shiftHex=log2(bpe), \
comment="incToNextRow: Scale by BPE"))
dstLow = f"{srcDstBaseSgpr}+0"
dstHigh = f"{srcDstBaseSgpr}+1"
if numRows >= 0:
module.add(SAddU32(dst=sgpr(dstLow), \
src0=sgpr(dstLow), \
src1=sgpr(tmpSgpr), \
comment="incToNextRow: gra SRD += inc(lower)" ))
module.add(SAddCU32(dst=sgpr(dstHigh), \
src0=sgpr(dstHigh), \
src1=0, \
comment="incToNextRow: gra SRD += inc(upper)" ))
else:
module.add(SSubU32(dst=sgpr(dstLow), \
src0=sgpr(dstLow), \
src1=sgpr(tmpSgpr), \
comment="incToNextRow: gra SRD -= inc(lower)" ))
module.add(SSubBU32(dst=sgpr(dstHigh), \
src0=sgpr(dstHigh), \
src1=0, \
comment="incToNextRow: gra SRD -= inc(upper)" ))
return module
def incrementToNextRow(self, kernel, tc, ss, stmp, forceinitrow0=0,
overrideAfterPrimerRows=0, bpeType=None, dst=-1):
"""
Generate code to move to the next row(s)
If optSrdIncForRow, this will move the SRD forward
If not, this could generate some other instructions
CompactLoopStore (CLS-loop mode) uses a DELAYED primer pattern: s_add
uses s[stmp] prepared by the PREVIOUS call, and the stride compute
(s_lshl/s_mul) at the END primes s[stmp] for the NEXT call. Every elt
must emit (including rowInc==0) to preserve the s[stmp] chain --
elt-0's s_add is a no-op (s[stmp]=0 from preamble init) + s_lshl that
primes s[stmp]=StrideCD1<<log2(bpe) for elt-1's real advance. Caller
passes `forceinitrow0=1` to allow this chain-seed emit when rowInc==0.
`overrideAfterPrimerRows` > 0 replaces the AFTER-primer's numRows with
the given value. The caller (GlobalWriteBatch) computes this as the
rowInc of the NEXT EMITTING elt (look-ahead), so the primer that
elt-N writes to s[stmp] is exactly the increment elt-(N+1) needs.
Without override, the primer would carry elt-N's own rowInc, which
elt-(N+1) would then erroneously consume -- the off-by-one root cause.
0 means no override (default delayed chain, primer = own numRows).
Legacy path (CompactLoopStore=False): emit stride compute BEFORE s_add
(each call self-contained, gated by rowInc != 0). `forceinitrow0=0`
and `overrideAfterPrimerRows=0` defaults keep legacy behaviour.
"""
module = Module("incrementToNextRow")
numRows = self.rowInc
tmpBpe = bpeType if bpeType else self.kernelWriter.states.bpeCexternal
if (tc == 'C' or tc == 'TD') and (kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel"):
tmpBpe = int(self.kernelWriter.states.bpr * kernel["ProblemType"]["DestDataType"].numRegisters())
if ss.optSrdIncForRow:
# CLS chain seed: when caller asks for `forceinitrow0=1` we must
# also emit on rowInc==0 to keep s[stmp] primed; otherwise legacy
# behaviour: skip when no advance is needed.
if numRows or forceinitrow0:
packedC1 = kernel["PackedC1IndicesX"]
assert(len(packedC1) == 1) # would need to extract each dim and scale
if tc == 'Bias' and (not kernel["WorkGroupReduction"]):
index = packedC1[0] - 1
strideCD1 = "Size%s" % "I" if index == 0 else ("J" if index == 1 else (self.kernelWriter.states.indexChars[index]))
elif tc == 'Gate':
# Gate uses its own GateStride+0 (gate's col stride) for row advance.
strideCD1 = "GateStride+0"
else:
td = "D" if tc == 'TD' else tc
strideCD1 = "Stride%s%s"%(td ,self.kernelWriter.states.indexChars[packedC1[0]])
# Build a strideCompute block for a given numRows. Used for
# BOTH the legacy "before s_add" emit (with the call's own
# numRows) and the CLS delayed "after s_add" primer.
def _buildStrideCompute(nr):
sc = Module("strideCompute")
if nr > 1:
sc.add(SMulI32(dst=sgpr(stmp), \
src0=sgpr(strideCD1), \
src1=nr*tmpBpe, \
comment="scale Stride%s *= numRows(%u) * bpe"%(tc,nr)))
elif nr < 0:
sc.add(SMulI32(dst=sgpr(stmp), \
src0=sgpr(strideCD1), \
src1=(-nr)*tmpBpe, \
comment="scale Stride%s *= numRows(%u) * bpe"%(tc,nr)))
else:
sc.add(SLShiftLeftB32(dst=sgpr(stmp), \
src=sgpr(strideCD1), \
shiftHex=log2(tmpBpe), \
comment="incToNextRow: Scale by BPE"))
return sc
# Legacy (non-CompactLoopStore): stride compute BEFORE s_add,
# using the call's own numRows.
if not kernel["CompactLoopStore"]:
module.add(_buildStrideCompute(numRows))
if dst == -1:
dstLow = "Srd%s+0"%(tc)
dstHigh = "Srd%s+1"%(tc)
else:
dstLow = dst+0
dstHigh = dst+1
if numRows >= 0:
module.add(SAddU32(dst=sgpr(dstLow), \
src0=sgpr(dstLow), \
src1=sgpr(stmp), \
comment="incToNextRow: gra SRD += inc(lower)" ))
module.add(SAddCU32(dst=sgpr(dstHigh), \
src0=sgpr(dstHigh), \
src1=0, \
comment="incToNextRow: gra SRD += inc(upper)" ))
else: # numRows < 0
module.add(SSubU32(dst=sgpr(dstLow), \
src0=sgpr(dstLow), \
src1=sgpr(stmp), \
comment="incToNextRow: gra SRD -= inc(lower)" ))
module.add(SSubBU32(dst=sgpr(dstHigh), \
src0=sgpr(dstHigh), \
src1=0, \
comment="incToNextRow: gra SRD -= inc(upper)" ))
# CompactLoopStore: stride compute AFTER s_add (primes s[stmp]
# for the NEXT call's s_add). When caller passes
# `overrideAfterPrimerRows > 0`, use it as the primer rows --
# this is the look-ahead value = NEXT EMITTING elt's rowInc,
# so elt-(N+1)'s s_add reads exactly its own advance. Without
# override, primer uses elt-N's own numRows (off-by-one for
# CLS, but kept as the documented fallback).
if kernel["CompactLoopStore"]:
primerRows = overrideAfterPrimerRows if overrideAfterPrimerRows else numRows
module.add(_buildStrideCompute(primerRows))
return module