Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions projects/hipblaslt/tensilelite/Tensile/Component.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,31 @@ class LocalRead(Component):
"""
Local read block.
"""
def _getLdsReadMemToken(self, writer, kernel, tP):
def _getLdsReadMemToken(self, writer, kernel, tP, ldsByteOffset=None, bothHalves=False):
from rocisa.container import MemTokenData
tok = writer.states.ldsReadTokenIdx
useSplit = (kernel["TDMSplit"] and not kernel["ProblemType"]["Sparse"]
and ldsByteOffset is not None and not tP.get("isM", False))
if useSplit:
parity = writer.states.ldsReadTokenIdx
if bothHalves:
# Tile whose per-wave reads do not statically separate the two TDMSplit halves
# (numVectorsPerTile==1, e.g. B with MIWaveTile[N] == VectorWidth): a single read's
# combined region is fed by BOTH half tensor_loads, but the byte-offset classifier
# below can never reach the half1 boundary so it would tag everything half0 and the
# half1 load would go un-waited. Carry BOTH half tokens so StinkyTofu waits on the
# youngest of the two (fine-grained min(count-1), not a full drain).
toks = [writer.states.memTokenLdsSplit[parity][0],
writer.states.memTokenLdsSplit[parity][1]]
return MemTokenData(toks), toks[0]
inBuf = ldsByteOffset - tP["localReadSwapByteOffset"]
half = 1 if inBuf >= writer.tdmSplitLdsBoundary(kernel, tP) else 0
tok = writer.states.memTokenLdsSplit[parity][half]
else:
tok = writer.states.ldsReadTokenIdx
return MemTokenData([tok]), tok

def _emitLdsRead(self, writer, kernel, tP, LocalReadX, dst, src, ds, module, comment=""):
ldsMemToken, ldsMemTokenIdx = self._getLdsReadMemToken(writer, kernel, tP)
def _emitLdsRead(self, writer, kernel, tP, LocalReadX, dst, src, ds, module, ldsByteOffset=None, bothHalves=False, comment=""):
ldsMemToken, ldsMemTokenIdx = self._getLdsReadMemToken(writer, kernel, tP, ldsByteOffset, bothHalves)
fullComment = "%s sync LDS%u" % (comment, ldsMemTokenIdx) if comment else "sync LDS%u" % ldsMemTokenIdx
inst = LocalReadX(dst=dst, src=src, ds=ds, comment=fullComment)
inst.setMemToken(ldsMemToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1652,6 +1652,7 @@ def calcGfx1250LdsOffset():

addrIdx = paramList[0] // 65536
srcAddr=vgpr("LocalReadAddr%s+%u"%(tc, addrIdx))
tdmFullLdsOffset = paramList[0]
paramList[0] -= addrIdx * 65536

if numOffsets == 1:
Expand All @@ -1667,7 +1668,14 @@ def calcGfx1250LdsOffset():
# indexTranpose case, disable index conversion for local read
destVgpr = self.getVgprForEmu(writer, kernel, tc, bufferIdx, iui, index, lrvwTile, vgprLen=numVgpr, dst=False, localRead=True)

self._emitLdsRead(writer, kernel, tP, LocalReadX, dst=destVgpr, src=srcAddr, ds=ds, module=localReadCodeT, comment=comment)
# When numVectorsPerTile==1 the per-wave reads never cross the TDMSplit
# half boundary (only vIdx=0 exists), so the byte-offset half classifier
# would tag every read half0 and leave the half1 tensor_load un-waited.
# Such reads' combined region depends on BOTH half loads -> carry both
# half tokens.
tdmBothHalves = (kernel["TDMSplit"] and not kernel["ProblemType"]["Sparse"]
and not tP.get("isM", False) and numVectorsPerTile == 1)
self._emitLdsRead(writer, kernel, tP, LocalReadX, dst=destVgpr, src=srcAddr, ds=ds, module=localReadCodeT, ldsByteOffset=tdmFullLdsOffset, bothHalves=tdmBothHalves, comment=comment)
# TODO - handle vector-load
with writer.allocTmpSgpr(1, tag="LocalReadVALU_tmpSgprInfo2") as tmpSgprInfo:
tmpSgpr = tmpSgprInfo.idx
Expand Down
17 changes: 15 additions & 2 deletions projects/hipblaslt/tensilelite/Tensile/Components/SIA.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,10 +555,17 @@ def noSchedGlobalRead(writer, kernel, globalReadIncACode, globalReadIncBCode):
imod.add(nonTdmMod)
imod.addComment1("Global Read MXSA")
imod.add(writer.codes.dtlsM0UpdateMXSA)
imod.add(writer.codes.globalReadMXSA)
# MXSA/MXSB share A/B's XOR double-buffered MX-scale LDS, so their
# tensor_load_to_lds must defer in lockstep with A/B (after the LDS swap).
# Emitting them eagerly lets the async TDM overwrite the MX-scale LDS while
# this wave's own MX ds_loads are still reading it (self-wave WAR; s_wait_dscnt
# does not gate TDM, which retires on tensorcnt).
nonTdmModMXSA, tdmLoadModMXSA = _splitTdmLoad(writer.codes.globalReadMXSA)
imod.add(nonTdmModMXSA)
imod.addComment1("Global Read MXSB")
imod.add(writer.codes.dtlsM0UpdateMXSB)
imod.add(writer.codes.globalReadMXSB)
nonTdmModMXSB, tdmLoadModMXSB = _splitTdmLoad(writer.codes.globalReadMXSB)
imod.add(nonTdmModMXSB)
imod.addComment1("Global Read B")
imod.add(writer.codes.dtlsM0UpdateB)
nonTdmModB, tdmLoadModB = _splitTdmLoad(writer.codes.globalReadB)
Expand All @@ -569,6 +576,12 @@ def noSchedGlobalRead(writer, kernel, globalReadIncACode, globalReadIncBCode):
if tdmLoadModA.itemsSize() > 0:
deferMod.addComment1("Global Read A (TDM deferred after LDS swap)")
deferMod.add(tdmLoadModA)
if tdmLoadModMXSA.itemsSize() > 0:
deferMod.addComment1("Global Read MXSA (TDM deferred after LDS swap)")
deferMod.add(tdmLoadModMXSA)
if tdmLoadModMXSB.itemsSize() > 0:
deferMod.addComment1("Global Read MXSB (TDM deferred after LDS swap)")
deferMod.add(tdmLoadModMXSB)
if tdmLoadModB.itemsSize() > 0:
# TODO: For the 1-wave case, schedule B's TDM load independently for better tensor load balance.
deferMod.addComment1("Global Read B (TDM deferred after LDS swap)")
Expand Down
17 changes: 15 additions & 2 deletions projects/hipblaslt/tensilelite/Tensile/KernelWriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4078,6 +4078,8 @@ def noLoadLoop( self, kernel, tensorParametersA, tensorParametersB, isOptNLL, is
module.add(self._wait(kernel, tensorParametersA, tensorParametersB, vlcntVal, -1, -1, "10wait for global read"))
if not kernel["NoLdsWriteCode"]:
module.add(self._wait(kernel, tensorParametersA, tensorParametersB, -1, 0, -1, "4wait for local write"))
elif kernel["enableTDMA"] and kernel["enableTDMB"]:
module.add(self._wait(kernel, tensorParametersA, tensorParametersB, 0, -1, -1, "wait for TDM tensor loads"))
module.add(self._syncThreads(kernel, "wait for local write done, sync"))
papPriorSync = True
elif kernel["enableTDMA"] and kernel["enableTDMB"]:
Expand Down Expand Up @@ -6238,7 +6240,7 @@ def _restoreNtabState():
module.addComment1("Recalc local read offsets")
module.add(self.recalcLocalReadAddressesAB(kernel, tensorParametersA, tensorParametersB))
module.add(self._wait(kernel, tensorParametersA, tensorParametersB, -1, 0, -1, "5wait for local write"))
module.add(self._syncThreads(kernel, "Tail loop LW->LR, sync LDS0", memoryToken=[self.states.memTokenLdsBuffer0]))
module.add(self._syncThreads(kernel, "Tail loop LW->LR, sync LDS0", memoryToken=self._tailLoopBarrierTokens(kernel)))
#module.add(self.dumpLds(kernel, 0, 8))

# tail: free G2L Vgpr
Expand Down Expand Up @@ -6466,7 +6468,7 @@ def _restoreNtabState():
# generate wait code for early exit
module.add(self._wait(kernel, tensorParametersA, tensorParametersB, 0, -1, -1, " tailloopInNll: wait for global read"))
module.add(self._wait(kernel, tensorParametersA, tensorParametersB, -1, 0, -1, " tailloopInNll: wait for local read"))
module.add(self._syncThreads(kernel, "tailloopInNll: wait until LR done, sync LDS0", memoryToken=[self.states.memTokenLdsBuffer0]))
module.add(self._syncThreads(kernel, "tailloopInNll: wait until LR done, sync LDS0", memoryToken=self._tailLoopBarrierTokens(kernel)))

if self.states.lastValuMXSAB:
self.vgprPool.add(0 , self.states.lastValuMXSAB, "ValuMXSAB")
Expand Down Expand Up @@ -7183,9 +7185,12 @@ def initSubTileInfo(tc):
if kernel["1LDSBuffer"]:
self.states.memTokenLdsBuffer0 = 0
self.states.memTokenLdsBuffer1 = 0
self.states.memTokenLdsSplit = [[1, 2], [1, 2]]
else:
self.states.memTokenLdsBuffer0 = 0
self.states.memTokenLdsBuffer1 = 1
# [bufferParity][half]; half-0 reuses buffer token so MXSA/MXSB share it
self.states.memTokenLdsSplit = [[self.states.memTokenLdsBuffer0, 2], [self.states.memTokenLdsBuffer1, 6]]
self.states.ldsReadTokenIdx = self.states.memTokenLdsBuffer0
self.states.ldsTensorTokenIdx = self.states.memTokenLdsBuffer0
self.states.ldsDirectToLDSTokenIdx = self.states.memTokenLdsBuffer0
Expand Down Expand Up @@ -10400,6 +10405,14 @@ def _syncThreads(self, kernel, comment="", skipForceWaitcnt0=False, memoryToken=
return syncThreads(kernel, self.states.archCaps, self.states.asmCaps, comment, skipForceWaitcnt0=skipForceWaitcnt0, memoryToken=memoryToken)
return Module("SyncThreads (Empty)")

def _tailLoopBarrierTokens(self, kernel):
# Explicit tail barriers must list every token; the auto-barrier pass doesn't cover them.
tokens = [self.states.memTokenLdsBuffer0]
if kernel["TDMSplit"] and not kernel["ProblemType"]["Sparse"]:
for row in self.states.memTokenLdsSplit:
tokens.extend(row)
return sorted(set(tokens))

##############################################################################
# PostMainLoopBarrierCheckAndReset
# Rebuild SBarrier placement by memory-token read/write phase transitions.
Expand Down
Loading
Loading