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
538 lines
25 KiB
Python
538 lines
25 KiB
Python
# Copyright Advanced Micro Devices, Inc., or its affiliates.
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
################################################################################
|
|
# Scale GR/LR emit for MX scale factor operands (MXSA/MXSB).
|
|
#
|
|
# Scale factors use a simpler access pattern than data tiles:
|
|
# GR: DTL with linear offset (serial * loadWidth), one buffer_load per wave
|
|
# LR: ds_read_b32 per scale group (2 M-adjacent subtiles per group)
|
|
#
|
|
# Each function operates on a single tensor component (MXSA or MXSB),
|
|
# called once per scale operand.
|
|
#
|
|
# Uses ti.sharedVgprGROffset / ti.sharedVgprLROffset (compat properties)
|
|
# since MXScaleTilePair has gr=None, lr=None.
|
|
################################################################################
|
|
|
|
import math
|
|
|
|
from rocisa.code import Module
|
|
from rocisa.container import DSModifiers, MUBUFModifiers, vgpr, sgpr, mgpr
|
|
from rocisa.instruction import (
|
|
BufferLoadB128,
|
|
DSLoadB32,
|
|
SAddCU32, SAddU32, SLShiftLeftB32, SMovB32, SMulI32, SNop, SXorB32,
|
|
VAddU32, VAndB32, VMulLOU32, VReadfirstlaneB32, VXorB32,
|
|
VLShiftLeftB32, VLShiftRightB32,
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Scale GR offset
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def emitScaleGROffset(ti, writer, kernel):
|
|
"""Compute per-thread DTL vaddr for scale GR load."""
|
|
return Module(f"Scale GR Offset ({ti.tc})") # STUB
|
|
module = Module(f"Scale GR Offset ({ti.tc})")
|
|
tc = ti.tc
|
|
loadWidth = ti.loadWidthGR
|
|
loadWidthShift = loadWidth.bit_length() - 1
|
|
|
|
scaleGroupSize = ti.lrSubtileSize
|
|
numThreadsPerGroup = (scaleGroupSize * int(ti.localSubtileGrid[1])) // loadWidth
|
|
|
|
vtmp = writer.vgprPool.checkOut(1, tag="emitScaleGROffset_vtmp")
|
|
stmp = writer.sgprPool.checkOut(1, tag="emitScaleGROffset_stmp")
|
|
|
|
module.add(VLShiftRightB32(dst=vgpr(vtmp),
|
|
shiftHex=hex(numThreadsPerGroup.bit_length()-1), src=vgpr("Serial"),
|
|
comment=f"scale{tc}: groupId"))
|
|
module.add(SMulI32(dst=sgpr(stmp), src0=int(ti.bpe), src1=sgpr("Strides" + tc),
|
|
comment=f"scale{tc}: stride * bpe"))
|
|
module.add(VMulLOU32(dst=vgpr(vtmp), src1=vgpr(vtmp), src0=sgpr(stmp),
|
|
comment=f"scale{tc}: groupId * stride"))
|
|
module.add(VAndB32(dst=vgpr(ti.sharedVgprGROffset[0]),
|
|
src0=hex(numThreadsPerGroup - 1), src1=vgpr("Serial"),
|
|
comment=f"scale{tc}: threadId"))
|
|
module.add(VLShiftLeftB32(dst=vgpr(ti.sharedVgprGROffset[0]),
|
|
shiftHex=hex(loadWidthShift), src=vgpr(ti.sharedVgprGROffset[0]),
|
|
comment=f"scale{tc}: threadId * loadWidth"))
|
|
module.add(VAddU32(dst=vgpr(ti.sharedVgprGROffset[0]),
|
|
src0=vgpr(ti.sharedVgprGROffset[0]), src1=vgpr(vtmp),
|
|
comment=f"scale{tc}: final offset"))
|
|
|
|
writer.vgprPool.checkIn(vtmp)
|
|
writer.sgprPool.checkIn(stmp)
|
|
return module
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Scale GR load (DTL)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def emitScaleGRLoad(ti, writer, kernel):
|
|
"""Emit buffer_load_b128 DTL for scale data (global -> LDS)."""
|
|
module = Module(f"Scale GR Load ({ti.tc})")
|
|
tc = ti.tc
|
|
|
|
isGlc = bool(kernel.get(f"NonTemporal{tc}", 0) & 0x1)
|
|
isSlc = bool(kernel.get(f"NonTemporal{tc}", 0) & 0x2)
|
|
isNT = bool(kernel.get(f"NonTemporal{tc}", 0) & 0x4)
|
|
|
|
module.add(SMovB32(dst=mgpr(0), src=sgpr(f"LocalWriteBaseAddr{tc}"),
|
|
comment=f"scale{tc}: M0 = scaleLdsBase"))
|
|
|
|
mubuf = MUBUFModifiers(offen=True, offset12=0, glc=isGlc, slc=isSlc, nt=isNT, lds=True)
|
|
module.add(BufferLoadB128(dst=None, vaddr=vgpr(ti.sharedVgprGROffset[0]),
|
|
saddr=sgpr(f"Srd{tc}", 4), soffset=0, mubuf=mubuf,
|
|
comment=f"scale{tc}: DTL b128 load"))
|
|
|
|
return module
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Scale LR offset
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def emitScaleLROffset(ti, writer, kernel):
|
|
"""Compute per-lane LDS read offset for scale LR."""
|
|
return Module(f"Scale LR Offset ({ti.tc})") # STUB
|
|
module = Module(f"Scale LR Offset ({ti.tc})")
|
|
tc = ti.tc
|
|
wavesize = kernel["WavefrontSize"]
|
|
|
|
mi = kernel["MIWaveGroup"]
|
|
totalScaleBytes = (ti.macroTile // ti.waveGroupSize) * ti.scaleDepthU * int(ti.bpe)
|
|
|
|
waveIdVgpr = writer.vgprPool.checkOut(1, tag="emitScaleLROffset_waveIdVgpr")
|
|
module.add(VLShiftRightB32(dst=vgpr(waveIdVgpr), shiftHex=hex(wavesize.bit_length()-1),
|
|
src=vgpr("Serial"), comment=f"scale{tc}: waveId"))
|
|
|
|
vtmp = writer.vgprPool.checkOut(1, tag="emitScaleLROffset_vtmp")
|
|
stmp = writer.sgprPool.checkOut(1, tag="emitScaleLROffset_stmp")
|
|
|
|
if tc in ('A', 'MXSA'):
|
|
module.add(VAndB32(dst=vgpr(vtmp), src0=mi[0]-1, src1=vgpr(waveIdVgpr),
|
|
comment=f"scale{tc}: waveId %% {mi[0]}"))
|
|
else:
|
|
module.add(VLShiftRightB32(dst=vgpr(vtmp),
|
|
shiftHex=int(math.log2(mi[0])), src=vgpr(waveIdVgpr),
|
|
comment=f"scale{tc}: waveId / {mi[0]}"))
|
|
|
|
module.add(SMovB32(dst=sgpr(stmp), src=totalScaleBytes,
|
|
comment=f"scale{tc}: partition stride"))
|
|
module.add(VMulLOU32(dst=vgpr(ti.sharedVgprLROffset[0]),
|
|
src0=sgpr(stmp), src1=vgpr(vtmp),
|
|
comment=f"scale{tc}: partition offset"))
|
|
|
|
writer.vgprPool.checkIn(vtmp)
|
|
writer.vgprPool.checkIn(waveIdVgpr)
|
|
|
|
# Per-lane offset: laneId * 4
|
|
laneOffset = writer.vgprPool.checkOut(1, tag="emitScaleLROffset_laneOffset")
|
|
module.add(VAndB32(dst=vgpr(laneOffset), src0=vgpr("Serial"), src1=wavesize-1,
|
|
comment=f"scale{tc}: laneId"))
|
|
module.add(VLShiftLeftB32(dst=vgpr(laneOffset), shiftHex=hex(2), src=vgpr(laneOffset),
|
|
comment=f"scale{tc}: laneId * 4"))
|
|
module.add(VAddU32(dst=vgpr(ti.sharedVgprLROffset[0]),
|
|
src0=vgpr(laneOffset), src1=vgpr(ti.sharedVgprLROffset[0]),
|
|
comment=f"scale{tc}: + laneOffset"))
|
|
writer.vgprPool.checkIn(laneOffset)
|
|
|
|
# Add global LDS offset
|
|
ldsStartOffset = getattr(writer, f'ldsStartOffset{tc}', 0)
|
|
if ldsStartOffset:
|
|
module.add(SMovB32(dst=sgpr(stmp), src=hex(ldsStartOffset),
|
|
comment=f"scale{tc}: LDS base offset"))
|
|
module.add(VAddU32(dst=vgpr(ti.sharedVgprLROffset[0]),
|
|
src0=vgpr(ti.sharedVgprLROffset[0]), src1=sgpr(stmp),
|
|
comment=f"scale{tc}: + LDS offset"))
|
|
|
|
# Init swap VGPRs
|
|
module.add(SMovB32(dst=sgpr(stmp), src=writer.ldsTotalSize,
|
|
comment=f"scale{tc}: ldsTotalSize"))
|
|
for i in range(len(ti.sharedVgprLROffset)):
|
|
vOff = ti.sharedVgprLROffset[i]
|
|
vSwap = ti.sharedVgprLROffsetSwap[i]
|
|
module.add(VAddU32(dst=vgpr(vSwap), src0=vgpr(vOff), src1=sgpr(stmp),
|
|
comment=f"scale{tc}: swap init"))
|
|
module.add(VXorB32(dst=vgpr(vSwap), src0=vgpr(vOff), src1=vgpr(vSwap),
|
|
comment=f"scale{tc}: swap mask"))
|
|
|
|
writer.sgprPool.checkIn(stmp)
|
|
return module
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Scale LR load
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def emitScaleLRLoad(ti, writer, kernel):
|
|
"""Emit ds_read_b32 for all scale groups."""
|
|
module = Module(f"Scale LR Load ({ti.tc})")
|
|
tc = ti.tc
|
|
|
|
if ti.mxBlock == 0:
|
|
return module
|
|
|
|
numScaleGroups = (int(ti.lrGlobalSubtileGrid[0]) // ti.waveGroupSize) * int(ti.lrGlobalSubtileGrid[1])
|
|
groupStride = int(ti.lrSubtileSize)
|
|
|
|
for gid in range(numScaleGroups):
|
|
dsOffset = groupStride * gid
|
|
vdst = ti.vgprTiles[4 * gid].regList.indices[0]
|
|
module.add(DSLoadB32(dst=vgpr(vdst),
|
|
src=vgpr(ti.sharedVgprLROffset[0]),
|
|
ds=DSModifiers(offset=dsOffset),
|
|
comment=f"scale{tc}[group{gid}]: 4B from LDS"))
|
|
|
|
return module
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Scale GR ptr update
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def emitScaleGRPtrUpdate(ti, writer, kernel):
|
|
"""Advance scale SRD base pointer by one depthU iteration."""
|
|
module = Module()
|
|
tc = ti.tc
|
|
|
|
inc = int(ti.lrSubtileSize * ti.lrGlobalSubtileGrid[1])
|
|
module.addComment0("Scale SRD update: %s += %u" % (tc, inc))
|
|
module.add(SAddU32(dst=sgpr(f"Srd{tc}"), src0=sgpr(f"Srd{tc}"), src1=inc))
|
|
module.add(SAddCU32(dst=sgpr(f"Srd{tc}+1"), src0=sgpr(f"Srd{tc}+1"), src1=0))
|
|
return module
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Scale LDS buffer swaps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def emitScaleGRLDSSwap(ti, writer, kernel):
|
|
"""Toggle scale GR DTL write target between double-buffer halves."""
|
|
module = Module()
|
|
tc = ti.tc
|
|
module.addComment0("Emit code to swap %s GR m0 offsets"%tc)
|
|
module.add(SXorB32(dst=sgpr(f"LocalWriteBaseAddr{tc}"),
|
|
src0=sgpr(f"LocalWriteBaseAddr{tc}"), src1=sgpr(f"Swap{tc}"),
|
|
comment=""))
|
|
return module
|
|
|
|
|
|
def emitScaleLRLDSSwap(ti, writer, kernel):
|
|
"""Toggle scale LR read offsets between double-buffer halves."""
|
|
module = Module()
|
|
module.addComment0("Emit code to swap %s LR vgpr offsets"%ti.tc)
|
|
for i in range(len(ti.sharedVgprLROffset)):
|
|
vOff = ti.sharedVgprLROffset[i]
|
|
vSwap = ti.sharedVgprLROffsetSwap[i]
|
|
module.add(VXorB32(dst=vgpr(vOff), src0=vgpr(vOff), src1=vgpr(vSwap), comment=""))
|
|
return module
|
|
|
|
|
|
# =========================================================================
|
|
# Legacy Scale emit functions (moved from SubtileBasedKernel.py)
|
|
# =========================================================================
|
|
|
|
##################################################
|
|
# Compute the per-thread global-read (DTL) vaddr for scale tensor tc.
|
|
#
|
|
# With DTL (buffer_load lds=True) the same vaddr serves as:
|
|
# - global byte offset from the SRD base (where to read from global memory)
|
|
# - LDS byte offset from M0 (where to write in LDS)
|
|
#
|
|
# Threads within a wave are split into groups of numThreadsPerGroup.
|
|
# Each group loads one contiguous subtile-column worth of scale bytes:
|
|
#
|
|
# groupId = serial / numThreadsPerGroup (which scale column)
|
|
# threadId = serial % numThreadsPerGroup (position within group)
|
|
#
|
|
# grOffset = groupId * stride_bpe (column byte offset via tensor stride)
|
|
# + threadId * loadWidth (byte offset within column)
|
|
#
|
|
# Output: sharedVgprGROffset[0] = grOffset (used as vaddr in DTL load)
|
|
#
|
|
def _graTileAssignmentScaleSwizzledCommon(tc, writer, kernel):
|
|
module = Module()
|
|
|
|
module.addComment("Computing GR Offset for %s"%tc)
|
|
|
|
# TODO: revisit property mappings below (lrSubtileSize,
|
|
# lrGlobalSubtileGrid); add helpers on TileInfo if they recur across emit functions.
|
|
ti_ = writer.states.mxsa.tileInfo if tc == 'MXSA' else writer.states.mxsb.tileInfo
|
|
loadWidth = ti_.loadWidthGR
|
|
loadWidthShift = loadWidth.bit_length() - 1
|
|
|
|
# lrSubtileSize = LR subtile bytes (2x2 MMA tiles = 256B for FP4 scale).
|
|
# This equals the old "2 * subtileSize" (2 M-adjacent [1,2] subtiles).
|
|
# lrGlobalSubtileGrid[1] = K-dim subtile count = old localSubtileGrid[1].
|
|
scaleGroupSize = ti_.lrSubtileSize
|
|
numThreadsPerGroup = (scaleGroupSize * int(ti_.lrGlobalSubtileGrid[1])) // loadWidth
|
|
|
|
vtmp = writer.vgprPool.checkOut(1, tag="_graTileAssignmentScaleSwizzledCommon_vtmp")
|
|
|
|
stmp = writer.sgprPool.checkOut(1, tag="_graTileAssignmentScaleSwizzledCommon_stmp")
|
|
|
|
module.add(VLShiftRightB32(dst=vgpr(vtmp),
|
|
shiftHex=hex(int(math.log2(numThreadsPerGroup))), src=vgpr("Serial"),
|
|
comment="%s: grOffset = serial / %d" % (tc, loadWidth)))
|
|
module.add(SLShiftLeftB32(sgpr(stmp), int(math.log2(ti_.bpe)), sgpr("Strides%s"%tc), comment="*= bpe (%d)"%(ti_.bpe)))
|
|
|
|
module.add(VMulLOU32(dst=vgpr(vtmp), src1=vgpr(vtmp), src0=sgpr(stmp), comment="Apply scale%s stride to each group"%tc))
|
|
module.add(VAndB32(dst=vgpr(ti_.sharedVgprGROffset[0]),
|
|
src0=hex(numThreadsPerGroup - 1), src1=vgpr("Serial"),
|
|
comment="%s: grOffset = serial %% %d" % (tc, loadWidth)))
|
|
module.add(VLShiftLeftB32(dst=vgpr(ti_.sharedVgprGROffset[0]),
|
|
shiftHex=hex(loadWidthShift), src=vgpr(ti_.sharedVgprGROffset[0]),
|
|
comment="Scale by load width for each thread in group"))
|
|
module.add(VAddU32(dst=vgpr(ti_.sharedVgprGROffset[0]), src0=vgpr(ti_.sharedVgprGROffset[0]), src1=vgpr(vtmp), comment="Final offset calc"))
|
|
writer.vgprPool.checkIn(vtmp)
|
|
writer.sgprPool.checkIn(stmp)
|
|
|
|
return module
|
|
|
|
##################################################
|
|
# Generate GR offset calculation for scaleA/B (DTL).
|
|
#
|
|
# With DTL, vaddr serves as both the global read offset (from SRD)
|
|
# and the LDS write offset (from M0). Simple linear access:
|
|
# grOffset = serial * scaleLoadWidth
|
|
#
|
|
def graTileAssignmentScaleSwizzled(writer, kernel):
|
|
module = Module()
|
|
if not kernel["ProblemType"].get("MXBlockA", 0) and not kernel["ProblemType"].get("MXBlockB", 0):
|
|
return module
|
|
module.add(_graTileAssignmentScaleSwizzledCommon('MXSA', writer, kernel))
|
|
module.add(_graTileAssignmentScaleSwizzledCommon('MXSB', writer, kernel))
|
|
return module
|
|
|
|
|
|
##################################################
|
|
# Apply wave partition offset for scale LR.
|
|
#
|
|
# Each wave reads from its assigned LDS partition for scale A or B.
|
|
#
|
|
# MXSA: partition index = waveId % MIWaveGroup[0] (M-direction wave index)
|
|
# MXSB: partition index = waveId / MIWaveGroup[0] (N-direction wave index)
|
|
# Using MIWaveGroup[0] (not [1]) correctly handles asymmetric configs
|
|
# (e.g. 4x1: all 4 M-waves share the same N partition -> index = 0).
|
|
#
|
|
# Output: sharedVgprLROffset[0] = partitionIndex * totalScaleBytes
|
|
#
|
|
def _applyScaleWavePartitionLROffset(module, writer, kernel, ti_, waveId):
|
|
tc = ti_.tc
|
|
|
|
# totalScaleBytes = bytes per wave partition in LDS for this scale tensor.
|
|
# lrGlobalSubtileGrid[0] = M-dim LR subtile count (globalMMATileGrid[0] / lrSubtileShape[0])
|
|
# lrGlobalSubtileGrid[1] = K-dim LR subtile count
|
|
# lrSubtileSize = bytes per LR subtile (2x2 MMA tiles for FP4 scale)
|
|
index = 0 if tc == 'MXSA' else 1
|
|
totalScaleBytes = (int(ti_.lrGlobalSubtileGrid[0]) // kernel["MIWaveGroup"][index]) * int(ti_.lrGlobalSubtileGrid[1]) * int(ti_.lrSubtileSize)
|
|
|
|
tmpSgpr = writer.sgprPool.checkOut(1, tag="_applyScaleWavePartitionLROffset_tmpSgpr")
|
|
tmp = writer.vgprPool.checkOut(2, tag="_applyScaleWavePartitionLROffset_tmp")
|
|
|
|
if tc == 'MXSA':
|
|
module.add(VAndB32(dst=vgpr(tmp), src0=kernel["MIWaveGroup"][0]-1, src1=vgpr(waveId), comment="scale%s: waveId %% 2"%tc))
|
|
else:
|
|
module.add(VLShiftRightB32(dst=vgpr(tmp), shiftHex=int(math.log2(kernel["MIWaveGroup"][0])), src=vgpr(waveId), comment="scale%s: waveId / numWavesM"%tc))
|
|
|
|
module.add(SMovB32(dst=sgpr(tmpSgpr), src=totalScaleBytes, comment="scale%s: scale region"%tc))
|
|
module.add(VMulLOU32(dst=vgpr(ti_.sharedVgprLROffset[0]), src0=sgpr(tmpSgpr), src1=vgpr(tmp), comment="scale%s: partition offset"%tc))
|
|
|
|
writer.vgprPool.checkIn(tmp)
|
|
writer.sgprPool.checkIn(tmpSgpr)
|
|
|
|
|
|
##################################################
|
|
# Generate LR offset calculation for scaleA/B.
|
|
#
|
|
# Computes the per-lane LDS read offset for scale tensors. Called once
|
|
# during kernel setup; the resulting VGPRs are used every loop iteration.
|
|
#
|
|
# Final LR offset per lane:
|
|
# lrOffset[lane] = wavePartitionOffset + laneId * 4 + ldsStartOffset
|
|
#
|
|
# where:
|
|
# wavePartitionOffset = partitionIndex * totalScaleBytes
|
|
# MXSA partitionIndex = waveId % MIWaveGroup[0] (M-direction)
|
|
# MXSB partitionIndex = waveId / MIWaveGroup[0] (N-direction)
|
|
# laneId = serial & (wavesize - 1)
|
|
# ldsStartOffset = writer.ldsStartOffsetMXSA/B
|
|
#
|
|
# LDS layout (double-buffered, one buffer shown):
|
|
# [ DataA | DataB | ScaleA | ScaleB ]
|
|
# ScaleA starts at ldsStartOffsetMXSA, ScaleB at ldsStartOffsetMXSB.
|
|
#
|
|
# After the LR offset is fully computed, the double-buffer swap VGPR is
|
|
# initialised here (not in localReadDTLInitCommonSwapVgpr, which runs
|
|
# before this function and would use uninitialised values):
|
|
# swapVgpr = lrOffset XOR (lrOffset + ldsTotalSize)
|
|
# This lets localReadLDSBufferSwap toggle between buffer 0 and buffer 1.
|
|
#
|
|
def lraTileAssignmentScaleSwizzled(writer, kernel):
|
|
return _lraTileAssignmentScaleSwizzled_legacy(writer, kernel)
|
|
|
|
def _lraTileAssignmentScaleSwizzled_legacy(writer, kernel):
|
|
module = Module()
|
|
if not kernel["ProblemType"].get("MXBlockA", 0) and not kernel["ProblemType"].get("MXBlockB", 0):
|
|
return module
|
|
tiA_ = writer.states.mxsa.tileInfo
|
|
tiB_ = writer.states.mxsb.tileInfo
|
|
module.addComment0("LR Offset Calculation for Scale Tensors")
|
|
wavesize = kernel["WavefrontSize"]
|
|
waveIdVgpr = writer.vgprPool.checkOut(1, tag="_lraTileAssignmentScaleSwizzled_legacy_waveIdVgpr")
|
|
module.add(VLShiftRightB32(dst=vgpr(waveIdVgpr), shiftHex=hex(wavesize.bit_length()-1), src=vgpr("Serial"), comment="scale: waveId"))
|
|
_applyScaleWavePartitionLROffset(module, writer, kernel, tiA_, waveIdVgpr)
|
|
_applyScaleWavePartitionLROffset(module, writer, kernel, tiB_, waveIdVgpr)
|
|
writer.vgprPool.checkIn(waveIdVgpr)
|
|
laneOffset = writer.vgprPool.checkOut(1, tag="_lraTileAssignmentScaleSwizzled_legacy_laneOffset")
|
|
module.add(VAndB32(dst=vgpr(laneOffset), src0=vgpr("Serial"), src1=wavesize-1, comment="scale: laneId"))
|
|
module.add(VLShiftLeftB32(dst=vgpr(laneOffset), shiftHex=hex(2), src=vgpr(laneOffset), comment="scale: laneId * 4"))
|
|
module.add(VAddU32(dst=vgpr(tiA_.sharedVgprLROffset[0]), src0=vgpr(laneOffset), src1=vgpr(tiA_.sharedVgprLROffset[0]), comment="scaleA: lrOffset = laneId * 4"))
|
|
module.add(VAddU32(dst=vgpr(tiB_.sharedVgprLROffset[0]), src0=vgpr(laneOffset), src1=vgpr(tiB_.sharedVgprLROffset[0]), comment="scaleB: lrOffset = laneId * 4"))
|
|
writer.vgprPool.checkIn(laneOffset)
|
|
tmpSgpr = writer.sgprPool.checkOut(1, tag="_lraTileAssignmentScaleSwizzled_legacy_tmpSgpr")
|
|
module.add(SMovB32(dst=sgpr(tmpSgpr), src=hex(writer.ldsStartOffsetMXSA), comment="scale: LDS offset for A scale"))
|
|
module.add(VAddU32(dst=vgpr(tiA_.sharedVgprLROffset[0]), src0=vgpr(tiA_.sharedVgprLROffset[0]), src1=sgpr(tmpSgpr), comment="scaleA: +=LDS offset"))
|
|
module.add(SMovB32(dst=sgpr(tmpSgpr), src=hex(writer.ldsStartOffsetMXSB), comment="scale: LDS offset for B scale"))
|
|
module.add(VAddU32(dst=vgpr(tiB_.sharedVgprLROffset[0]), src0=vgpr(tiB_.sharedVgprLROffset[0]), src1=sgpr(tmpSgpr), comment="scaleB: +=LDS offset"))
|
|
module.add(SMovB32(dst=sgpr(tmpSgpr), src=writer.ldsTotalSize, comment="scale: total LDS size for swap"))
|
|
for ti_ in [tiA_, tiB_]:
|
|
for i in range(len(ti_.sharedVgprLROffset)):
|
|
vgprId = ti_.sharedVgprLROffset[i]
|
|
vgprSwapId = ti_.sharedVgprLROffsetSwap[i]
|
|
module.add(VAddU32(dst=vgpr(vgprSwapId), src0=vgpr(vgprId), src1=sgpr(tmpSgpr), comment="scale%s: LR swap"%ti_.tc))
|
|
module.add(VXorB32(dst=vgpr(vgprSwapId), src0=vgpr(vgprId), src1=vgpr(vgprSwapId), comment="scale%s: LR swap"%ti_.tc))
|
|
writer.sgprPool.checkIn(tmpSgpr)
|
|
return module
|
|
|
|
##################################################
|
|
# Scale GR: Load scale bytes from global memory directly to LDS (DTL).
|
|
#
|
|
# Uses BufferLoadB128 with lds=True. M0 is set to scaleLdsBase, and
|
|
# sharedVgprGROffset[0] = serial * scaleLoadWidth serves as both the
|
|
# global read offset (from SRD) and the LDS write offset (from M0).
|
|
def globalReadDoScaleSubtile(tc, writer, kernel):
|
|
module = Module()
|
|
|
|
if not kernel["ProblemType"].get("MXBlockA", 0) and not kernel["ProblemType"].get("MXBlockB", 0):
|
|
return module
|
|
|
|
tileInfo = writer.states.mxsa.tileInfo if tc == 'MXSA' else writer.states.mxsb.tileInfo
|
|
|
|
isGlc = bool(kernel["NonTemporal%s"%tc] & 0x1)
|
|
isSlc = bool(kernel["NonTemporal%s"%tc] & 0x2)
|
|
isNT = bool(kernel["NonTemporal%s"%tc] & 0x4)
|
|
|
|
assert len(tileInfo.sharedVgprGROffset) > 0, "Scale GR requires at least 1 GR offset VGPR"
|
|
|
|
module.addComment0("Scale GR: %s (DTL: BufferLoadB128 -> LDS)" % tc)
|
|
|
|
# Set M0 to scale LDS base address for DTL write destination
|
|
module.add(SMovB32(dst=mgpr(0), src=sgpr("LocalWriteBaseAddr%s"%tc),
|
|
comment="scale%s: M0 = scaleLdsBase" % tc))
|
|
|
|
# DTL load: data goes directly from global memory to LDS (no intermediate VGPR)
|
|
mubuf = MUBUFModifiers(offen=True, offset12=0, glc=isGlc, slc=isSlc, nt=isNT, lds=True)
|
|
module.add(BufferLoadB128(dst=None, vaddr=vgpr(tileInfo.sharedVgprGROffset[0]),
|
|
saddr=sgpr("Srd%s" % tc, 4), soffset=0, mubuf=mubuf,
|
|
comment="scale%s: DTL b128 load" % tc))
|
|
|
|
return module
|
|
|
|
##################################################
|
|
# Scale LR: Read scale data from LDS into scale VGPRs (DSLoadB32).
|
|
#
|
|
# Each lane reads 4 bytes from LDS using ds_read_b32. The base address
|
|
# is sharedVgprLROffset[0] (computed by lraTileAssignmentScaleSwizzled).
|
|
# MMA tile and subtile selection is done via constant ds_offset at emit time.
|
|
#
|
|
# Each 32-bit VGPR holds 4 E8M0 scale bytes; opsel/opsel_hi selects
|
|
# the correct byte per MFMA invocation.
|
|
#
|
|
def emitSubtileScaleDsRead(tc, writer, kernel, scaleGroupIdx):
|
|
"""Emit a single DSLoadB32 for a scale group (2 M-adjacent [1,2] subtiles).
|
|
Each ds_read_b32 loads 4 bytes = 4 E8M0 scale values into one VGPR."""
|
|
module = Module()
|
|
tileInfo = writer.states.mxsa.tileInfo if tc == 'MXSA' else writer.states.mxsb.tileInfo
|
|
|
|
if tileInfo.mxBlock == 0:
|
|
return module
|
|
|
|
# TileInfo LR subtile (2,2) already spans 2 M-adjacent tiles -> stride = lrSubtileSize.
|
|
# Legacy TileInfo subtile (1,2) spans 1 M-tile -> stride = 2 * subtileSize.
|
|
if hasattr(tileInfo, 'lrSubtileSize'):
|
|
groupStride = int(tileInfo.lrSubtileSize)
|
|
else:
|
|
groupStride = 2 * tileInfo.subtileSize
|
|
dsOffset = groupStride * scaleGroupIdx
|
|
vdst = tileInfo.vgprTiles[4 * scaleGroupIdx].regList.indices[0]
|
|
module.add(DSLoadB32(dst=vgpr(vdst),
|
|
src=vgpr(tileInfo.sharedVgprLROffset[0]),
|
|
ds=DSModifiers(offset=dsOffset),
|
|
comment="scale%s[group%u]: load 4B from LDS" % (tc, scaleGroupIdx)))
|
|
return module
|
|
|
|
def localReadDoScaleSubtile(tc, writer, kernel):
|
|
"""Emit scale ds_reads for all scale groups (PGR=0 path)."""
|
|
module = Module()
|
|
|
|
if not kernel["ProblemType"].get("MXBlockA", 0) and not kernel["ProblemType"].get("MXBlockB", 0):
|
|
return module
|
|
|
|
tileInfo = writer.states.mxsa.tileInfo if tc == 'MXSA' else writer.states.mxsb.tileInfo
|
|
|
|
# Iterate over scale groups: one ds_read per 2 M-adjacent subtiles
|
|
numScaleGroups = math.ceil(tileInfo.localSubtileGrid[0] / 2) * tileInfo.localSubtileGrid[1]
|
|
for gid in range(numScaleGroups):
|
|
module.add(emitSubtileScaleDsRead(tc, writer, kernel, gid))
|
|
|
|
return module
|
|
|
|
##################################################
|
|
# Scale SRD pointer update: advance scale SRD by scaleDepthU * scaleBpe bytes.
|
|
#
|
|
def globalReadScalePtrUpdates(tc, writer, kernel):
|
|
ti_ = writer.states.mxsa.tileInfo if tc == 'MXSA' else writer.states.mxsb.tileInfo
|
|
return emitScaleGRPtrUpdate(ti_, writer, kernel)
|
|
|
|
##################################################
|
|
# Subroutine to generate DTL M0 LDS buffer swap
|
|
#
|
|
# For Swizzled Scales each wave will collectively stream
|
|
# the scale values
|
|
#
|
|
def globalReadScaleSwizzledDTLInitCommonSgpr(writer, kernel):
|
|
module = Module()
|
|
|
|
wavesize = kernel["WavefrontSize"]
|
|
vgprWaveId = writer.vgprPool.checkOut(1, tag="globalReadScaleSwizzledDTLInitCommonSgpr_vgprWaveId")
|
|
module.addComment0("Compute shared offsets used by m0 in DTL loads")
|
|
module.add(VLShiftRightB32(dst=vgpr(vgprWaveId), shiftHex=hex(wavesize.bit_length()-1), src=vgpr("Serial"), comment="Wave Id"))
|
|
|
|
tiMXSA_ = writer.states.mxsa.tileInfo
|
|
tiMXSB_ = writer.states.mxsb.tileInfo
|
|
|
|
loadWidth = tiMXSA_.loadWidthGR
|
|
|
|
bytesPerLoad = loadWidth * wavesize
|
|
module.add(VLShiftLeftB32(dst=vgpr(vgprWaveId), shiftHex=hex((bytesPerLoad).bit_length()-1), src=vgpr(vgprWaveId), comment="Apply wave-specific common offset (%u) for A/B"%bytesPerLoad))
|
|
|
|
module.add(SNop(waitState=0, comment="Wait for VGPR to be ready"))
|
|
module.add(VReadfirstlaneB32(dst=sgpr("LocalWriteBaseAddrMXSA"), src=vgpr(vgprWaveId), comment="Store base LDS offset, will be modified"))
|
|
module.add(VReadfirstlaneB32(dst=sgpr("LocalWriteBaseAddrMXSB"), src=vgpr(vgprWaveId), comment="Store base LDS offset, will be modified"))
|
|
module.add(SAddU32(dst=sgpr("LocalWriteBaseAddrMXSA"), src0=sgpr("LocalWriteBaseAddrMXSA"), src1=hex(writer.ldsStartOffsetMXSA), comment=""))
|
|
module.add(SAddU32(dst=sgpr("LocalWriteBaseAddrMXSB"), src0=sgpr("LocalWriteBaseAddrMXSB"), src1=hex(writer.ldsStartOffsetMXSB), comment=""))
|
|
|
|
module.add(SAddU32(dst=sgpr("SwapMXSA"), src0=sgpr("LocalWriteBaseAddrMXSA"), src1=writer.ldsTotalSize, comment=""))
|
|
module.add(SXorB32(dst=sgpr("SwapMXSA"), src0=sgpr("LocalWriteBaseAddrMXSA"), src1=sgpr("SwapMXSA"), comment=""))
|
|
module.add(SAddU32(dst=sgpr("SwapMXSB"), src0=sgpr("LocalWriteBaseAddrMXSB"), src1=writer.ldsTotalSize, comment=""))
|
|
module.add(SXorB32(dst=sgpr("SwapMXSB"), src0=sgpr("LocalWriteBaseAddrMXSB"), src1=sgpr("SwapMXSB"), comment=""))
|
|
|
|
writer.vgprPool.checkIn(vgprWaveId)
|
|
return module
|