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

1186 lines
68 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 .AsmAddressCalculation import AddrCalculation
from .Common import DataDirection, isSubtileMultiDU
from .Common.DataType import DataType
from math import ceil, trunc, modf
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class VectorUnit:
dataType: Optional[DataType] = None
dstVgpr: int = -1
offsetVgpr: int = -1
turn: int = 0
ldsOffset: int = 0
@dataclass
class VectorDataTypes:
scaleA: VectorUnit = field(default_factory=VectorUnit)
scaleB: VectorUnit = field(default_factory=VectorUnit)
biasM: VectorUnit = field(default_factory=VectorUnit)
biasN: VectorUnit = field(default_factory=VectorUnit)
scaleAlphaM: VectorUnit = field(default_factory=VectorUnit)
scaleAlphaN: VectorUnit = field(default_factory=VectorUnit)
def bias(self, dim):
return self.biasM if dim == 0 else self.biasN
def scaleAlpha(self, dim):
return self.scaleAlphaM if dim == 0 else self.scaleAlphaN
def isValid(self):
return self.scaleA.dataType != None or self.scaleB.dataType != None or self.biasM.dataType != None \
or self.scaleAlphaM.dataType != None or self.biasN.dataType != None or self.scaleAlphaN.dataType != None
##############################################################################
# StoreState
# tracks state that is preserved across globalWriteBatch calls:
# init is called before globalWriteBatch
# the KernelWriter object
##############################################################################
class StoreState:
##############################################################################
# Setup store config for number of sgpr and vgpr needed
# These are set based on edge, atomic, etc - do not change during
# the generation of the store code.
##############################################################################
class StoreConstConfig:
def __init__(self, kernelWriter, kernel, ss, gwvw, edge, beta, atomic, isWorkspace=False):
self.gwvw = gwvw
self.lsu = kernel["LocalSplitU"]
if ss.optSingleColVgpr:
# use one vgpr (allocated in ss.sharedColDVgprs) for all addressing
# - need 0 additional vgpr per element.
self.numVgprsPerAddr = 0
else:
self.numVgprsPerAddr = kernelWriter.states.rpgo if kernel["BufferStore"] else kernelWriter.states.rpga
if ss.optSGPRUsage == 'BufferLoad_Mask':
self.numMaskSgprPerElement = 0
self.numMaskSgprPerBatch = 0
self.numTempSgprPerBatch = (2 if getattr(kernelWriter.states, 'storeAlign8', False) else 1) * kernelWriter.states.laneSGPRCount
elif ss.optSGPRUsage == 'BufferLoad_Edge_Mask':
self.numMaskSgprPerElement = 0
self.numMaskSgprPerBatch = kernelWriter.states.laneSGPRCount
self.numTempSgprPerBatch = 2 * kernelWriter.states.laneSGPRCount
else:
self.numMaskSgprPerElement = kernelWriter.states.laneSGPRCount
self.numMaskSgprPerBatch = 0
self.numTempSgprPerBatch = 2 * kernelWriter.states.laneSGPRCount
if self.numMaskSgprPerElement:
numSgprAvailable = kernelWriter.states.regCaps["MaxSgpr"] - kernelWriter.sgprPool.size() + kernelWriter.sgprPool.availableBlockAtEnd()
numSgprAvailable = numSgprAvailable & ~0x1 # make sure it's aligned
#print("numSgprAvailable=", numSgprAvailable)
self.numElementsPerBatchLimitedBySgprs = (numSgprAvailable - self.numTempSgprPerBatch - self.numMaskSgprPerBatch) // self.numMaskSgprPerElement
else:
self.numElementsPerBatchLimitedBySgprs = 9999 # no limit
if self.numElementsPerBatchLimitedBySgprs<=0:
kernelWriter.overflowedResources = 2
self.numElementsPerBatchLimitedBySgprs = 1 # dummy value
#assert self.numElementsPerBatchLimitedBySgprs > 0, "numElementsPerBatchLimitedBySgprs=0 for %s"%self.kernelName
bpeC = kernelWriter.states.bpeCexternal
if isWorkspace:
bpeC = kernelWriter.states.bpeCinternal
if atomic:
# flat atomics have another VGPR to allow different data for return#
regsPerElement = 2 if kernel["BufferStore"] else (3 + 1) # + 1 for alignment
# The atomic loop processes multiple elements in single instruction
# so will use VGPR from consec elements? TODO
self.numVgprsPerDataPerVI = (1.0 * regsPerElement * bpeC) / kernelWriter.states.bpr
elif beta:
self.numVgprsPerDataPerVI = (1.0 * bpeC) / kernelWriter.states.bpr
else:
self.numVgprsPerDataPerVI = 0.0
if kernelWriter.states.serializedStore:
#self.numVgprPerValuC = kernel["MIRegPerOut"]
self.numVgprPerValuC = kernelWriter.states.bpeCinternal//kernelWriter.states.bpr # vgpr needed from register pool
else:
self.numVgprPerValuC = 0 # null since they are already declared in macro part of assembly kernel
# indicates each vector element is actually half -
# changes vgpr allocation so two elements share a data vgpr
# Really only used if gwvw=1 - edge cases
# exception: data vgpr cannot be shared if UseInitialStridesCD is enabled and card enable EccHalf,
# since each buffer_load_short would overwrite undefined 16bit as zero.
self.halfDataRegPerVI = gwvw*self.numVgprsPerDataPerVI == 0.5 and not (kernel["ProblemType"]["UseInitialStridesCD"] and (kernelWriter.states.archCaps["HasEccHalf"] or not kernelWriter.states.asmCaps["HasWMMA_V1"])) and not (kernel["ProblemType"]["DestDataType"].numRegisters() == 0.25)
# indicates the VGPRs index offset from LSU Reduction.
# Used for multi-batch/Edge cases.
self.lsuStartVgprOffset = 0
# StoreState constructor:
def __init__(self, kernelWriter, kernel, gwvw, edge, beta, atomic, elements, vectorDataTypes, dim, isWorkspace=False):
self.kernelWriter = kernelWriter
self.kernel = kernel
self.lsu = kernel["LocalSplitU"]
self.lsuStartVgprOffset = 0
self.factorDim = dim
self.vectorDataTypes = vectorDataTypes
self.isReset = False
#--
# Optimizations for coord0/column address calculations:
#
# optSingleColVgpr:
# - works in cases where the data is written row by row to memory.
# In this case we can use a single vgpr for addressing:
# - Use the load/store instruction offset (fixed at compile-time)
# - the horizontal addresses are fixed offsets from the base
# - as we move to a new row, increment the appropriate SRDs
# optSharedColVgpr:
# - Each col gets it's own address, but elements in later rows with the same col will share VGPR.
# - allows cols to be non-adjacent
# - this is mutually exclusive with optSingleColVgpr - not as optimal but provides
# more flexibility.
# optSrdIncForRow: optimize coord1/row address calculations:
# - Move the SRD between memory operations to get to new row
# atomic needs to reset the SRD to handle retry loop. Then might work.
self.optSingleColVgpr = 0
self.optSharedColVgpr = 0
self.optSrdIncForRow = 0
# opt*ColVgpr doesn't work for edge since each element requires own addr VGPR so
# we can perform bounds check and set to -1 for OOB accesses.
# if optSingleColVgpr = optSharedColVgpr = 0, then each element gets
# 1-2 VGPRs to track address. Address calcs are performed independently
# for each element.
# atomic contains multiple memory operations which need to preserve
# the address for each load. Memops in same row can use offsets
# and share a base register but Memops in different rows need
# different registers or need to inteligently reset the SRD.
if kernel["BufferStore"] and not edge and not atomic:
if len(kernel["PackedC0IndicesX"]) > 1:
# packed mode needs a unique VGPR address calc for each column.
self.optSharedColVgpr = 1
elif len(kernel["PackedC1IndicesX"]) > 1:
self.optSharedColVgpr = 0
self.optSingleColVgpr = 0
else:
self.optSingleColVgpr = 1
if not atomic and len(kernel["PackedC1IndicesX"]) == 1:
self.optSrdIncForRow = 1
if kernel["StoreRemapVectorWidth"] and not isWorkspace:
self.optSrdIncForRow = 1
if kernel["ProblemType"]["UseInitialStridesCD"]:
self.optSingleColVgpr = 0 # BOZO, hack to disable this
self.optSharedColVgpr = 0# BOZO, hack to disable this
self.optSGPRUsage = None
if kernel["BufferStore"] and (not atomic):
self.optSGPRUsage = 'BufferLoad_Edge_Mask' if edge else 'BufferLoad_Mask'
# can't have both of these enabled:
assert (not (self.optSingleColVgpr and self.optSharedColVgpr))
self.cfg = self.StoreConstConfig(kernelWriter, kernel, self, gwvw, edge, beta, atomic, isWorkspace)
# Use to detect new rows:
self.lastCoordOffset1 = 0
# vgpr holding current coord, setup initial state
self.coord1Vgpr = kernelWriter.vgprs.coord1
# epilogue related
self.useBias = kernelWriter.states.useBias
if kernel["StreamK"] > 0 and isWorkspace:
self.useBias = DataDirection.NONE
self.useGateResidual = kernelWriter.states.useGateResidual
if kernel["StreamK"] > 0 and isWorkspace:
self.useGateResidual = False
isSingleKernel = ((kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1) or kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel") or (kernel["StreamK"] > 0 and not isWorkspace)
self.referenceVgprDim = [[], []]
if self.useBias == DataDirection.READ:
self.referenceVgprDim[self.factorDim].append("Bias")
if kernel["ProblemType"]["UseScaleAlphaVec"] and isSingleKernel:
self.referenceVgprDim[self.factorDim].append("ScaleAlpha")
if (kernel["ProblemType"]["UseScaleAB"] == "Vector") and isSingleKernel:
self.referenceVgprDim[0].append("ScaleA")
self.referenceVgprDim[1].append("ScaleB")
if self.optSharedColVgpr:
numCols = len([e for e in elements if e[0] == 0 and e[2] == 0]) # count #elements with row d1=v1==0
self.numAddrVgpr = numCols
self.sharedColDVgprs = kernelWriter.vgprPool.checkOut(self.numAddrVgpr, "sharedColDVgprs for packed elements")
if kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel":
self.sharedGSUSyncVgprs = kernelWriter.vgprPool.checkOut(self.numAddrVgpr, "sharedGSUSyncVgprs for packed elements")
else:
self.sharedGSUSyncVgprs = None
if kernel["GroupLoadStore"] and kernel["ProblemType"]["UseBeta"]:
self.sharedColCVgprs = kernelWriter.vgprPool.checkOut(self.numAddrVgpr, "sharedColCVgprs for packed elements")
else:
self.sharedColCVgprs = self.sharedColDVgprs
if self.useBias != DataDirection.NONE:
self.sharedColBiasVgprs = kernelWriter.vgprPool.checkOut(self.numAddrVgpr, "sharedColBiasVgprs for packed elements")
else:
self.sharedColBiasVgprs = None
if kernel["ProblemType"]["UseE"] and (kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1):
self.sharedColEVgprs = kernelWriter.vgprPool.checkOut(self.numAddrVgpr, "sharedColEVgprs for packed elements")
else:
self.sharedColEVgprs = None
if self.useGateResidual and (kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1 or kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel"):
self.sharedColGateVgprs = kernelWriter.vgprPool.checkOut(self.numAddrVgpr, "sharedColGateVgprs for packed elements")
else:
self.sharedColGateVgprs = None
if kernel["ProblemType"]["UseScaleAlphaVec"] and isSingleKernel:
# Only allocate a separate ScaleAlphaVec column-address vgpr for
# multi-DU kernels. Non-multi-DU reuses the Bias column address
# (localReferenceVgpr) for the SAV LDS read, so a dedicated SAV
# address would only force a redundant per-read recompute in
# emitLdChange.
if (kernel.get("UseSubtileImpl") and self.useBias == DataDirection.READ and isSubtileMultiDU(kernel)) or \
(self.referenceVgprDim[self.factorDim] and self.referenceVgprDim[self.factorDim][0] == "ScaleAlpha"):
self.sharedColScaleAlphaVecVgprs = kernelWriter.vgprPool.checkOut(self.numAddrVgpr, "sharedColScaleAlphaVecVgprs for packed elements")
else:
self.sharedColScaleAlphaVecVgprs = None
else:
self.sharedColScaleAlphaVecVgprs = None
if (kernel["ProblemType"]["UseScaleAB"] == "Vector") and isSingleKernel:
if self.referenceVgprDim[0] and self.referenceVgprDim[0][0] == "ScaleA":
self.sharedColScaleAVecVgprs = kernelWriter.vgprPool.checkOut(self.numAddrVgpr, "sharedColScaleAVecVgprs for packed elements")
else:
self.sharedColScaleAVecVgprs = None
if self.referenceVgprDim[1] and self.referenceVgprDim[1][0] == "ScaleB":
self.sharedColScaleBVecVgprs = kernelWriter.vgprPool.checkOut(self.numAddrVgpr, "sharedColScaleBVecVgprs for packed elements")
else:
self.sharedColScaleBVecVgprs = None
else:
self.sharedColScaleAVecVgprs = None
self.sharedColScaleBVecVgprs = None
elif self.optSingleColVgpr:
self.numAddrVgpr = 1
self.sharedColDVgprs = kernelWriter.vgprPool.checkOut(1, "sharedColDVgprs")
if kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel":
self.sharedGSUSyncVgprs = kernelWriter.vgprPool.checkOut(1, "sharedGSUSyncVgprs")
else:
self.sharedGSUSyncVgprs = None
self.singleColBiasAddrUpdated = False
self.singleColEAddrUpdated = False
self.singleColDAddrUpdated = False
self.singleColTDAddrUpdated = False
self.singleColCAddrUpdated = False
self.singleColGateAddrUpdated = False
if kernel["ProblemType"]["UseBeta"]:
self.sharedColCVgprs = kernelWriter.vgprPool.checkOut(1, "sharedColCVgprs")
else:
self.sharedColCVgprs = self.sharedColDVgprs
if self.useBias != DataDirection.NONE:
self.sharedColBiasVgprs = kernelWriter.vgprPool.checkOut(1, "sharedColBiasVgprs for packed elements")
else:
self.sharedColBiasVgprs = None
if kernel["ProblemType"]["UseE"] and (kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1):
self.sharedColEVgprs = kernelWriter.vgprPool.checkOut(1, "sharedColEVgprs for packed elements")
else:
self.sharedColEVgprs = None
if self.useGateResidual and (kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1 or kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel"):
self.sharedColGateVgprs = kernelWriter.vgprPool.checkOut(1, "sharedColGateVgprs")
else:
self.sharedColGateVgprs = None
if kernel["ProblemType"]["UseScaleAlphaVec"] and isSingleKernel:
# Only allocate a separate ScaleAlphaVec column-address vgpr for
# multi-DU kernels (see note above).
if (kernel.get("UseSubtileImpl") and self.useBias == DataDirection.READ and isSubtileMultiDU(kernel)) or \
(self.referenceVgprDim[self.factorDim] and self.referenceVgprDim[self.factorDim][0] == "ScaleAlpha"):
self.sharedColScaleAlphaVecVgprs = kernelWriter.vgprPool.checkOut(1, "sharedColScaleAlphaVecVgprs for packed elements")
else:
self.sharedColScaleAlphaVecVgprs = None
else:
self.sharedColScaleAlphaVecVgprs = None
if (kernel["ProblemType"]["UseScaleAB"] == "Vector") and isSingleKernel:
if self.referenceVgprDim[0] and self.referenceVgprDim[0][0] == "ScaleA":
self.sharedColScaleAVecVgprs = kernelWriter.vgprPool.checkOut(1, "sharedColScaleAVecVgprs for packed elements")
else:
self.sharedColScaleAVecVgprs = None
if self.referenceVgprDim[1] and self.referenceVgprDim[1][0] == "ScaleB":
self.sharedColScaleBVecVgprs = kernelWriter.vgprPool.checkOut(1, "sharedColScaleBVecVgprs for packed elements")
else:
self.sharedColScaleBVecVgprs = None
else:
self.sharedColScaleAVecVgprs = None
self.sharedColScaleBVecVgprs = None
else:
self.numAddrVgpr = 0
self.sharedColEVgprs = None
self.sharedColDVgprs = None
self.sharedGSUSyncVgprs = None
self.sharedColCVgprs = None
self.sharedColBiasVgprs = None
self.sharedColGateVgprs = None
self.sharedColScaleAVecVgprs = None
self.sharedColScaleBVecVgprs = None
self.sharedColScaleAlphaVecVgprs = None
# For detecting when we are running first batch
self.firstBatch = True
# TODO code migrated from globalWrite, comments outdated with respect to features like activation
# 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
# Calculate numVgprsPerElement
# print("numVgprsPerAddr=%u, numVgprsPerDataPerVI=%u, numVgprPerValuC=%u"%(self.cfg.numVgprsPerAddr, self.cfg.numVgprsPerDataPerVI, self.cfg.numVgprPerValuC))
self.numVgprsPerElement = self.cfg.numVgprPerValuC*gwvw + self.cfg.numVgprsPerAddr + int(ceil(self.cfg.numVgprsPerDataPerVI * gwvw))
# TODO STREAM-K Update numVgprsPerElement for workspace
if kernel["GroupLoadStore"] and kernel["ProblemType"]["UseBeta"]:
self.numVgprsPerElement += self.cfg.numVgprsPerAddr
if kernel["ProblemType"]["UseE"] and (kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1):
self.numVgprsPerElement += self.cfg.numVgprsPerAddr # E address
# Only needed in gradient activation
if (kernel["ProblemType"]["Gradient"] and kernel["ProblemType"]["ActivationType"] != 'none'):
numVgprs = int(ceil(kernel["ProblemType"]["ComputeDataType"].numRegisters()))
self.numVgprsPerElement += numVgprs * gwvw # Loaded data
if self.useGateResidual and (kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1 or kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel"):
numVgprs = int(ceil(kernel["ProblemType"]["ComputeDataType"].numRegisters()))
self.numVgprsPerElement += numVgprs * gwvw # Loaded data
# We will use the same vgpr + ds_offset to load the vec addr
if self.useBias != DataDirection.NONE:
self.numVgprsPerElement += self.cfg.numVgprsPerAddr # Bias address
if self.useBias == DataDirection.READ:
numVgprs = int(ceil(kernel["ProblemType"]["ComputeDataType"].numRegisters()))
self.numVgprsPerElement += numVgprs * gwvw if self.factorDim == 0 else min(gwvw, 2) # Loaded data
if kernel["ProblemType"]["UseScaleAlphaVec"] and isSingleKernel:
if self.referenceVgprDim[self.factorDim] and self.referenceVgprDim[self.factorDim][0] == "ScaleAlpha":
self.numVgprsPerElement += self.cfg.numVgprsPerAddr # ScaleAlphaVec address
numVgprs = int(ceil(kernel["ProblemType"]["ComputeDataType"].numRegisters()))
self.numVgprsPerElement += numVgprs * gwvw if self.factorDim == 0 else min(gwvw, 2) # Loaded data
if (kernel["ProblemType"]["UseScaleAB"] == "Vector") and isSingleKernel:
if self.referenceVgprDim[0] and self.referenceVgprDim[0][0] == "ScaleA":
self.numVgprsPerElement += self.cfg.numVgprsPerAddr # ScaleAVec address
if self.referenceVgprDim[1] and self.referenceVgprDim[1][0] == "ScaleB":
self.numVgprsPerElement += self.cfg.numVgprsPerAddr # ScaleBVec address
numVgprs = int(ceil(kernel["ProblemType"]["ComputeDataType"].numRegisters()))
self.numVgprsPerElement += numVgprs * gwvw + (numVgprs * min(gwvw, 2)) # Loaded data
if kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel":
self.numVgprsPerElement += self.cfg.numVgprsPerAddr # addrGSUSyncVgprs
# Calculate align
self.align = 1
# align adjustment
if self.cfg.numVgprsPerAddr > 1:
self.align = max(self.align, self.cfg.numVgprsPerAddr)
if self.cfg.numVgprPerValuC*gwvw > 1:
self.align = max(self.align, self.cfg.numVgprPerValuC*gwvw)
if int(ceil(self.cfg.numVgprsPerDataPerVI * gwvw)) > 1:
self.align = max(self.align, int(ceil(self.cfg.numVgprsPerDataPerVI * gwvw)))
##############################################################################
# Setup data structures to feed store loops:
# self.elementAddr, self.elementData, self.elementMask, self.elementSumIdx
# batchElements is a list of (d0,d1,v0,v1) for which stores to perform
# batchElementSgprs is SGPRs to use for mask. If None, elementMask is
# not initialized.
#
# Also create an AddrCalc for each memory operation.
##############################################################################
def getStoreElementsInfoForBatch(self, kernel, batchElements):
self.elementCoord0 = []
self.elementCoord1 = []
self.elementSumIdx = []
kw = self.kernelWriter
if kernel["EnableMatrixInstruction"]:
matrixInstT = min(kernel["MatrixInstM"], kernel["MatrixInstN"])
matrixInstBM = kernel["MatrixInstM"] // matrixInstT
matrixInstBN = kernel["MatrixInstN"] // matrixInstT
matrixInstM = (kernel["MatrixInstM"] * kernel["MatrixInstBM"]) if (kernel["MatrixInstM"] == 4) else matrixInstT
matrixInstN = (kernel["MatrixInstN"] * kernel["MatrixInstBN"]) if (kernel["MatrixInstN"] == 4) else matrixInstT
matrixInstBM = 1 if (kernel["MatrixInstM"] == 4) else kernel["MatrixInstBM"] * matrixInstBM
matrixInstBN = 1 if (kernel["MatrixInstN"] == 4) else kernel["MatrixInstBN"] * matrixInstBN
for elementIdx in range(0, len(batchElements)):
element = batchElements[elementIdx]
(d1,d0,vc1,vc0) = element
coordOffset1 = 0
if kernel["EnableMatrixInstruction"]:
vectorWidth = kernel["VectorWidthB"] # TODO: nonSwap VectorWidth
MIOutputVectorWidth = kernel["MIOutputVectorWidth"]
MFMAContinuousOutputs = MIOutputVectorWidth if kernel["SourceSwap"] else 1
OutputsPerMIMN = (matrixInstM * matrixInstN // self.kernel["WavefrontSize"]) if kernel["SourceSwap"] else 1
eIdx1 = d1 % (OutputsPerMIMN // MFMAContinuousOutputs)
remain_d1 = d1 // (OutputsPerMIMN // MFMAContinuousOutputs)
bIdx1 = remain_d1 % matrixInstBN
remain_d1 = remain_d1 // matrixInstBN
wtIdex = remain_d1 % kernel["MIWaveTile"][1]
coordOffset1 = eIdx1 * (self.kernel["WavefrontSize"] // matrixInstN) * MFMAContinuousOutputs
coordOffset1 += bIdx1 * matrixInstN
# Subtile kernels: successive wave tiles step by MIBShape1 (not MIBShape1 * MIWaveGroup[1]).
wtStep1 = matrixInstN * matrixInstBN if kernel.get("UseSubtileImpl") else matrixInstN * matrixInstBN * kernel["MIWaveGroup"][1]
coordOffset1 += wtIdex * wtStep1
coordOffset1 = coordOffset1 * vectorWidth + vc1
else: # mac instruction
if kernel["LocalSplitU"] > 1:
strideD1 = (kernel["NumThreads"]*kernel["VectorWidthB"]//kernel["MacroTile0"])
else:
strideD1 = (kernel["SubGroup1"] * kernel["VectorWidthB"])
coordOffset1 = d1 * strideD1 + vc1
newCoord1 = (self.firstBatch and elementIdx==0) or (coordOffset1 != self.lastCoordOffset1)
self.elementCoord1.append(coordOffset1)
# gpr and offset assignments for element
coordOffset0 = 0
if kernel["EnableMatrixInstruction"]:
vectorWidth = kernel["VectorWidthA"]
MFMAContinuousOutputs = 1 if kernel["SourceSwap"] else kernel["MIOutputVectorWidth"]
OutputsPerMIMN = 1 if kernel["SourceSwap"] else matrixInstM * matrixInstN // self.kernel["WavefrontSize"]
eIdx0 = d0 % (OutputsPerMIMN // MFMAContinuousOutputs)
remain_d0 = d0 // (OutputsPerMIMN // MFMAContinuousOutputs)
bIdx0 = remain_d0 % matrixInstBM
remain_d0 = remain_d0 // matrixInstBM
wtIdex = remain_d0 % kernel["MIWaveTile"][0]
coordOffset0 = eIdx0 * (self.kernel["WavefrontSize"] // matrixInstM) * MFMAContinuousOutputs
coordOffset0 += bIdx0 * matrixInstM
# Subtile kernels: each wave owns a contiguous block of rows, so successive
# wave tiles step by MIBShape0 (not MIBShape0 * MIWaveGroup[0]).
wtStep = matrixInstM * matrixInstBM if kernel.get("UseSubtileImpl") else matrixInstM * matrixInstBM * kernel["MIWaveGroup"][0]
coordOffset0 += wtIdex * wtStep
coordOffset0 = coordOffset0 * vectorWidth + vc0
else: # mac instruction
coordOffset0 = d0 * kernel["SubGroup0"]*kernel["VectorWidthA"] + vc0
self.elementCoord0.append(coordOffset0)
return self.elementCoord0, self.elementCoord1
def setupStoreElementsForBatchWihoutVgprCheckOut(self, kernel, gwvw, batchElements, batchElementSgprs, isOptNLL, factorDim, isWorkspace=False, elementStartIdx=0):
self.elementAddr = []
self.elementDataE = []
self.elementDataGate = []
self.elementData = [] # VGPR to use for element data, needed for atomic or beta
self.elementDataBias = []
self.elementDataScaleAVec = []
self.elementDataScaleBVec = []
self.elementDataScaleAlphaVec = []
self.elementMask = [] # SGPR to use for element mask
self.elementSumIdx = []
self.elementCoord0 = []
self.elementCoord1 = []
#generate elementCoord0/1
self.getStoreElementsInfoForBatch(kernel, batchElements)
kw = self.kernelWriter
dataType = kernel["ProblemType"]["DestDataType"]
if isWorkspace:
dataType = kernel["ProblemType"]["ComputeDataType"]
biasVgprMap = {}
scaleAVecVgprMap = {}
scaleBVecVgprMap = {}
scaleAlphaVecVgprMap = {}
lastData = 0
for elementIdx in range(0, len(batchElements)):
# Create the AddrCalc for each memory load/store
# This is the control code that sets up the dest, source, offsets, etc and
# identifies cases where the AddrCalc is a new row and therefore needs some
# additional math. Each AddrCalc contains isolated state sufficient to
# perform any needed range checks and address calculations for the element.
#
# The AddrCalc creation code here maintains state across elements (including
# across write batches) to remove replicated calculations.
#
# Later the AddrCalc::emitAddressSetupCode will emit the necessary code
# Also allocate VGPR resources here, if needed.
element = batchElements[elementIdx]
coordOffset0 = self.elementCoord0[elementIdx]
coordOffset1 = self.elementCoord1[elementIdx]
newCoord1 = (self.firstBatch and elementIdx==0) or (coordOffset1 != self.lastCoordOffset1)
(d1,d0,vc1,vc0) = element
#print "Edge=", edge, element
sumIdx = 0
if kernel["LocalSplitU"] > 1:
if len(self.elementSumIdx) == 0:
sumIdx = kw.states.c.startVgprValu // self.cfg.numVgprPerValuC + elementStartIdx * gwvw
else:
sumIdx = self.elementSumIdx[-1] + self.cfg.gwvw
else:
bestVw = kernel["VectorWidthA"]
elementsLoadedPerVw = kernel["NumThreads"] * bestVw
elementsLoadedPerbestVw = kernel["NumThreads"] * kernel["StoreVectorWidth"]
if elementsLoadedPerVw < elementsLoadedPerbestVw:
bestVw = kernel["StoreVectorWidth"]
if kernel["EnableMatrixInstruction"]:
alignment = self.cfg.numVgprPerValuC * self.cfg.gwvw
sumIdx = kw.vgprPool.checkOutAligned(self.cfg.numVgprPerValuC*self.cfg.gwvw, alignment, "vgprValuC") // self.cfg.numVgprPerValuC
else:
sumIdx = kw.states.c.startVgprValu + vc0 + d0*kernel["VectorWidthA"] + vc1*kernel["ThreadTile0"] + d1*kernel["VectorWidthA"]*kernel["ThreadTile0"]
self.elementSumIdx.append(sumIdx) # sumIdx is an element idx, need to div/2 for half
for elementIdx in range(0, len(batchElements)):
# Create the AddrCalc for each memory load/store
# This is the control code that sets up the dest, source, offsets, etc and
# identifies cases where the AddrCalc is a new row and therefore needs some
# additional math. Each AddrCalc contains isolated state sufficient to
# perform any needed range checks and address calculations for the element.
#
# The AddrCalc creation code here maintains state across elements (including
# across write batches) to remove replicated calculations.
#
# Later the AddrCalc::emitAddressSetupCode will emit the necessary code
# Also allocate VGPR resources here, if needed.
element = batchElements[elementIdx]
coordOffset0 = self.elementCoord0[elementIdx]
coordOffset1 = self.elementCoord1[elementIdx]
newCoord1 = (self.firstBatch and elementIdx==0) or (coordOffset1 != self.lastCoordOffset1)
(d1,d0,vc1,vc0) = element
# if numVgprsPerDataPerVI == 0.5, then two consecutive elements
# should have same data pointer, next should move.
if self.cfg.numVgprsPerDataPerVI > 0:
if self.cfg.halfDataRegPerVI:
# TODO- check (H,H,H,H,S,S)
if kernel["ProblemType"]["HighPrecisionAccumulate"] and \
(dataType.isBFloat16() or dataType.isHalf()):
data = kw.vgprPool.checkOutAligned(int(2*self.cfg.numVgprsPerDataPerVI*self.cfg.gwvw), \
int(ceil(int(2*self.cfg.numVgprsPerDataPerVI*self.cfg.gwvw))), "writeBatch-data for ei=%u and ei=%u"%(elementIdx,elementIdx+1), preventOverflow=not isOptNLL)
else:
if elementIdx%2 == 0:
# allocate for two elements:
data = kw.vgprPool.checkOutAligned(int(2*self.cfg.numVgprsPerDataPerVI*self.cfg.gwvw), \
int(ceil(int(2*self.cfg.numVgprsPerDataPerVI*self.cfg.gwvw))), "writeBatch-data for ei=%u and ei=%u"%(elementIdx,elementIdx+1), preventOverflow=not isOptNLL)
lastData = data
else:
data = lastData
del lastData
else:
if self.cfg.numVgprsPerDataPerVI == 0.5 or self.cfg.numVgprsPerDataPerVI == 0.25:
data = kw.vgprPool.checkOutAligned(int(ceil(self.cfg.numVgprsPerDataPerVI*self.cfg.gwvw)), \
int(ceil(self.cfg.numVgprsPerDataPerVI*self.cfg.gwvw)), "writeBatch-data for ei=%u"%elementIdx, preventOverflow=False)
else:
data = kw.vgprPool.checkOutAligned(int(self.cfg.numVgprsPerDataPerVI*self.cfg.gwvw), \
int(ceil(self.cfg.numVgprsPerDataPerVI*self.cfg.gwvw)), "writeBatch-data for ei=%u"%elementIdx, preventOverflow=False)
#data = kw.vgprPool.checkOut(int(self.cfg.numVgprsPerDataPerVI*self.cfg.gwvw), \
# "writeBatch-data for ei=%u"%elementIdx, preventOverflow=False)
else:
data = 0
self.elementData.append(data)
# if self.useBias == DataDirection.READ:
# coordOffset = coordOffset0 if factorDim == 0 else coordOffset1
# if coordOffset in biasVgprMap:
# dataBias = biasVgprMap[coordOffset]
# else:
# gwvw = self.cfg.gwvw if factorDim == 0 else min(self.cfg.gwvw, 2)
# numVgprs = int(ceil(kernel["ProblemType"]["ComputeDataType"].numRegisters()))
# dataBias = kw.vgprPool.checkOutAligned(int(numVgprs*gwvw), \
# int(ceil(numVgprs*gwvw)), "bias data for ei=%u"%elementIdx, preventOverflow=False)
# biasVgprMap[coordOffset] = dataBias
# else:
# dataBias = 0
# self.elementDataBias.append(dataBias)
# Only needed in gradient activation
# if (kernel["ProblemType"]["Gradient"] and kernel["ProblemType"]["ActivationType"] != 'none' and kernel["ProblemType"]["UseE"]) and (kernel["GlobalSplitU"] == 1):
# numVgprs = int(ceil(kernel["ProblemType"]["ComputeDataType"].numRegisters()))
# dataE = kw.vgprPool.checkOutAligned(int(numVgprs*self.cfg.gwvw), \
# int(ceil(numVgprs*self.cfg.gwvw)), "e data for ei=%u"%elementIdx, preventOverflow=False)
# else:
# dataE = 0
# self.elementDataE.append(dataE)
# if (kernel["ProblemType"]["UseScaleAB"] == "Vector") and ((kernel["GlobalSplitU"] == 1) or (kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel")):
# if coordOffset0 in scaleAVecVgprMap:
# dataScaleAVec = scaleAVecVgprMap[coordOffset0]
# else:
# numVgprs = int(ceil(kernel["ProblemType"]["ComputeDataType"].numRegisters()))
# dataScaleAVec = kw.vgprPool.checkOutAligned(int(numVgprs*self.cfg.gwvw), \
# int(ceil(numVgprs*self.cfg.gwvw)), "scaleAVec data for ei=%u"%elementIdx, preventOverflow=False)
# scaleAVecVgprMap[coordOffset0] = dataScaleAVec
# if coordOffset1 in scaleBVecVgprMap:
# dataScaleBVec = scaleBVecVgprMap[coordOffset1]
# else:
# gwvw = min(self.cfg.gwvw, 2)
# numVgprs = int(ceil(kernel["ProblemType"]["ComputeDataType"].numRegisters()))
# dataScaleBVec = kw.vgprPool.checkOutAligned(int(numVgprs*gwvw), \
# int(ceil(numVgprs*gwvw)), "scaleBVec data for ei=%u"%elementIdx, preventOverflow=False)
# scaleBVecVgprMap[coordOffset1] = dataScaleBVec
# else:
# dataScaleAVec = 0
# dataScaleBVec = 0
# self.elementDataScaleAVec.append(dataScaleAVec)
# self.elementDataScaleBVec.append(dataScaleBVec)
# if kernel["ProblemType"]["UseScaleAlphaVec"] and ((kernel["GlobalSplitU"] == 1) or (kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel")):
# coordOffset = coordOffset0 if factorDim == 0 else coordOffset1
# gwvw = self.cfg.gwvw if factorDim == 0 else min(self.cfg.gwvw, 2)
# if coordOffset in scaleAlphaVecVgprMap:
# dataScaleAlphaVec = scaleAlphaVecVgprMap[coordOffset]
# else:
# numVgprs = int(ceil(kernel["ProblemType"]["ComputeDataType"].numRegisters()))
# dataScaleAlphaVec = kw.vgprPool.checkOutAligned(int(numVgprs*gwvw), \
# int(ceil(numVgprs*gwvw)), "scaleAlphaVec data for ei=%u"%elementIdx, preventOverflow=False)
# scaleAlphaVecVgprMap[coordOffset] = dataScaleAlphaVec
# else:
# dataScaleAlphaVec = 0
# self.elementDataScaleAlphaVec.append(dataScaleAlphaVec)
if batchElementSgprs != None:
if self.optSGPRUsage:
mask = batchElementSgprs
else:
mask = batchElementSgprs + self.cfg.numMaskSgprPerBatch + elementIdx * self.cfg.numMaskSgprPerElement
self.elementMask.append(mask)
if self.optSingleColVgpr:
# use same address vgpr for all
addrEVgpr = self.sharedColEVgprs
addrDVgpr = self.sharedColDVgprs
addrGSUSyncVgprs = self.sharedGSUSyncVgprs
addrCVgpr = self.sharedColCVgprs
addrBiasVgpr = self.sharedColBiasVgprs
addrGateVgpr = self.sharedColGateVgprs
addrScaleAVecVgpr = self.sharedColScaleAVecVgprs
addrScaleBVecVgpr = self.sharedColScaleBVecVgprs
addrScaleAlphaVecVgpr = self.sharedColScaleAlphaVecVgprs
elif self.optSharedColVgpr:
if kernel["EnableMatrixInstruction"]:
elementCol = (d0 * kernel["MIOutputVectorWidth"] + vc0) / gwvw
else:
elementCol = (d0 * kernel["VectorWidthA"] + vc0) / gwvw
assert (modf(elementCol)[0] < 0.001)
elementCol = trunc(elementCol)
addrDVgpr = self.sharedColDVgprs+elementCol
if kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel":
addrGSUSyncVgprs = self.sharedGSUSyncVgprs+elementCol
else:
addrGSUSyncVgprs = None
addrCVgpr = self.sharedColCVgprs+elementCol
if self.useBias != DataDirection.NONE:
addrBiasVgpr = self.sharedColBiasVgprs+elementCol
else:
addrBiasVgpr = None
if kernel["ProblemType"]["UseE"] and (kernel["GlobalSplitU"] == 1):
addrEVgpr = self.sharedColEVgprs+elementCol
else:
addrEVgpr = None
if self.useGateResidual and (kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1 or kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel"):
addrGateVgpr = self.sharedColGateVgprs+elementCol
else:
addrGateVgpr = None
#print ("d0=", d0, "vc0=", vc0, "elementCol=", elementCol)
if kernel["ProblemType"]["UseScaleAlphaVec"] and (kernel["GlobalSplitU"] == 1):
if self.referenceVgprDim[self.factorDim] and self.referenceVgprDim[self.factorDim][0] == "ScaleAlpha":
addrScaleAlphaVecVgpr = self.sharedColScaleAlphaVecVgprs+elementCol
else:
addrScaleAlphaVecVgpr = None
else:
addrScaleAlphaVecVgpr = None
if (kernel["ProblemType"]["UseScaleAB"] == "Vector") and (kernel["GlobalSplitU"] == 1):
if self.referenceVgprDim[0] and self.referenceVgprDim[0][0] == "ScaleA":
addrScaleAVecVgpr = self.sharedColScaleAVecVgprs+elementCol
else:
addrScaleAVecVgpr = None
if self.referenceVgprDim[1] and self.referenceVgprDim[1][0] == "ScaleB":
addrScaleBVecVgpr = self.sharedColScaleBVecVgprs+elementCol
else:
addrScaleBVecVgpr = None
else:
addrScaleAVecVgpr = None
addrScaleBVecVgpr = None
else:
# allocate new VGPR for each element:
addrDVgpr = kw.vgprPool.checkOutAligned(self.cfg.numVgprsPerAddr, \
int(ceil(self.cfg.numVgprsPerAddr)), "writeDBatch-addr for ei=%u"%(elementIdx), preventOverflow=not isOptNLL)
if kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel":
addrGSUSyncVgprs = kw.vgprPool.checkOutAligned(self.cfg.numVgprsPerAddr, \
int(ceil(self.cfg.numVgprsPerAddr)), "writeDBatch-addr for ei=%u"%(elementIdx), preventOverflow=False)
else:
addrGSUSyncVgprs = None
if kernel["GroupLoadStore"] and kernel["ProblemType"]["UseBeta"]:
addrCVgpr = kw.vgprPool.checkOutAligned(self.cfg.numVgprsPerAddr, \
int(ceil(self.cfg.numVgprsPerAddr)), "loadCBatch-addr for ei=%u"%(elementIdx), preventOverflow=not isOptNLL)
else:
addrCVgpr = addrDVgpr
if self.useBias != DataDirection.NONE:
addrBiasVgpr = kw.vgprPool.checkOutAligned(self.cfg.numVgprsPerAddr, \
int(ceil(self.cfg.numVgprsPerAddr)), "loadBiasBatch-addr for ei=%u"%(elementIdx), preventOverflow=not isOptNLL)
else:
addrBiasVgpr = None
if kernel["ProblemType"]["UseE"] and (kernel["GlobalSplitU"] == 1):
addrEVgpr = kw.vgprPool.checkOutAligned(self.cfg.numVgprsPerAddr, \
int(ceil(self.cfg.numVgprsPerAddr)), "loadEBatch-addr for ei=%u"%(elementIdx), preventOverflow=not isOptNLL)
else:
addrEVgpr = None
if self.useGateResidual and (kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1 or kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel"):
# No-opt path: reuse D's addr
addrGateVgpr = addrDVgpr
else:
addrGateVgpr = None
if kernel["ProblemType"]["UseScaleAlphaVec"] and ((kernel["GlobalSplitU"] == 1) or (kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel")):
if self.referenceVgprDim[self.factorDim] and self.referenceVgprDim[self.factorDim][0] == "ScaleAlpha":
addrScaleAlphaVecVgpr = kw.vgprPool.checkOutAligned(self.cfg.numVgprsPerAddr, \
int(ceil(self.cfg.numVgprsPerAddr)), "loadScaleAlphaVecBatch-addr for ei=%u"%(elementIdx), preventOverflow=not isOptNLL)
else:
addrScaleAlphaVecVgpr = None
else:
addrScaleAlphaVecVgpr = None
if (kernel["ProblemType"]["UseScaleAB"] == "Vector") and ((kernel["GlobalSplitU"] == 1) or (kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel")):
if self.referenceVgprDim[0] and self.referenceVgprDim[0][0] == "ScaleA":
addrScaleAVecVgpr = kw.vgprPool.checkOutAligned(self.cfg.numVgprsPerAddr, \
int(ceil(self.cfg.numVgprsPerAddr)), "loadScaleAVecBatch-addr for ei=%u"%(elementIdx), preventOverflow=not isOptNLL)
else:
addrScaleAVecVgpr = None
if self.referenceVgprDim[1] and self.referenceVgprDim[1][0] == "ScaleB":
addrScaleBVecVgpr = kw.vgprPool.checkOutAligned(self.cfg.numVgprsPerAddr, \
int(ceil(self.cfg.numVgprsPerAddr)), "loadScaleAVecBatch-addr for ei=%u"%(elementIdx), preventOverflow=not isOptNLL)
else:
addrScaleBVecVgpr = None
else:
addrScaleAVecVgpr = None
addrScaleBVecVgpr = None
self.elementAddr.append(AddrCalculation(kw, self, addrCVgpr, addrDVgpr, addrGSUSyncVgprs, addrEVgpr, addrBiasVgpr, addrScaleAVecVgpr, addrScaleBVecVgpr, addrScaleAlphaVecVgpr, element, coordOffset0, \
self.kernelWriter.vgprs.coord1, coordOffset1, coordOffset1 - self.lastCoordOffset1, newCoord1, self.vectorDataTypes, addrGateVgpr=addrGateVgpr))
self.lastCoordOffset1 = coordOffset1
# reset flag
self.isReset = False
def setupStoreElementsForBatch(self, kernel, gwvw, batchElements, batchElementSgprs, isOptNLL, factorDim, isWorkspace=False, elementStartIdx=0):
self.elementAddr = []
self.elementDataE = []
self.elementDataGate = []
self.elementData = [] # VGPR to use for element data, needed for atomic or beta
self.elementDataBias = []
self.elementDataScaleAVec = []
self.elementDataScaleBVec = []
self.elementDataScaleAlphaVec = []
self.elementMask = [] # SGPR to use for element mask
self.elementSumIdx = []
self.elementCoord0 = []
self.elementCoord1 = []
#generate elementCoord0/1
self.getStoreElementsInfoForBatch(kernel, batchElements)
kw = self.kernelWriter
dataType = kernel["ProblemType"]["DestDataType"]
if isWorkspace:
dataType = kernel["ProblemType"]["ComputeDataType"]
biasVgprMap = {}
scaleAVecVgprMap = {}
scaleBVecVgprMap = {}
scaleAlphaVecVgprMap = {}
lastData = 0
isSingleKernel = ((kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1) or kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel") or (kernel["StreamK"] > 0 and not isWorkspace)
for elementIdx in range(0, len(batchElements)):
# Create the AddrCalc for each memory load/store
# This is the control code that sets up the dest, source, offsets, etc and
# identifies cases where the AddrCalc is a new row and therefore needs some
# additional math. Each AddrCalc contains isolated state sufficient to
# perform any needed range checks and address calculations for the element.
#
# The AddrCalc creation code here maintains state across elements (including
# across write batches) to remove replicated calculations.
#
# Later the AddrCalc::emitAddressSetupCode will emit the necessary code
# Also allocate VGPR resources here, if needed.
element = batchElements[elementIdx]
coordOffset0 = self.elementCoord0[elementIdx]
coordOffset1 = self.elementCoord1[elementIdx]
newCoord1 = (self.firstBatch and elementIdx==0) or (coordOffset1 != self.lastCoordOffset1)
(d1,d0,vc1,vc0) = element
#print "Edge=", edge, element
sumIdx = 0
if kernel["LocalSplitU"] > 1:
if len(self.elementSumIdx) == 0:
sumIdx = kw.states.c.startVgprValu // self.cfg.numVgprPerValuC + elementStartIdx * gwvw
else:
sumIdx = self.elementSumIdx[-1] + self.cfg.gwvw
else:
bestVw = kernel["VectorWidthA"]
elementsLoadedPerVw = kernel["NumThreads"] * bestVw
elementsLoadedPerbestVw = kernel["NumThreads"] * kernel["StoreVectorWidth"]
if elementsLoadedPerVw < elementsLoadedPerbestVw:
bestVw = kernel["StoreVectorWidth"]
if kernel["EnableMatrixInstruction"]:
alignment = self.cfg.numVgprPerValuC * self.cfg.gwvw
#print(self.cfg.numVgprPerValuC, self.cfg.gwvw)
#exit(1)
sumIdx = kw.vgprPool.checkOutAligned(self.cfg.numVgprPerValuC*self.cfg.gwvw, alignment, "vgprValuC") // self.cfg.numVgprPerValuC
else:
sumIdx = kw.states.c.startVgprValu + vc0 + d0*kernel["VectorWidthA"] + vc1*kernel["ThreadTile0"] + d1*kernel["VectorWidthA"]*kernel["ThreadTile0"]
self.elementSumIdx.append(sumIdx) # sumIdx is an element idx, need to div/2 for half
for elementIdx in range(0, len(batchElements)):
# Create the AddrCalc for each memory load/store
# This is the control code that sets up the dest, source, offsets, etc and
# identifies cases where the AddrCalc is a new row and therefore needs some
# additional math. Each AddrCalc contains isolated state sufficient to
# perform any needed range checks and address calculations for the element.
#
# The AddrCalc creation code here maintains state across elements (including
# across write batches) to remove replicated calculations.
#
# Later the AddrCalc::emitAddressSetupCode will emit the necessary code
# Also allocate VGPR resources here, if needed.
element = batchElements[elementIdx]
coordOffset0 = self.elementCoord0[elementIdx]
coordOffset1 = self.elementCoord1[elementIdx]
newCoord1 = (self.firstBatch and elementIdx==0) or (coordOffset1 != self.lastCoordOffset1)
(d1,d0,vc1,vc0) = element
# if numVgprsPerDataPerVI == 0.5, then two consecutive elements
# should have same data pointer, next should move.
if self.cfg.numVgprsPerDataPerVI > 0:
if self.cfg.halfDataRegPerVI:
# TODO- check (H,H,H,H,S,S)
if kernel["ProblemType"]["HighPrecisionAccumulate"] and \
(dataType.isBFloat16() or dataType.isHalf()):
data = kw.vgprPool.checkOutAligned(int(2*self.cfg.numVgprsPerDataPerVI*self.cfg.gwvw), \
int(ceil(int(2*self.cfg.numVgprsPerDataPerVI*self.cfg.gwvw))), "writeBatch-data for ei=%u and ei=%u"%(elementIdx,elementIdx+1), preventOverflow=not isOptNLL)
else:
if elementIdx%2 == 0:
# allocate for two elements:
data = kw.vgprPool.checkOutAligned(int(2*self.cfg.numVgprsPerDataPerVI*self.cfg.gwvw), \
int(ceil(int(2*self.cfg.numVgprsPerDataPerVI*self.cfg.gwvw))), "writeBatch-data for ei=%u and ei=%u"%(elementIdx,elementIdx+1), preventOverflow=not isOptNLL)
lastData = data
else:
data = lastData
del lastData
else:
if self.cfg.numVgprsPerDataPerVI == 0.5 or self.cfg.numVgprsPerDataPerVI == 0.25:
data = kw.vgprPool.checkOutAligned(int(ceil(self.cfg.numVgprsPerDataPerVI*self.cfg.gwvw)), \
int(ceil(self.cfg.numVgprsPerDataPerVI*self.cfg.gwvw)), "writeBatch-data for ei=%u"%elementIdx, preventOverflow=False)
else:
data = kw.vgprPool.checkOutAligned(int(self.cfg.numVgprsPerDataPerVI*self.cfg.gwvw), \
int(ceil(self.cfg.numVgprsPerDataPerVI*self.cfg.gwvw)), "writeBatch-data for ei=%u"%elementIdx, preventOverflow=False)
#data = kw.vgprPool.checkOut(int(self.cfg.numVgprsPerDataPerVI*self.cfg.gwvw), \
# "writeBatch-data for ei=%u"%elementIdx, preventOverflow=False)
else:
data = 0
self.elementData.append(data)
if self.useBias == DataDirection.READ:
coordOffset = coordOffset0 if factorDim == 0 else coordOffset1
if coordOffset in biasVgprMap:
dataBias = biasVgprMap[coordOffset]
else:
gwvw = self.cfg.gwvw if factorDim == 0 else min(self.cfg.gwvw, 2)
numVgprs = int(ceil(kernel["ProblemType"]["ComputeDataType"].numRegisters()))
dataBias = kw.vgprPool.checkOutAligned(int(numVgprs*gwvw), \
int(ceil(numVgprs*gwvw)), "bias data for ei=%u"%elementIdx, preventOverflow=False)
biasVgprMap[coordOffset] = dataBias
else:
dataBias = 0
self.elementDataBias.append(dataBias)
# Only needed in gradient activation
if (kernel["ProblemType"]["Gradient"] and kernel["ProblemType"]["ActivationType"] != 'none' and kernel["ProblemType"]["UseE"]) and (kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1):
numVgprs = int(ceil(kernel["ProblemType"]["ComputeDataType"].numRegisters()))
dataE = kw.vgprPool.checkOutAligned(int(numVgprs*self.cfg.gwvw), \
int(ceil(numVgprs*self.cfg.gwvw)), "e data for ei=%u"%elementIdx, preventOverflow=False)
else:
dataE = 0
self.elementDataE.append(dataE)
# Per-element gate data VGPR
if self.useGateResidual and (kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1 or kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel"):
numVgprs = int(ceil(kernel["ProblemType"]["ComputeDataType"].numRegisters()))
dataGate = kw.vgprPool.checkOutAligned(int(numVgprs*self.cfg.gwvw), \
int(ceil(numVgprs*self.cfg.gwvw)), "gate data for ei=%u"%elementIdx, preventOverflow=False)
else:
dataGate = 0
self.elementDataGate.append(dataGate)
if (kernel["ProblemType"]["UseScaleAB"] == "Vector") and isSingleKernel:
if coordOffset0 in scaleAVecVgprMap:
dataScaleAVec = scaleAVecVgprMap[coordOffset0]
else:
numVgprs = int(ceil(kernel["ProblemType"]["ComputeDataType"].numRegisters()))
dataScaleAVec = kw.vgprPool.checkOutAligned(int(numVgprs*self.cfg.gwvw), \
int(ceil(numVgprs*self.cfg.gwvw)), "scaleAVec data for ei=%u"%elementIdx, preventOverflow=False)
scaleAVecVgprMap[coordOffset0] = dataScaleAVec
if coordOffset1 in scaleBVecVgprMap:
dataScaleBVec = scaleBVecVgprMap[coordOffset1]
else:
gwvw = min(self.cfg.gwvw, 2)
numVgprs = int(ceil(kernel["ProblemType"]["ComputeDataType"].numRegisters()))
dataScaleBVec = kw.vgprPool.checkOutAligned(int(numVgprs*gwvw), \
int(ceil(numVgprs*gwvw)), "scaleBVec data for ei=%u"%elementIdx, preventOverflow=False)
scaleBVecVgprMap[coordOffset1] = dataScaleBVec
else:
dataScaleAVec = 0
dataScaleBVec = 0
self.elementDataScaleAVec.append(dataScaleAVec)
self.elementDataScaleBVec.append(dataScaleBVec)
if kernel["ProblemType"]["UseScaleAlphaVec"] and isSingleKernel:
coordOffset = coordOffset0 if factorDim == 0 else coordOffset1
gwvw = self.cfg.gwvw if factorDim == 0 else min(self.cfg.gwvw, 2)
if coordOffset in scaleAlphaVecVgprMap:
dataScaleAlphaVec = scaleAlphaVecVgprMap[coordOffset]
else:
numVgprs = int(ceil(kernel["ProblemType"]["ComputeDataType"].numRegisters()))
dataScaleAlphaVec = kw.vgprPool.checkOutAligned(int(numVgprs*gwvw), \
int(ceil(numVgprs*gwvw)), "scaleAlphaVec data for ei=%u"%elementIdx, preventOverflow=False)
scaleAlphaVecVgprMap[coordOffset] = dataScaleAlphaVec
else:
dataScaleAlphaVec = 0
self.elementDataScaleAlphaVec.append(dataScaleAlphaVec)
if batchElementSgprs != None:
if self.optSGPRUsage:
mask = batchElementSgprs
else:
mask = batchElementSgprs + self.cfg.numMaskSgprPerBatch + elementIdx * self.cfg.numMaskSgprPerElement
self.elementMask.append(mask)
if self.optSingleColVgpr:
# use same address vgpr for all
addrEVgpr = self.sharedColEVgprs
addrDVgpr = self.sharedColDVgprs
addrGSUSyncVgprs = self.sharedGSUSyncVgprs
addrCVgpr = self.sharedColCVgprs
addrBiasVgpr = self.sharedColBiasVgprs
addrGateVgpr = self.sharedColGateVgprs
addrScaleAVecVgpr = self.sharedColScaleAVecVgprs
addrScaleBVecVgpr = self.sharedColScaleBVecVgprs
addrScaleAlphaVecVgpr = self.sharedColScaleAlphaVecVgprs
elif self.optSharedColVgpr:
if kernel["EnableMatrixInstruction"]:
elementCol = (d0 * kernel["MIOutputVectorWidth"] + vc0) / gwvw
else:
elementCol = (d0 * kernel["VectorWidthA"] + vc0) / gwvw
assert (modf(elementCol)[0] < 0.001)
elementCol = trunc(elementCol)
addrDVgpr = self.sharedColDVgprs+elementCol
if kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel":
addrGSUSyncVgprs = self.sharedGSUSyncVgprs+elementCol
else:
addrGSUSyncVgprs = None
addrCVgpr = self.sharedColCVgprs+elementCol
if self.useBias != DataDirection.NONE:
addrBiasVgpr = self.sharedColBiasVgprs+elementCol
else:
addrBiasVgpr = None
if kernel["ProblemType"]["UseE"] and (kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1):
addrEVgpr = self.sharedColEVgprs+elementCol
else:
addrEVgpr = None
if self.useGateResidual and (kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1 or kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel"):
addrGateVgpr = self.sharedColGateVgprs+elementCol
else:
addrGateVgpr = None
#print ("d0=", d0, "vc0=", vc0, "elementCol=", elementCol)
if kernel["ProblemType"]["UseScaleAlphaVec"] and ((kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1) or (kernel["StreamK"] > 0 and not isWorkspace)):
if self.referenceVgprDim[self.factorDim] and self.referenceVgprDim[self.factorDim][0] == "ScaleAlpha":
addrScaleAlphaVecVgpr = self.sharedColScaleAlphaVecVgprs+elementCol
else:
addrScaleAlphaVecVgpr = None
else:
addrScaleAlphaVecVgpr = None
if (kernel["ProblemType"]["UseScaleAB"] == "Vector") and ((kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1) or (kernel["StreamK"] > 0 and not isWorkspace)):
if self.referenceVgprDim[0] and self.referenceVgprDim[0][0] == "ScaleA":
addrScaleAVecVgpr = self.sharedColScaleAVecVgprs+elementCol
else:
addrScaleAVecVgpr = None
if self.referenceVgprDim[1] and self.referenceVgprDim[1][0] == "ScaleB":
addrScaleBVecVgpr = self.sharedColScaleBVecVgprs+elementCol
else:
addrScaleBVecVgpr = None
else:
addrScaleAVecVgpr = None
addrScaleBVecVgpr = None
else:
# allocate new VGPR for each element:
# Allow pool growth for per-element D addrs; refineOccupancy still caps total VGPR.
addrDVgpr = kw.vgprPool.checkOutAligned(self.cfg.numVgprsPerAddr, \
int(ceil(self.cfg.numVgprsPerAddr)), "writeDBatch-addr for ei=%u"%(elementIdx), preventOverflow=False)
if kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel":
addrGSUSyncVgprs = kw.vgprPool.checkOutAligned(self.cfg.numVgprsPerAddr, \
int(ceil(self.cfg.numVgprsPerAddr)), "writeDBatch-addr for ei=%u"%(elementIdx), preventOverflow=False)
else:
addrGSUSyncVgprs = None
if kernel["GroupLoadStore"] and kernel["ProblemType"]["UseBeta"]:
addrCVgpr = kw.vgprPool.checkOutAligned(self.cfg.numVgprsPerAddr, \
int(ceil(self.cfg.numVgprsPerAddr)), "loadCBatch-addr for ei=%u"%(elementIdx), preventOverflow=not isOptNLL)
else:
addrCVgpr = addrDVgpr
if self.useBias != DataDirection.NONE:
addrBiasVgpr = kw.vgprPool.checkOutAligned(self.cfg.numVgprsPerAddr, \
int(ceil(self.cfg.numVgprsPerAddr)), "loadBiasBatch-addr for ei=%u"%(elementIdx), preventOverflow=not isOptNLL)
else:
addrBiasVgpr = None
if kernel["ProblemType"]["UseE"] and (kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1):
addrEVgpr = kw.vgprPool.checkOutAligned(self.cfg.numVgprsPerAddr, \
int(ceil(self.cfg.numVgprsPerAddr)), "loadEBatch-addr for ei=%u"%(elementIdx), preventOverflow=not isOptNLL)
else:
addrEVgpr = None
if self.useGateResidual and (kernel["GlobalSplitU"] == 1 or kernel["GlobalSplitU"] == -1 or kernel["_GlobalAccumulation"] == "MultipleBufferSingleKernel"):
# No-opt path: Gate reuses D's per-element addr VGPR.
addrGateVgpr = addrDVgpr
else:
addrGateVgpr = None
if kernel["ProblemType"]["UseScaleAlphaVec"] and isSingleKernel:
if self.referenceVgprDim[self.factorDim] and self.referenceVgprDim[self.factorDim][0] == "ScaleAlpha":
addrScaleAlphaVecVgpr = kw.vgprPool.checkOutAligned(self.cfg.numVgprsPerAddr, \
int(ceil(self.cfg.numVgprsPerAddr)), "loadScaleAlphaVecBatch-addr for ei=%u"%(elementIdx), preventOverflow=not isOptNLL)
else:
addrScaleAlphaVecVgpr = None
else:
addrScaleAlphaVecVgpr = None
if (kernel["ProblemType"]["UseScaleAB"] == "Vector") and isSingleKernel:
if self.referenceVgprDim[0] and self.referenceVgprDim[0][0] == "ScaleA":
addrScaleAVecVgpr = kw.vgprPool.checkOutAligned(self.cfg.numVgprsPerAddr, \
int(ceil(self.cfg.numVgprsPerAddr)), "loadScaleAVecBatch-addr for ei=%u"%(elementIdx), preventOverflow=not isOptNLL)
else:
addrScaleAVecVgpr = None
if self.referenceVgprDim[1] and self.referenceVgprDim[1][0] == "ScaleB":
addrScaleBVecVgpr = kw.vgprPool.checkOutAligned(self.cfg.numVgprsPerAddr, \
int(ceil(self.cfg.numVgprsPerAddr)), "loadScaleAVecBatch-addr for ei=%u"%(elementIdx), preventOverflow=not isOptNLL)
else:
addrScaleBVecVgpr = None
else:
addrScaleAVecVgpr = None
addrScaleBVecVgpr = None
self.elementAddr.append(AddrCalculation(kw, self, addrCVgpr, addrDVgpr, addrGSUSyncVgprs, addrEVgpr, addrBiasVgpr, addrScaleAVecVgpr, addrScaleBVecVgpr, addrScaleAlphaVecVgpr, element, coordOffset0, \
self.kernelWriter.vgprs.coord1, coordOffset1, coordOffset1 - self.lastCoordOffset1, newCoord1, self.vectorDataTypes, addrGateVgpr=addrGateVgpr))
self.lastCoordOffset1 = coordOffset1
# reset flag
self.isReset = False
def checkInTempVgprC(self):
if not hasattr(self, 'elementSumIdx'):
return # object was not fully initialized
if self.kernelWriter.states.serializedStore is False:
return # early exit; currently only serializedStore==True checks out C-tile from register pool
if len(self.elementSumIdx) > 0 and self.lsu == 1:
for i in self.elementSumIdx:
self.kernelWriter.vgprPool.checkIn(i * self.cfg.numVgprPerValuC)
# print("checked in vgpr %u"%i)
self.elementSumIdx = []
def resetState(self):
if not self.isReset:
# Init part
if self.optSharedColVgpr:
pass # Nothing to reset
elif self.optSingleColVgpr:
self.singleColBiasAddrUpdated = False
self.singleColEAddrUpdated = False
self.singleColDAddrUpdated = False
self.singleColTDAddrUpdated = False
self.singleColCAddrUpdated = False
self.singleColGateAddrUpdated = False
else:
pass # Nothing to reset
# setup store element
self.lastCoordOffset1 = 0
self.isReset = True
def __del__(self):
if (self.sharedColEVgprs != None):
self.kernelWriter.vgprPool.checkIn(self.sharedColEVgprs)
self.sharedColEVgprs = None
if (self.sharedColDVgprs != None):
self.kernelWriter.vgprPool.checkIn(self.sharedColDVgprs)
if (self.sharedColCVgprs != self.sharedColDVgprs and self.sharedColCVgprs != None):
self.kernelWriter.vgprPool.checkIn(self.sharedColCVgprs)
self.sharedColCVgprs = None
self.sharedColDVgprs = None
if (self.sharedGSUSyncVgprs != None):
self.kernelWriter.vgprPool.checkIn(self.sharedGSUSyncVgprs)
if (self.sharedColBiasVgprs != None):
self.kernelWriter.vgprPool.checkIn(self.sharedColBiasVgprs)
self.sharedColBiasVgprs = None
if (self.sharedColGateVgprs != None):
self.kernelWriter.vgprPool.checkIn(self.sharedColGateVgprs)
self.sharedColGateVgprs = None
if (self.sharedColScaleAVecVgprs != None):
self.kernelWriter.vgprPool.checkIn(self.sharedColScaleAVecVgprs)
self.sharedColScaleAVecVgprs = None
if (self.sharedColScaleBVecVgprs != None):
self.kernelWriter.vgprPool.checkIn(self.sharedColScaleBVecVgprs)
self.sharedColScaleBVecVgprs = None
if (self.sharedColScaleAlphaVecVgprs != None):
self.kernelWriter.vgprPool.checkIn(self.sharedColScaleAlphaVecVgprs)
self.sharedColScaleAlphaVecVgprs = None
self.checkInTempVgprC()
self.resetState()