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
5741 lines
285 KiB
Python
5741 lines
285 KiB
Python
################################################################################
|
|
#
|
|
# Copyright (C) 2016-2024 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
|
|
# copies 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
|
|
# IMPLIED, 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 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
# SOFTWARE.
|
|
#
|
|
################################################################################
|
|
|
|
from . import Code
|
|
from . import Common
|
|
from .Common import globalParameters, CHeader, roundUp, Backup, tPrint
|
|
from .ReplacementKernels import ReplacementKernels
|
|
from .CustomKernels import isCustomKernelConfig
|
|
from .SolutionStructs import Solution
|
|
|
|
import abc
|
|
from collections.abc import Sequence
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import copy
|
|
from math import ceil
|
|
|
|
################################################################################
|
|
# Kernel Writer
|
|
################################################################################
|
|
class KernelWriter(metaclass=abc.ABCMeta):
|
|
#__metaclass__=abc.ABCMeta
|
|
|
|
##############################################################################
|
|
# Init
|
|
##############################################################################
|
|
def __init__( self, kernelMinNaming, kernelSerialNaming, removeTemporaries=True ):
|
|
self.kernelMinNaming = kernelMinNaming
|
|
self.kernelSerialNaming = kernelSerialNaming
|
|
self.overflowedResources = 0
|
|
self.removeTemporaries = removeTemporaries
|
|
|
|
@property
|
|
def asmCaps(self):
|
|
"""
|
|
Assembler capabilities for the current ISA version.
|
|
"""
|
|
return globalParameters["AsmCaps"][self.version]
|
|
|
|
@property
|
|
def archCaps(self):
|
|
"""
|
|
Architectural capabilities for the current ISA version.
|
|
"""
|
|
return globalParameters["ArchCaps"][self.version]
|
|
|
|
@property
|
|
def globalParams(self):
|
|
"""
|
|
Global parameters for current configuration.
|
|
"""
|
|
return globalParameters
|
|
|
|
##############################################################################
|
|
# returns number of Local Read included in current loop iteration
|
|
##############################################################################
|
|
def countNumMfmaForCurrentOrNextLoopLR(self, kernel, tensorParametersA, tensorParametersB, curr=True):
|
|
numMfmaForLRA = 0
|
|
numMfmaForLRB = 0
|
|
latencyLeft = self.miLatencyLeft
|
|
issueLatencyA = min(self.miLatencyLeft, tensorParametersA["localReadInstruction"].IssueLatency*2)
|
|
issueLatencyB = min(self.miLatencyLeft, tensorParametersB["localReadInstruction"].IssueLatency*2)
|
|
# loop iteration:
|
|
# current loop: kernel["LoopIters"] - self.numItersPLR
|
|
# next loop : 1 (prefetch for the next loop iter
|
|
loopIter = kernel["LoopIters"] - self.numItersPLR if curr else 1
|
|
for u in range(loopIter):
|
|
doReadA = (not curr) or (u < kernel["LoopIters"] // self.numIterPerCoalescedReadA - self.numItersPLR)
|
|
doReadB = (not curr) or (u < kernel["LoopIters"] // self.numIterPerCoalescedReadB - self.numItersPLR)
|
|
# count the number of LocalRead even for DirectToVgpr
|
|
# (performance is better with this)
|
|
# ds_read[A][0]
|
|
for i in range(self.numReadPerVectorA * doReadA):
|
|
latencyLeft -= issueLatencyA
|
|
if latencyLeft < 0:
|
|
numMfmaForLRA += 1
|
|
latencyLeft = self.miLatencyLeft - issueLatencyA
|
|
# ds_read[B][0]
|
|
for i in range(self.numReadPerVectorB * doReadB):
|
|
latencyLeft -= issueLatencyB
|
|
if latencyLeft < 0:
|
|
numMfmaForLRB += 1
|
|
latencyLeft = self.miLatencyLeft - issueLatencyB
|
|
# ds_read[A][1:]
|
|
for i in range((self.numReadsPerIterA - self.numReadPerVectorA) * doReadA):
|
|
latencyLeft -= issueLatencyA
|
|
if latencyLeft < 0:
|
|
numMfmaForLRA += 1
|
|
latencyLeft = self.miLatencyLeft - issueLatencyA
|
|
# ds_read[B][1:]
|
|
for i in range((self.numReadsPerIterB - self.numReadPerVectorB) * doReadB):
|
|
latencyLeft -= issueLatencyB
|
|
if latencyLeft < 0:
|
|
numMfmaForLRB += 1
|
|
latencyLeft = self.miLatencyLeft - issueLatencyB
|
|
|
|
# initial value(1) + A + B
|
|
numMfmaForLR = 1 + numMfmaForLRA + numMfmaForLRB
|
|
|
|
latencyForLRCount = 0
|
|
# no need to count if DTVA and DTVB are both true (no local read code)
|
|
if not (kernel["DirectToVgprA"] and kernel["DirectToVgprB"]):
|
|
# to calculate number of mfma we need to wait before data arrive from lds to vgpr.
|
|
# latency: 40 quad-cycle for 4 word, 20 quad-cycle for 2 word, 10 quad-cycle for 1 word / half word
|
|
latencyForLRA = roundUp(tensorParametersA["localReadInstruction"].blockWidth) * 10
|
|
latencyForLRB = roundUp(tensorParametersB["localReadInstruction"].blockWidth) * 10
|
|
|
|
if kernel["DirectToVgprA"]:
|
|
# DTVA case, use B
|
|
latencyForLRA = latencyForLRB
|
|
elif kernel["DirectToVgprB"]:
|
|
# DTVB case, use A
|
|
latencyForLRB = latencyForLRA
|
|
if not curr:
|
|
latencyForLR = latencyForLRB
|
|
else:
|
|
#latencyForLR = max(latencyForLRA, latencyForLRB)
|
|
latencyForLR = latencyForLRA if self.numIterPerCoalescedReadB > self.numIterPerCoalescedReadA else latencyForLRB
|
|
|
|
latencyForLR -= max(latencyLeft,0) # remaining latency in mfma
|
|
if not curr:
|
|
latencyForLR -= self.miLatency # last LR will have 1 mfma latency
|
|
# add extra latency
|
|
latencyForLR += kernel["ExtraLatencyForLR"]
|
|
while latencyForLR > 0:
|
|
latencyForLR -= self.miLatency
|
|
latencyForLRCount += 1
|
|
|
|
if kernel["1LDSBuffer"] and curr and latencyForLRCount == 0:
|
|
# 1LDS buffer case, we need at least 1 MFMA between end of local read and start of local write (fur current local read only)
|
|
# otherwise, it results in overflowedResources = 5 error
|
|
latencyForLRCount = 1
|
|
|
|
return numMfmaForLR, latencyForLRCount, latencyLeft
|
|
|
|
##############################################################################
|
|
# set and adjust lwEndMfmaIndex
|
|
##############################################################################
|
|
def setAndAdjustLwEndMfmaIndex(self, kernel, tensorParametersA, tensorParametersB, numMfmaBetweenLWandBarrier, lastLoop):
|
|
numMfmaPerIter = self.numMfmaPerIter
|
|
self.lwEndMfmaIndex = max(self.barrierMfmaIndex - numMfmaBetweenLWandBarrier,0) if self.numItersPLR else numMfmaPerIter*kernel["LoopIters"] - 1
|
|
# adjust lwEndMfmaIndex for the following cases
|
|
# 1) PGR=2 + DirectToVgpr(DTV)
|
|
# 2) last loop and StoreCInUnrollPostLoop enabled case
|
|
# In these cases, lwEndMfmaIndex needs to be < numMfmaPerIter * (kernel["LoopIters"] - 1)
|
|
# to schedule global read for DTV after lwEndMfmaIndex or execute PostLoop after StoreC in NoLoadLoop
|
|
# kernel["LoopIters"] has to be > 1 to make this logic work.
|
|
if kernel["LoopIters"] > 1 and \
|
|
((kernel["PrefetchGlobalRead"] == 2 and (kernel["DirectToVgprA"] or kernel["DirectToVgprB"])) or \
|
|
(lastLoop and kernel["StoreCInUnrollPostLoop"])):
|
|
self.lwEndMfmaIndex = min(self.lwEndMfmaIndex, numMfmaPerIter * (kernel["LoopIters"] - 1) - 1)
|
|
# another adjustment
|
|
if (kernel["1LDSBuffer"] or kernel["DirectToLdsA"] or kernel["DirectToLdsB"]) and kernel["PrefetchGlobalRead"] == 2:
|
|
# (1LDSBuffer of DirectToLds) + PGR=2 case, lwEndMfmaIndex must be after the end of local read (excluding local reads for next iter)
|
|
numMfmaForCurrentLoopLR, latencyForLRCount, _ = self.countNumMfmaForCurrentOrNextLoopLR(kernel, tensorParametersA, tensorParametersB)
|
|
numMfmaForCurrentLoopLR += latencyForLRCount
|
|
lrEnd = min(self.barrierMfmaIndex - 1, numMfmaForCurrentLoopLR)
|
|
if self.lwEndMfmaIndex < lrEnd:
|
|
self.lwEndMfmaIndex = lrEnd
|
|
|
|
##############################################################################
|
|
# makeSchedule: Schedule work into interations.
|
|
|
|
# Tensile uses a two-level scheduler. This the first-level, which
|
|
# schedules global reads, global incs, and local writes into iteration.
|
|
# Then makeSubIterSchedule schedules the instructions within the iteration.
|
|
#
|
|
# Inputs:
|
|
# localWriteEndIter: loop iteration where last writes should be inserted
|
|
# If scheduleLocalWrite=0, all writes will be be placed in this iteration.
|
|
# If scheduleLocalWrite=1, the scheduler will work backwards from this
|
|
# iteration.
|
|
#
|
|
# Outputs:
|
|
# self.unrollLoopHeaderCode:
|
|
# - Code module that should be added into the unroll loop header
|
|
# In unscheduled code this contains global loads and global address increment
|
|
# self.perIterGlobalReadCode[], self.perIterLocalWriteCode[]
|
|
# - List indexed by unroll iteration.
|
|
# Each entry in the list is a code module that should be added into that iteration.
|
|
# May be None, indicating no extra code for that iteration
|
|
# self.grEndMfmaIndex
|
|
# self.lwStartMfmaIndex
|
|
# self.lwEndMfmaIndex
|
|
# self.barrierMfmaIndex
|
|
# self.numMfmaForNextLoopLR
|
|
# This routine is responsible for setting the schedule including determining
|
|
# that all necessary dependency are met. The driver code in kernelBody
|
|
# blindly follows the plan set in unrollLoopHeaderCode and perIterCode
|
|
##############################################################################
|
|
def makeSchedule(self, kernel, tensorParametersA, tensorParametersB, localWriteEndIter, uDu=0, skipGlobalReadInc=False, firstIter=False, lastLoop=False, lastLc=False):
|
|
|
|
currentIsa = kernel["ISA"]
|
|
maxVmcnt = globalParameters["AsmCaps"][currentIsa]["MaxVmcnt"]
|
|
|
|
self.unrollLoopHeaderCode = Code.Module()
|
|
# schedule of work for each local_read iteration:
|
|
self.perIterGlobalReadCode = [ Code.Module() for i in range (kernel["LoopIters"]) ]
|
|
self.perIterLocalWriteCode = [ Code.Module() for i in range (kernel["LoopIters"]) ]
|
|
if lastLc:
|
|
self.perIterLocalWriteCodeNGLL = [ Code.Module() for i in range (kernel["LoopIters"]) ]
|
|
self.perIterLocalWriteCanSkip = [ 0 for i in range (kernel["LoopIters"]) ]
|
|
self.perIterGlobalReadCodeDTV = [ Code.Module() for i in range (kernel["LoopIters"]) ] # global read for DirectToVgpr
|
|
assert([item.name for item in self.globalReadIncrements.itemList] == ['globalReadIncrementA', 'globalReadIncrementB'])
|
|
|
|
globalReadIncACode = self.globalReadIncrements.findNamedItem("globalReadIncrementA")
|
|
globalReadIncBCode = self.globalReadIncrements.findNamedItem("globalReadIncrementB")
|
|
|
|
if uDu < kernel["DepthULdsDivisor"] - 1 and kernel.enabledSplitLDS and kernel["PrefetchGlobalRead"] \
|
|
or skipGlobalReadInc:
|
|
globalReadIncACode = Code.Module()
|
|
globalReadIncBCode = Code.Module()
|
|
|
|
grBackup = None
|
|
if uDu != kernel["DepthULdsDivisor"] - 2 and kernel.enabledSplitLDS:
|
|
# hack RAII object for auto restore
|
|
# withhold issuing global read codes until in the 2nd last subloop, meaning we empty the code
|
|
# modules in other subloops.
|
|
grBackup = Backup(self, globalReadACode = self.globalReadACode, globalReadBCode = self.globalReadBCode)
|
|
self.globalReadACode = Code.StructuredModule() # empty
|
|
self.globalReadBCode = Code.StructuredModule() # empty
|
|
|
|
numGlobalReadC = self.getNumberOfLoadCInForLoadC(kernel)
|
|
|
|
lastLoadIter = 0
|
|
# PRECISION for scheduling
|
|
# use 2 times value to make the calculation more accurate
|
|
# using larger PRECISION makes scheduling more accurate but it increses Tensile execution time
|
|
#PRECISION = 100
|
|
PRECISION = 100 * 2
|
|
self.PRECISION = PRECISION
|
|
if kernel["EnableMatrixInstruction"] and kernel["ScheduleIterAlg"] == 3:
|
|
numMfmaPerIter = self.numMfmaPerIter
|
|
#########
|
|
# Get localWriteEnd
|
|
#########
|
|
# assign parameter
|
|
# 1. we calculate number of mfma to prefetch localReads for next loop
|
|
# 2. we put barrier 1 mfma ahead that
|
|
# 3. we put last localWrite 1~2 mfma ahead barrier
|
|
# localReads followed following sequence to be scheduled
|
|
# ds_read[A][0], ds_read[B][0], ds_read[A][1:], ds_read[B][1:]
|
|
# NOTE: we need this sequence for new feature "breaking waitcnt"
|
|
# TODO: breaking waitcnt
|
|
# number of local read for the next loop iteration
|
|
self.numMfmaForLR, latencyForLRCount, latencyLeft = self.countNumMfmaForCurrentOrNextLoopLR(kernel, tensorParametersA, tensorParametersB, curr=False)
|
|
self.numMfmaForNextLoopLR = self.numMfmaForLR + latencyForLRCount
|
|
# final index definition
|
|
self.numMfmaForNextLoopLR = min(self.numMfmaForNextLoopLR,numMfmaPerIter-1)
|
|
self.barrierMfmaIndex = numMfmaPerIter*(kernel["LoopIters"]-self.numItersPLR+1) - self.numMfmaForNextLoopLR - 1 if self.numItersPLR else 0
|
|
numMfmaBetweenLWandBarrier = 2 if kernel["MatrixInstM"] == 32 else 3
|
|
if self.miLatency <= 4 and kernel["LoopIters"] >= 4:
|
|
# low latency MFMA and enough number of loop iteration case, we double numMfmaBetweenLWandBarrier
|
|
numMfmaBetweenLWandBarrier *= 2
|
|
# set and adjust lwEndMfmaIndex
|
|
self.setAndAdjustLwEndMfmaIndex(kernel, tensorParametersA, tensorParametersB, numMfmaBetweenLWandBarrier, lastLoop)
|
|
|
|
#########
|
|
# Internally assign an optimized LWPM value for PGR2
|
|
#########
|
|
# strategy is to distribute LW/GR as wide as possible to avoid hitting vmem FIFO
|
|
# LWPM = (LW_End - LW_Start) / numLW
|
|
# calculate numLocalWriteModPerMfma for all LocalWritePerMfma cases (use this for parameter check in LocalWritePerMfma!=1 case)
|
|
#if kernel["LocalWritePerMfma"] == -1:
|
|
if True:
|
|
#########
|
|
# Get localWriteStart
|
|
#########
|
|
if not (kernel["1LDSBuffer"] or (kernel["DirectToLdsA"] or kernel["DirectToLdsB"])):
|
|
# TODO: replace here for real number of globalReadIncInst
|
|
numGRIncInst = 12 if not kernel["StaggerU"] else 18
|
|
numInstPerMfma = max(roundUp(self.miLatencyLeft/2),1)
|
|
numMfmaToSched = roundUp(numGRIncInst/numInstPerMfma)
|
|
lwStartMfmaIndex = 1 + numMfmaToSched
|
|
latencyForLRCount = 0
|
|
else:
|
|
# for 1LDSB or DTL, we have to issue localwrites after localreads
|
|
numMfmaForLRCurr, latencyForLRCount, latencyLeft = self.countNumMfmaForCurrentOrNextLoopLR(kernel, tensorParametersA, tensorParametersB)
|
|
if self.numVgprBuffer == kernel["LoopIters"]:
|
|
if ((not kernel["DirectToVgprA"]) and self.numReadPerVectorA > 1 or (not kernel["DirectToVgprB"]) and self.numReadPerVectorB > 1) and \
|
|
(not kernel["VgprForLocalReadPacking"]):
|
|
# no VgprForLocalReadPacking only
|
|
# fp16 or bf16, we read 1 element to vgprBuffer the other element to tempVgpr.
|
|
# since each iteration shares same tempVgpr, only read-to-vgprBuffer can
|
|
# be scheduled in the front of loop.
|
|
# localwrite have to start after last read-to-tempVgpr.
|
|
numHalfReads = (self.numReadPerVectorA//2)*kernel["InnerUnroll"]*kernel["MIWaveTileA"] + (self.numReadPerVectorB//2)*kernel["InnerUnroll"]*kernel["MIWaveTileB"]
|
|
numMfmaForHalfRead = 1
|
|
latencyLeft = self.miLatencyLeft
|
|
sub2 = min(2, self.miLatencyLeft) # value for subtraction should not exceed self.miLatencyLeft
|
|
for i in range(numHalfReads):
|
|
latencyLeft -= sub2
|
|
if latencyLeft < 0:
|
|
numMfmaForHalfRead += 1
|
|
latencyLeft = self.miLatencyLeft - sub2
|
|
lwStartMfmaIndex = numMfmaPerIter * (kernel["LoopIters"] - 1 - self.numItersPLR) + numMfmaForHalfRead
|
|
else:
|
|
# we have enough vgprBuffer to schedule localReads in the front of loop
|
|
lwStartMfmaIndex = numMfmaForLRCurr
|
|
else:
|
|
lwStartMfmaIndex = numMfmaPerIter * (kernel["LoopIters"] - 1 - self.numItersPLR) + self.numMfmaForLR
|
|
# add latency count for local read (applicable for all one buffer scheduling cases)
|
|
lwStartMfmaIndex += latencyForLRCount
|
|
#########
|
|
# Get LocalWritePerMfma
|
|
#########
|
|
if lwStartMfmaIndex > self.lwEndMfmaIndex:
|
|
lwStartMfmaIndex = self.lwEndMfmaIndex
|
|
numMfmaCanSched = self.lwEndMfmaIndex - lwStartMfmaIndex + 1
|
|
numLoadsA = kernel["DepthU"]*kernel["MacroTileA"]//int(kernel["GlobalLoadVectorWidthA"]*kernel["NumThreads"])
|
|
numLoadsB = kernel["DepthU"]*kernel["MacroTileB"]//int(kernel["GlobalLoadVectorWidthB"]*kernel["NumThreads"])
|
|
# PGR2 + DirectToVgpr case, no need to schedule local write. Change numLoads to 0
|
|
if kernel["PrefetchGlobalRead"] == 2:
|
|
if kernel["DirectToVgprA"]:
|
|
numLoadsA = 0
|
|
if kernel["DirectToVgprB"]:
|
|
numLoadsB = 0
|
|
writesToSched = (numLoadsA + numLoadsB - 1) * PRECISION
|
|
# In StoreCInUnroll case, add StoreC code related code to writesToSched
|
|
if kernel["StoreCInUnroll"]:
|
|
numStoreCUnrollCode = len(list(self.StoreCUnrollCode.items()))
|
|
writesToSched += numStoreCUnrollCode * PRECISION
|
|
oldValue = 0
|
|
newValue = PRECISION
|
|
loop = 0
|
|
# 1. number of padded writesToSched is (numWrites - 1) * 100 + 1
|
|
# LW ---99--- LW ---99--- LW
|
|
# 2. we need to pad it to multiple of LWPM
|
|
# LW ---99--- LW ---99--- LW --?--
|
|
# | ------- multiple of LWPM ---- |
|
|
# 3. if LWPM is not multiple of 100, we need extra empty instructions to schedule GR for PGR2
|
|
# LW ---99--- LW ---99--- LW --?-- --?--
|
|
# | ------- multiple of LWPM ---- |-LWPM-|
|
|
# 4. then we put GR into padded writesToSched
|
|
# put GR after LW + LWPM of empty inst, so that we can offset GR 1 mfma with LW if possible
|
|
# Ex. LWPM = 0.25
|
|
# LW --24- GR ------74------ LW --24- GR ------74------ LW --24- GR --24-
|
|
# mfma--24-mfma--24-mfma--24-mfma--24-mfma--24-mfma--24-mfma--24-mfma--24-mfma
|
|
# we need LWPM to get precise LWPM
|
|
# so we iterate formula 10 times to get LWPM
|
|
while oldValue != newValue and loop < 10:
|
|
loop += 1
|
|
oldValue = newValue
|
|
newValue = roundUp((writesToSched+1 + (oldValue - (writesToSched+1) % oldValue) + oldValue%PRECISION) / numMfmaCanSched)
|
|
newValue = max(1, newValue) # minimum 1 to avoid 0 division
|
|
numLocalWriteModPerMfma = newValue
|
|
|
|
#####
|
|
# Assign GRPM and LWPM
|
|
#####
|
|
# HOW THIS WORK
|
|
# padding each globalReadInstruction to 100 with empty instruction,
|
|
# each mfma will schedule intructions GRPM*100 times from padded globalReadInstruction.
|
|
# Ex. GRPM = 0.5
|
|
# GR ---------99--------- GR --------99---------- GR
|
|
# mfma --49-- mfma --49-- mfma --49-- mfma --49-- mfma --49--
|
|
self.numGlobalReadInsPerMfma = roundUp(kernel["GlobalReadPerMfma"]*PRECISION)
|
|
|
|
# HOW THIS WORK
|
|
# padding each globalReadInstruction to 100 with empty instruction,
|
|
# each mfma will schedule intructions GRPM*100 times from padded globalReadInstruction.
|
|
# Ex. LWPM = 0.5
|
|
# LW ---------99--------- LW --------99---------- LW
|
|
# mfma --49-- mfma --49-- mfma --49-- mfma --49-- mfma --49--
|
|
# calculate numLocalWriteModPerMfma for all LocalWritePerMfma cases (use this for parameter check in LocalWritePerMfma!=1 case)
|
|
if kernel["PrefetchGlobalRead"] == 1:
|
|
# In PGR1:
|
|
# Larger LWPM can provide more latency to hide global read
|
|
# However, larger LWPM may cause mfma bubbles
|
|
# we set LWPM to 1 unless it requires larger LWPM to enable 1LDSB
|
|
if kernel["1LDSBuffer"]:
|
|
self.numLocalWriteModPerMfma = max(numLocalWriteModPerMfma,PRECISION)
|
|
else:
|
|
self.numLocalWriteModPerMfma = PRECISION
|
|
else:
|
|
self.numLocalWriteModPerMfma = numLocalWriteModPerMfma
|
|
if kernel["EnableMatrixInstruction"] and kernel["ScheduleIterAlg"] == 3 and kernel["PrefetchGlobalRead"] == 2:
|
|
if kernel["LocalWritePerMfma"] != -1:
|
|
valueFromParam = roundUp(kernel["LocalWritePerMfma"]*PRECISION)
|
|
# parameter check (to avoid mismatch due to overwrapping)
|
|
if (valueFromParam < self.numLocalWriteModPerMfma):
|
|
tPrint(3, "LocalWritePerMfma (%f) is too small. Auto-adjusted." % kernel["LocalWritePerMfma"])
|
|
valueFromParam = self.numLocalWriteModPerMfma
|
|
self.numLocalWriteModPerMfma = valueFromParam
|
|
elif (kernel["DirectToVgprA"] or kernel["DirectToVgprB"]):
|
|
# DTV + PGR2 case, DTV load is not a part of LocalWrite scheduing and NumLocalWritePerMfma can be very small
|
|
# we set minimum value of NumLocalWritePerMfma for DTV + PGR2
|
|
# currently, minimum is set as 1/6 (means 1 local write per 6 MFMA)
|
|
minNumLocalWritePerMfma = int((1/6) * PRECISION)
|
|
self.numLocalWriteModPerMfma = max(self.numLocalWriteModPerMfma, minNumLocalWritePerMfma)
|
|
|
|
|
|
##################################
|
|
numGlobalReadInsPerIter = numMfmaPerIter * self.numGlobalReadInsPerMfma
|
|
numLocalWriteModPerIter = numMfmaPerIter * self.numLocalWriteModPerMfma
|
|
# if numGlobalReadInsPerMfma>1, we still want to schedule only 1 GlobalReadIncCode per mfma
|
|
# inserting empty CodeModule so that generator will schedule 1 GlobalReadIncCode 1 empty CodeModule if numGlobalReadInsPerMfma=2
|
|
numEmptyGlobalReadIncCode = self.numGlobalReadInsPerMfma - 1
|
|
|
|
# If numLocalWriteModPerMfma is not multiple of 100,
|
|
# last globalread will be scheduled at lwEndMfmaIndex,
|
|
# and last localwrite will be scheduled at lwEndMfmaIndex - 1
|
|
# so we offset lwEndMfmaIndex by 1 mfma
|
|
if kernel["PrefetchGlobalRead"] == 2 and self.numLocalWriteModPerMfma % PRECISION != 0:
|
|
numMfmaBetweenLWandBarrier -= 1
|
|
def assignParamSplitLds(numMfmaBetweenLWandBarrier):
|
|
if not kernel.enabledSplitLDS:
|
|
return numMfmaBetweenLWandBarrier
|
|
# how many local reads in terms of mfma indices (height)
|
|
# total number of instructions (total) minus the instructions prefetched outside of loop (spent), divided by mfma bubble (width)
|
|
issueLatency = max(self.localReadInstructionA.IssueLatency, self.localReadInstructionB.IssueLatency) * 2
|
|
width = self.miLatencyLeft // issueLatency
|
|
width = max(width, 1)
|
|
spent = self.numItersPLR * (self.numReadsPerIterA + self.numReadsPerIterB)
|
|
total = kernel["LoopIters"]//self.numIterPerCoalescedReadA*self.numReadsPerIterA + \
|
|
kernel["LoopIters"]//self.numIterPerCoalescedReadB*self.numReadsPerIterB
|
|
height = int(ceil((total-spent)/width))
|
|
# how many local writes
|
|
localWritesToSched = self.localWriteACode.countType(Code.LocalWriteInst) + \
|
|
self.localWriteBCode.countType(Code.LocalWriteInst)
|
|
if kernel["StoreCInUnroll"]:
|
|
# in StoreCInUnroll case, add number of storeC related code here
|
|
# add store C related code to itemsLWToSched
|
|
numStoreCUnrollCode = len(list(self.StoreCUnrollCode.items()))
|
|
localWritesToSched += numStoreCUnrollCode
|
|
localWritesPerMfma = self.numLocalWriteModPerMfma / PRECISION # was scaled by PRECISION
|
|
# _numMfmaBetweenLastLWandBarrier: a function of 'spacing', which is num of mfma instructions until local write starts
|
|
_numMfmaBetweenLastLWandBarrier = lambda spacing : self.barrierMfmaIndex + 1 - ceil(localWritesToSched/localWritesPerMfma) - spacing
|
|
addrIncToSched = sum(1 for codemod in [globalReadIncACode, globalReadIncBCode] if len(codemod.items()))
|
|
if uDu < kernel["DepthULdsDivisor"] - 1:
|
|
if kernel["1LDSBuffer"] and kernel["PrefetchLocalRead"] > 1:
|
|
# space the stream of local writes so that 1st local write is scheduled after last local read,
|
|
# but give it 2 mfma's worth of headroom
|
|
spacing = 2 + height
|
|
else:
|
|
# can start ds_write/buffer_load as soon as loop starts, but give it 1 mfma's worth of headroom
|
|
spacing = 1
|
|
else:
|
|
# query how much spacing we have by calling lambda(0), minus the original 'numMfmaBetweenLWandBarrier'
|
|
# we get the spacing that results in exactly 'numMfmaBetweenLWandBarrier' between last write and barrier
|
|
spacing = _numMfmaBetweenLastLWandBarrier(0) - numMfmaBetweenLWandBarrier + addrIncToSched - 1
|
|
return max(0, _numMfmaBetweenLastLWandBarrier(spacing))
|
|
|
|
numMfmaBetweenLWandBarrier = assignParamSplitLds(numMfmaBetweenLWandBarrier)
|
|
# In StoreCInUnroll + num of store > 1 case, reduce numMfmaBetweenLWandBarrier to 1
|
|
# because interval between local write and read is already added by StoreCInUnroll code
|
|
if kernel["StoreCInUnroll"] and self.getNumberOfStoreCInTemplate(kernel) > 1:
|
|
numMfmaBetweenLWandBarrier = min(numMfmaBetweenLWandBarrier, 1)
|
|
|
|
# set and adjust lwEndMfmaIndex
|
|
self.setAndAdjustLwEndMfmaIndex(kernel, tensorParametersA, tensorParametersB, numMfmaBetweenLWandBarrier, lastLoop)
|
|
|
|
localWriteEndIter = self.lwEndMfmaIndex//numMfmaPerIter
|
|
localWriteEndIter = min(kernel["LoopIters"] - 1, localWriteEndIter)
|
|
assert localWriteEndIter < kernel["LoopIters"]
|
|
assert self.lwEndMfmaIndex < numMfmaPerIter*kernel["LoopIters"]
|
|
else:
|
|
numGlobalReadInsPerIter = roundUp(kernel["GlobalReadPerMfma"] * PRECISION) if kernel["GlobalReadPerMfma"] > 0 else PRECISION
|
|
numLocalWriteModPerIter = roundUp(kernel["LocalWritePerMfma"] * PRECISION) if kernel["LocalWritePerMfma"] > 0 else PRECISION
|
|
numEmptyGlobalReadIncCode = numGlobalReadInsPerIter - 1
|
|
|
|
numLocalWritesPerSched = numLocalWriteModPerIter if kernel["ScheduleIterAlg"] != 3 else self.numLocalWriteModPerMfma
|
|
|
|
if not self.scheduleGlobalRead:
|
|
# put everything in the header:
|
|
self.unrollLoopHeaderCode.addCode(self.dtlsM0UpdateACode)
|
|
self.unrollLoopHeaderCode.addCode(self.globalReadACode)
|
|
self.unrollLoopHeaderCode.addCode(self.dtlsM0UpdateBCode)
|
|
self.unrollLoopHeaderCode.addCode(self.globalReadBCode)
|
|
self.unrollLoopHeaderCode.addCode(globalReadIncACode)
|
|
self.unrollLoopHeaderCode.addCode(globalReadIncBCode)
|
|
if kernel["EnableMatrixInstruction"] and kernel["ScheduleIterAlg"] == 3:
|
|
self.grEndMfmaIndex = 0
|
|
itemsGRToSchedLater = []
|
|
else:
|
|
self.unrollLoopHeaderCode.addCode(self.globalReadACode.header)
|
|
self.unrollLoopHeaderCode.addCode(self.globalReadBCode.header)
|
|
|
|
# Add all loads from middle as individual schedulable items
|
|
# when using PGR2, put global read instruction right after corresponding localWrite instruction
|
|
if kernel["PrefetchGlobalRead"] == 2 or kernel.enabledSplitLDS:
|
|
itemsGRToSched = []
|
|
itemsGRToSchedLater = list(self.globalReadACode.middle.items()) + \
|
|
list(self.globalReadBCode.middle.items())
|
|
itemsGRToSchedLaterDTV = []
|
|
# PGR2 and DirectToVgpr case, schedule global read for DirectToVgpr separately after registers are used for mfma
|
|
if kernel["EnableMatrixInstruction"]:
|
|
if kernel["DirectToVgprA"] or kernel["DirectToVgprB"]:
|
|
itemsGRToSchedLater = list(self.globalReadACode.middle.items()) # not DirectToVgpr (A has non-DirectToVgpr load)
|
|
itemsGRToSchedLaterDTV = list(self.globalReadBCode.middle.items()) # DirectToVgpr (B has DirectToVgpr load)
|
|
# add to self.perIterGlobalReadCodeDTV to schedule DirectToVgpr
|
|
while itemsGRToSchedLaterDTV:
|
|
itemGR = itemsGRToSchedLaterDTV.pop(0)
|
|
self.perIterGlobalReadCodeDTV[kernel["LoopIters"] - 1].addCode(itemGR)
|
|
if kernel.enabledSetPrioSplitLDS and itemsGRToSchedLater:
|
|
itemsGRToSchedLater.insert(1, Code.Inst("s_setprio", "3", "top priority for load"))
|
|
itemsGRToSchedLater.insert(len(itemsGRToSchedLater), Code.Inst("s_setprio", "0", ""))
|
|
else:
|
|
itemsGRToSched = list(self.globalReadACode.middle.items()) + \
|
|
list(self.globalReadBCode.middle.items())
|
|
itemsGRToSchedLater = []
|
|
if kernel["StoreCInUnroll"]:
|
|
# in StoreCInUnroll case, add loadC code here (self.LoadCTemplate is empty for no loadC required case)
|
|
# The location to insert LoadC is decided based on DirectToLds and DirectToVgpr setting
|
|
# 1) No Lds write case (Both DirectToLds or DirectToVgpr enabled), insert Load C before Load A and B
|
|
if kernel["NoLdsWriteCode"]:
|
|
itemsGRToSched = list(list(self.LoadCUnrollCode.itemList) + self.globalReadACode.middle.items()) +\
|
|
list(self.globalReadBCode.middle.items())
|
|
# 2) DirectToVgpr only enabled case, insert Load C before Load for DirectToVgpr
|
|
elif kernel["DirectToVgprA"] or kernel["DirectToVgprB"]:
|
|
itemsGRToSched = list(self.globalReadACode.middle.items()) + list(self.LoadCUnrollCode.itemList) +\
|
|
list(self.globalReadBCode.middle.items())
|
|
# 3) no DirectToVgpr/Lds case, insert Load C after Load A,B
|
|
else:
|
|
itemsGRToSched += list(self.LoadCUnrollCode.itemList)
|
|
|
|
itemsGRToSchedTemp = []
|
|
for i in range(len(itemsGRToSched)):
|
|
itemsGRToSchedTemp.append(itemsGRToSched.pop(0))
|
|
for j in range(PRECISION-1):
|
|
itemsGRToSchedTemp.append(Code.Module())
|
|
itemsGRToSched = itemsGRToSchedTemp
|
|
|
|
itemsGRIncToSched = []
|
|
if kernel["EnableMatrixInstruction"] and kernel["ScheduleIterAlg"] == 3:
|
|
# for SIA3, we can break GlobalReadIncCode to avoid mfma bubbles
|
|
if kernel["PrefetchGlobalRead"] == 2:
|
|
# skip to schedule global read for PGR2 first mfma
|
|
for i in range(numEmptyGlobalReadIncCode+1):
|
|
imod = Code.Module()
|
|
itemsGRIncToSched.append(imod)
|
|
numInst = globalReadIncACode.countType(Code.Inst) + globalReadIncBCode.countType(Code.Inst)
|
|
numInstPerMfma = max(roundUp(self.miLatencyLeft/2),1)
|
|
|
|
globalReadInc1 = globalReadIncACode.flatitems()
|
|
globalReadInc2 = globalReadIncBCode.flatitems()
|
|
if self.isSwapGlobalReadOrderForDtvOrDtl(kernel):
|
|
# swap the order of readInc for DTV
|
|
globalReadInc1, globalReadInc2 = globalReadInc2, globalReadInc1
|
|
globalReadIncItems = globalReadInc1 + globalReadInc2
|
|
if kernel["StoreCInUnroll"] and kernel["PrefetchGlobalRead"] == 2:
|
|
# PGR=2 + StoreCInUnroll case, add first LoadC after IncA, second LoadC (if exist) after IncB
|
|
tmpList = list(self.LoadCUnrollCode.itemList)
|
|
dummyList = [ Code.Module() for i in range (numInstPerMfma - 1) ]
|
|
if len(tmpList) > 0:
|
|
# first LoadC
|
|
globalReadIncItems = globalReadInc1 + tmpList[0:1] + dummyList + globalReadInc2
|
|
if len(tmpList) > 1:
|
|
# second LoadC
|
|
globalReadIncItems += tmpList[1:]
|
|
# add len(LoadCUnrollCode.itemList) to numInst
|
|
numInst += len(tmpList)
|
|
numMfmaToSched = roundUp(numInst/numInstPerMfma)
|
|
for j in range(numMfmaToSched):
|
|
imod = Code.Module()
|
|
count = 0
|
|
while globalReadIncItems and count < numInstPerMfma:
|
|
tempInst = globalReadIncItems.pop(0)
|
|
imod.addCode(tempInst)
|
|
if tempInst.countType(Code.Inst):
|
|
count += 1
|
|
itemsGRIncToSched.append(imod)
|
|
for i in range(numEmptyGlobalReadIncCode):
|
|
imod = Code.Module()
|
|
itemsGRIncToSched.append(imod)
|
|
else:
|
|
itemsGRIncToSched.append(globalReadIncACode)
|
|
for i in range(numEmptyGlobalReadIncCode):
|
|
imod = Code.Module()
|
|
itemsGRIncToSched.append(imod)
|
|
itemsGRIncToSched.append(globalReadIncBCode)
|
|
for i in range(numEmptyGlobalReadIncCode):
|
|
imod = Code.Module()
|
|
itemsGRIncToSched.append(imod)
|
|
|
|
if kernel["EnableMatrixInstruction"] and kernel["ScheduleIterAlg"] == 3:
|
|
# Loop in PGR1: GlobalRead -> GlobalReadInc -> LocalWrite
|
|
# but GlobalReadInc shouldn't block LocalWrite so we count them out
|
|
# Loop in PGR2: GlobalReadInc -> LocalWrite/GlobalRead pair
|
|
# since LocalWrite/GlobalRead pair depends on GlobalReadInc, we count in only GlobalReadInc
|
|
if kernel["PrefetchGlobalRead"] == 2:
|
|
loadsToSched = len(itemsGRIncToSched)
|
|
else:
|
|
loadsToSched = len(itemsGRToSched)
|
|
|
|
# Here is to adjust scheduling silently in order to have validation pass.
|
|
# Better way is to use larger globalReadPerMfma.
|
|
## schedule more instructions at first iteration if no enough mfma to schedule globalRead
|
|
self.grEndMfmaIndex = max(0, roundUp(loadsToSched/self.numGlobalReadInsPerMfma) - 1)
|
|
if self.grEndMfmaIndex > self.lwEndMfmaIndex:
|
|
schedNumForIter0 = numGlobalReadInsPerIter + (self.grEndMfmaIndex - self.lwEndMfmaIndex) * self.numGlobalReadInsPerMfma
|
|
self.grEndMfmaIndex = self.lwEndMfmaIndex
|
|
else:
|
|
schedNumForIter0 = numGlobalReadInsPerIter
|
|
if kernel["PrefetchGlobalRead"] == 1:
|
|
globalReadIncEndMfmaIndex = self.grEndMfmaIndex + roundUp(len(itemsGRIncToSched)/self.numGlobalReadInsPerMfma)
|
|
endIter = roundUp((globalReadIncEndMfmaIndex+1)/numMfmaPerIter)
|
|
else:
|
|
endIter = roundUp((self.grEndMfmaIndex+1)/numMfmaPerIter)
|
|
## schedule more instructions at first iteration if no enough mfma to schedule globalRead + globalReadInc
|
|
if endIter > kernel["LoopIters"]:
|
|
endIter = kernel["LoopIters"]
|
|
if kernel["PrefetchGlobalRead"] == 1:
|
|
schedNumForIter0 += (globalReadIncEndMfmaIndex+1 - kernel["LoopIters"]*numMfmaPerIter) * self.numGlobalReadInsPerMfma
|
|
|
|
# SIA 1 or 2
|
|
# distribute the instructions in itemsGRToSched evenly as possible to iterations: perIterGlobalReadCode[0,endIter)
|
|
# last one is perIterGlobalReadCode[endIter-1],
|
|
# Ideally: endIter <= localWriteEndIter,
|
|
# then put M0 updateCode (if any) and first 'schedNumForIter0' GR-inst in perIterGlobalReadCode[0]
|
|
# put every numGlobalReadInsPerIter GR-insts in perIterGlobalReadCode[1]~[endIter-1]
|
|
# corner case: endIter > localWriteEndIter, set endIter = localWriteEndIter,in this case, schedNumForIter0 will > 1
|
|
# and perIterGlobalReadCode[0] would need to schedule more instructions
|
|
else:
|
|
# reads and incs are scheduled in iters range(0..endIter)
|
|
endIter = roundUp((len(itemsGRToSched) + len(itemsGRIncToSched)) / numGlobalReadInsPerIter)
|
|
# FIXME:
|
|
# above formula precisely count number of GR + GRInc
|
|
# however it has regression issue with tuned yaml with default GRPM.
|
|
# below formula follows old logic to add 2 to the instruction count, so it may has larger schedNumForIter0
|
|
# we should use above formula with GRPM tuning for better performance
|
|
# NOTE: both formula pass validation test
|
|
endIter = roundUp((len(itemsGRToSched) + len(itemsGRIncToSched) + 2*PRECISION) / numGlobalReadInsPerIter)
|
|
if endIter > localWriteEndIter:
|
|
# Front-load some of the buffer loads if we don't have enough loop iters:
|
|
# could use a different/smarter algorithm to space out the loads?
|
|
schedNumForIter0 = (endIter-(localWriteEndIter) + 1) * numGlobalReadInsPerIter
|
|
endIter = localWriteEndIter
|
|
else:
|
|
# schedule b2b for readCnt > 2 (True for bigger TT)
|
|
schedNumForIter0 = numGlobalReadInsPerIter
|
|
|
|
# insert dtlsM0UpdateACode dtlsM0UpdateBCode code
|
|
if self.globalReadACode.middle.items():
|
|
self.globalReadACode.middle.items()[0].items().insert(0,self.dtlsM0UpdateACode)
|
|
if self.globalReadBCode.middle.items():
|
|
self.globalReadBCode.middle.items()[0].items().insert(0,self.dtlsM0UpdateBCode)
|
|
|
|
itemsGRToSched.extend(itemsGRIncToSched)
|
|
# append 'n' global load at a time
|
|
# append global load(S) first 'number of global load(s)' determined by schedNumForIter0
|
|
for item in itemsGRToSched[:schedNumForIter0]:
|
|
self.perIterGlobalReadCode[0].addCode(item)
|
|
itemsGRToSched = itemsGRToSched[schedNumForIter0:] # trim the scheduled GRs, do the rest in the following loop
|
|
|
|
for u in range(1, endIter):
|
|
# append itemPerIter GR for each iteration,
|
|
# and trim the scheduled ones at the end of loop
|
|
itemPerIter = 1 * numGlobalReadInsPerIter
|
|
try:
|
|
for item in itemsGRToSched[:itemPerIter]:
|
|
self.perIterGlobalReadCode[u].addCode(item)
|
|
lastLoadIter = u
|
|
itemsGRToSched = itemsGRToSched[itemPerIter:]
|
|
except IndexError:
|
|
break # itemsGRToSched is 0-length, no code left to schedule
|
|
|
|
assert not itemsGRToSched # should have scheduled everything already, itemsGRToSched should be empty
|
|
|
|
# adjustment for StoreCInUnroll
|
|
# lastLoop case, make the last perIterGlobalReadCode[] (LoopIters-1) empty
|
|
# otherwise, mixing global read inc code and StoreCInUnroll post code could cause memory access issue
|
|
if kernel["StoreCInUnroll"] and lastLoop:
|
|
lastIter = kernel["LoopIters"] - 1
|
|
prevLastIter = max(0, lastIter - 1)
|
|
if prevLastIter < lastIter:
|
|
while self.perIterGlobalReadCode[lastIter].items():
|
|
self.perIterGlobalReadCode[prevLastIter].addCode(self.perIterGlobalReadCode[lastIter].items().pop(0))
|
|
|
|
self.perIterGlobalReadCode[endIter-1].addCode(self.globalReadACode.footer)
|
|
self.perIterGlobalReadCode[endIter-1].addCode(self.globalReadBCode.footer)
|
|
|
|
# Now schedule the writes:
|
|
if not self.scheduleLocalWrite:
|
|
# if no scheduleLocalWrite - just add writes to localWritelocalWriteEndIter
|
|
# If PGR=0, writes have to be done immediately following the loads - no opportunity to schedule
|
|
# so don't add to schedule, these will be added separately and before the first iter
|
|
if kernel["PrefetchGlobalRead"]:
|
|
# do we need a module here? That would prevent these from being scheduled
|
|
imod = self.perIterLocalWriteCode[localWriteEndIter].addCode(Code.Module())
|
|
if self.enable["Wait"]:
|
|
imod.addCode(
|
|
self.wait(kernel, tensorParametersA, tensorParametersB, 0, -1, -1, \
|
|
"1wait for global read"))
|
|
imod.addComment1("local write A")
|
|
imod.addCode(self.localWriteACode)
|
|
imod.addComment1("local write B")
|
|
imod.addCode(self.localWriteBCode)
|
|
if kernel["EnableMatrixInstruction"] and kernel["ScheduleIterAlg"] == 3:
|
|
self.lwStartMfmaIndex = self.lwEndMfmaIndex
|
|
else:
|
|
#################
|
|
# create a plan #
|
|
#################
|
|
itemsLWToSched = list(self.localWriteACode.items()) + list(self.localWriteBCode.items())
|
|
if kernel["PrefetchGlobalRead"] == 2:
|
|
# PrefetchGlobalRead + DirectToLds case, need to add dummy list to insert global read
|
|
tmpList = []
|
|
numDummy = 0
|
|
lenA = len(list(self.globalReadACode.middle.items()))
|
|
lenB = len(list(self.globalReadBCode.middle.items()))
|
|
if self.isSwapGlobalReadOrderForDtvOrDtl(kernel):
|
|
# swap A and B (SwapGlobalReadOrder case, the actual content is swapped (B is in globalReadACode). Need adjustment)
|
|
lenA, lenB = lenB, lenA
|
|
if kernel["DirectToLdsA"]:
|
|
if not kernel["StoreCInUnroll"]:
|
|
# PGR2 + DTL (and not SCIU) case, footer code is added in middle. Need to subtract 1 (for footer inst)
|
|
lenA -= 1
|
|
numDummy += lenA
|
|
if kernel["DirectToLdsB"]:
|
|
if not kernel["StoreCInUnroll"]:
|
|
# PGR2 + DTL (and not SCIU) case, footer code is added in middle. Need to subtract 1 (for footer inst)
|
|
lenB -= 1
|
|
numDummy += lenB
|
|
for i in range(numDummy):
|
|
tmpList.append(Code.Module())
|
|
# add dummy at the top of the list
|
|
itemsLWToSched = tmpList + itemsLWToSched
|
|
if kernel["StoreCInUnroll"]:
|
|
# in StoreCInUnroll case, add storeC related code here
|
|
# add store C related code to itemsLWToSched
|
|
tmpList = list(self.StoreCUnrollCode.items())
|
|
itemsLWToSched += tmpList
|
|
# extend localWrite by inserting empty Module
|
|
itemsLWToSchedTemp = []
|
|
for i in range(len(itemsLWToSched)-1):
|
|
itemsLWToSchedTemp.append(itemsLWToSched.pop(0))
|
|
for j in range(PRECISION-1):
|
|
itemsLWToSchedTemp.append(Code.Module())
|
|
if itemsLWToSched:
|
|
itemsLWToSchedTemp.append(itemsLWToSched.pop(0))
|
|
for i in range(numLocalWritesPerSched + numLocalWritesPerSched % PRECISION - len(itemsLWToSchedTemp) % numLocalWritesPerSched):
|
|
itemsLWToSchedTemp.append(Code.Module())
|
|
itemsLWToSched = itemsLWToSchedTemp
|
|
# This counts the number of modules which contain a ds_write
|
|
# Scheduler below keeps all writes in the same module in same iteration
|
|
# so this is better match to what it is trying to do
|
|
# writesToSched = sum(1 for item in itemsLWToSched if item.countType(Code.LocalWriteInst))
|
|
writesToSched = len(itemsLWToSched)
|
|
# assign schedule index
|
|
if kernel["EnableMatrixInstruction"] and kernel["ScheduleIterAlg"] == 3:
|
|
self.lwStartMfmaIndex = self.lwEndMfmaIndex - max(1,roundUp(writesToSched/numLocalWritesPerSched)) + 1
|
|
if self.lwStartMfmaIndex < self.grEndMfmaIndex:
|
|
self.lwStartMfmaIndex = self.grEndMfmaIndex
|
|
if kernel["PrefetchGlobalRead"]==2:
|
|
# adjustment for PGR2
|
|
# new lwStartMfmaIndex value calculated with numLocalWritesPerSched should not be smaller than pre-calculated lwStartMfmaIndex
|
|
# (which can cause overwrapping)
|
|
if self.lwStartMfmaIndex < lwStartMfmaIndex:
|
|
self.lwStartMfmaIndex = lwStartMfmaIndex
|
|
# after adjusting lwStartMfmaIndex, lwStartMfmaIndex should not be larger than lwEndMfmaIndex
|
|
if self.lwStartMfmaIndex > self.lwEndMfmaIndex:
|
|
self.lwStartMfmaIndex = self.lwEndMfmaIndex
|
|
startIter = self.lwStartMfmaIndex//numMfmaPerIter
|
|
assert startIter < localWriteEndIter+1 # startIter should be at or before the endIter
|
|
else:
|
|
startIter = localWriteEndIter - roundUp(writesToSched/numLocalWritesPerSched) + 1
|
|
# - can't move a write past the load it depends on
|
|
# as a simplification, don't move writes past any loads
|
|
if startIter < lastLoadIter:
|
|
startIter = lastLoadIter
|
|
|
|
readsToWait = len(list(self.localWriteACode.items())) + len(list(self.localWriteBCode.items()))
|
|
readsToWaitDTV = 0
|
|
# add waitcnt for DirectToVgpr. Delaying wait for DirectToVgpr global read
|
|
if kernel["DirectToVgprA"] or kernel["DirectToVgprB"]:
|
|
# DirectToVgpr + swapGlobalRead case, actual DTV load is in self.globalReadBCode (due to swap).
|
|
# Need to check self.globalReadBCode
|
|
readsToWaitDTV += len(list(self.globalReadBCode.middle.items()))
|
|
# add waitcnt for StoreCInUnroll. Delaying wait for Load C
|
|
readsToWait += numGlobalReadC
|
|
|
|
readsToWaitNGLL = readsToWait
|
|
|
|
localwriteCnt = 0
|
|
for u in range(startIter, localWriteEndIter+1):
|
|
if u==(localWriteEndIter):
|
|
itemPerIter = len(itemsLWToSched) # schedule all remaining activity
|
|
else:
|
|
itemPerIter = numLocalWriteModPerIter
|
|
# if localwrite is not multiple of numLocalWriteModPerIter, fill last iteration first.
|
|
# make sure numLocalWriteModPerIter is enough to schedule localwrite
|
|
# TODO: if numLocalWriteModPerIter is not enough to schedule localwrite, need smarter way to distribute localWrite
|
|
if u == startIter and kernel["ScheduleIterAlg"] == 3:
|
|
itemPerIter = numLocalWriteModPerIter - (self.lwStartMfmaIndex % numMfmaPerIter) * numLocalWritesPerSched
|
|
|
|
for item in itemsLWToSched[:itemPerIter]:
|
|
# Use a module to ensure these pieces stay together in the sub-iter scheduler
|
|
imod = Code.Module("LocalWriteMod%u"%u)
|
|
imodNGLL = Code.Module("LocalWriteMod%u"%u)
|
|
writesPerItem = item.countType(Code.LocalWriteInst)
|
|
readsToWaitAdjustForStoreC = 0
|
|
if kernel["StoreCInUnroll"] and not firstIter and kernel["PrefetchGlobalRead"]==2:
|
|
# get number of StoreC in template
|
|
readsToWaitAdjustForStoreC += self.getNumberOfStoreCInTemplate(kernel)
|
|
if writesPerItem:
|
|
imod.addComment0("sched write - iter %u writesPerItem=%u"%(u,writesPerItem))
|
|
imodNGLL.addComment0("sched write - iter %u writesPerItem=%u"%(u,writesPerItem))
|
|
# if writesPerItem>1 this indicates multiple LocalWrites in the same module
|
|
# this happens in some transpose cases. Here the first write needs to wait
|
|
# for the associated global read to finish, then the remaining writes can flow
|
|
# TODO - can schedule these writes across iters, should figure this out above
|
|
readsToWait = readsToWait - 1
|
|
readsToWaitNGLL = readsToWaitNGLL - 1
|
|
if uDu < kernel["DepthULdsDivisor"]-1:
|
|
imod.addComment0("no wait vmcnt except for in the last subLdsLoop")
|
|
else:
|
|
imod.addCode(Code.WaitCnt(self.version, -1, min(maxVmcnt, readsToWait + readsToWaitDTV + readsToWaitAdjustForStoreC), \
|
|
"wait for global read before writing to local"))
|
|
imodNGLL.addCode(Code.WaitCnt(self.version, -1, min(maxVmcnt, readsToWaitNGLL + readsToWaitDTV + readsToWaitAdjustForStoreC), \
|
|
"wait for global read before writing to local"))
|
|
if kernel["StoreCInUnroll"] or kernel["PrefetchGlobalRead"]==2:
|
|
if "s_waitcnt" in str(item) and "__placeholder__" in str(item):
|
|
# waitcnt adjustment for StoreCInUnroll
|
|
readsToWaitAdjust = readsToWait + readsToWaitDTV - numGlobalReadC
|
|
if kernel["PrefetchGlobalRead"]==2:
|
|
# PGR=2 special cases
|
|
if (kernel["AtomicAddC"] or not kernel["ProblemType"]["UseBeta"]):
|
|
# no Load C case
|
|
if not firstIter:
|
|
# PGR=2 and not firstIter case, __placeholder__ includes num of storeC from previous Iter
|
|
readsToWaitAdjust += readsToWaitAdjustForStoreC
|
|
else:
|
|
# Load C case
|
|
# adjustment for waitcnt for loadC
|
|
if kernel["StoreCInUnroll"] and self.StoreCUnrollLoadCWaitComment in str(item):
|
|
# readsToWaitDTV should not be added for loadC waitcnt
|
|
readsToWaitAdjust -= readsToWaitDTV
|
|
if kernel["NoLdsWriteCode"] and kernel["PrefetchGlobalRead"]!=2:
|
|
# DirectToLds or DirectToVgpr for both A and B case, use the number of global read for both A and B as vmcnt (only for PGR=1)
|
|
readsToWaitAdjust = len(list(self.globalReadACode.middle.items())) + len(list(self.globalReadBCode.middle.items()))
|
|
item = str(item).replace("__placeholder__", str(readsToWaitAdjust))
|
|
|
|
imod.addCode(item)
|
|
# schedule global instruction that need to be scheduled later
|
|
if localwriteCnt % PRECISION == (numLocalWritesPerSched % PRECISION):
|
|
reads = 0
|
|
while itemsGRToSchedLater:
|
|
itemGR = itemsGRToSchedLater[0]
|
|
readsInc = itemGR.countType(Code.GlobalReadInst)
|
|
if kernel["StoreCInUnroll"] and readsInc == 0:
|
|
# adjustment for StoreCInUnroll
|
|
# count buffer_load if it exist but not counted
|
|
readsInc += str(itemGR).count("_buffer_load")
|
|
reads = reads + readsInc
|
|
if reads > 1:
|
|
break
|
|
if "s_waitcnt" in str(itemGR) and "__placeholder__" in str(itemGR):
|
|
itemGR2 = (str(itemGR).replace("__placeholder__", str(readsToWait)))
|
|
imod.addText(itemGR2)
|
|
else:
|
|
imod.addCode(itemGR)
|
|
readsToWait = readsToWait + readsInc # GR instruction increments vmcnt
|
|
itemsGRToSchedLater.pop(0)
|
|
localwriteCnt += 1
|
|
self.perIterLocalWriteCode[u].addCode(imod)
|
|
|
|
imodNGLL.addCode(copy.deepcopy(item))
|
|
if lastLc:
|
|
# local write code for NGLL should be updated at the last lc
|
|
# in init acc opt case, the last inner loop generated is not for the last lc.
|
|
# in that case, local write code for NGLL is not as expected.
|
|
self.perIterLocalWriteCodeNGLL[u].addCode(imodNGLL)
|
|
|
|
itemsLWToSched = itemsLWToSched[itemPerIter:]
|
|
|
|
# should never run out of items to schedule
|
|
assert not itemsLWToSched # should have scheduled everthing already
|
|
|
|
if grBackup is not None:
|
|
del grBackup
|
|
|
|
##############################################################################
|
|
# Schedule work into the each unroll loop iteration
|
|
# localReadCode is the local reads for this loop iteration
|
|
# (returned by localReadDo). The instructions in localReadCode
|
|
# will retain their relative order, but may be interleaved
|
|
# with instructions from otherCode.
|
|
|
|
# globalReadCode is the 'other' buffer loads and addr increments
|
|
# localWriteCode is the 'other' local writes
|
|
# to schedule in with the ds reads. The instructions
|
|
# will retain their relative order, but may be interleaved
|
|
# with instructions from localReadCode.
|
|
|
|
# pointerCode contains local pointer changes (if needed)
|
|
# waitCode contains s_waitcnt before macs.
|
|
# - Cannot be "" or None
|
|
# - may be empty Module if not waiting is desired (perhaps for debug)
|
|
# - may be multiple instructions (ConservativeWaitCnt)
|
|
# - typically is a single Code.WaitCnt. This routine will
|
|
# modify the lgkmcnt to account for any scheduling decisions.
|
|
# If this is not desired, add the waitCnt to pointerCode and
|
|
# set waitCode to an empty module
|
|
# macIterCode contains the mac iters. May be a macro call.
|
|
#
|
|
# returns: a Module with the combined, optimally scheduled
|
|
# localReadCode + otherCode
|
|
##############################################################################
|
|
def makeSubIterSchedule(self, kernel, localReadCode, iteration, pointerLWCode, pointerLRCode, waitCode, macIterCode, \
|
|
waitLWCode = Code.Module(), syncCode = Code.Module(), packCode = Code.Module(), isDTVodd = False, NLLlast = False):
|
|
|
|
iterCode = Code.Module()
|
|
globalReadCode = copy.deepcopy(self.perIterGlobalReadCode[iteration])
|
|
globalReadCodeDTV = self.perIterGlobalReadCodeDTV[iteration]
|
|
origLenGlobalReadCodeDTV = len(list(self.perIterGlobalReadCodeDTV[iteration].items()))
|
|
localWriteCode = self.perIterLocalWriteCode[iteration]
|
|
isBarrier = kernel["LoopIters"] - self.numItersPLR
|
|
hasLocalRead = localReadCode.countType(Code.LocalReadInst)
|
|
# Default schedule is other, local reads, then local writes:
|
|
if self.scheduleIterAlg==0:
|
|
# simple schedule, just add the modules in-order
|
|
iterCode.addCode(globalReadCode)
|
|
iterCode.addCode(globalReadCodeDTV)
|
|
# pop out all items
|
|
while len(list(globalReadCodeDTV.items())):
|
|
globalReadCodeDTV.items().pop(0)
|
|
iterCode.addCode(waitLWCode)
|
|
iterCode.addCode(syncCode)
|
|
iterCode.addCode(localReadCode)
|
|
iterCode.addCode(localWriteCode)
|
|
iterCode.addCode(pointerLWCode)
|
|
iterCode.addCode(pointerLRCode)
|
|
iterCode.addCode(waitCode)
|
|
iterCode.addCode(packCode)
|
|
iterCode.addCode(macIterCode)
|
|
elif self.scheduleIterAlg == 1:
|
|
iterCode.addCode(waitLWCode)
|
|
iterCode.addCode(syncCode)
|
|
#import pdb
|
|
#pdb.set_trace()
|
|
# simple algorithm - do half the reads first:
|
|
readsToSchedule = localReadCode.countType(Code.LocalReadInst) / 2
|
|
#localReadCode.prettyPrint()
|
|
readItems = localReadCode.flatitems()
|
|
while readItems:
|
|
item = readItems.pop(0)
|
|
#print "readsToSchedule=", readsToSchedule, "item=", item
|
|
iterCode.addCode(item)
|
|
readsThisItem = item.countType(Code.LocalReadInst)
|
|
if readsThisItem:
|
|
assert readsThisItem==1, "Scheduler assumes 1 read per item"
|
|
readsToSchedule = readsToSchedule - 1
|
|
if readsToSchedule == 0:
|
|
break
|
|
|
|
iterCode.addCode(globalReadCode)
|
|
iterCode.addCode(globalReadCodeDTV)
|
|
# pop out all items
|
|
while len(list(globalReadCodeDTV.items())):
|
|
globalReadCodeDTV.items().pop(0)
|
|
|
|
# add rest of the reads here
|
|
for item in readItems:
|
|
iterCode.addCode(item)
|
|
|
|
#move down write to be the last
|
|
iterCode.addCode(localWriteCode)
|
|
# tack on the pointer and mac code:
|
|
iterCode.addCode(pointerLWCode)
|
|
iterCode.addCode(pointerLRCode)
|
|
iterCode.addCode(waitCode)
|
|
iterCode.addCode(packCode)
|
|
iterCode.addCode(macIterCode)
|
|
elif self.scheduleIterAlg == 2:
|
|
# SIA2 use only 1 iteration and separate compute and fetch by raising compute priority
|
|
# 2 workgroup interleave, while WG0/WG1 doing compute, WG1/WG0 doing fetch
|
|
# EPS need to be 1, or valu instruction will break interleave
|
|
iterCode.addCode(globalReadCode)
|
|
iterCode.addCode(globalReadCodeDTV)
|
|
# pop out all items
|
|
while len(list(globalReadCodeDTV.items())):
|
|
globalReadCodeDTV.items().pop(0)
|
|
iterCode.addCode(waitLWCode)
|
|
iterCode.addCode(syncCode)
|
|
iterCode.addCode(localReadCode)
|
|
iterCode.addCode(waitCode)
|
|
|
|
# interleave pack code
|
|
# BF16 or FP16: each packCode is for one 32-bit reg, 1 packing inst: half-to-single x1
|
|
# INT8 : each packCode is for one 32-bit regs, 3 packing inst: byte-to-half x2 + half-to-single x1
|
|
if self.archCaps["HasEccHalf"]:
|
|
instPerRegPack = 1 / kernel["ProblemType"]["DataType"].numRegisters() - 1
|
|
else:
|
|
instPerRegPack = 1 if (kernel["ProblemType"]["DataType"].numRegisters() == 0.25) else 0
|
|
instPerPack = int(kernel["MIInputPerThread"] * kernel["ProblemType"]["DataType"].numRegisters() * instPerRegPack)
|
|
packItems = []
|
|
for iui in range(kernel["InnerUnroll"]):
|
|
packINtems = [ [] for j in range(max(self.numReadsIterCoalescedA,self.numReadsIterCoalescedB)) ]
|
|
packA = packCode.findNamedItem("packA_I%s"%(iui))
|
|
packB = packCode.findNamedItem("packB_I%s"%(iui))
|
|
# In case localReadDo not generate pack Module
|
|
# and findNamedItem will return None type
|
|
# TODO: let all type have pack Module
|
|
if not packA:
|
|
packA = Code.Module()
|
|
packAItems = packA.flatitems()
|
|
if not packB:
|
|
packB = Code.Module()
|
|
packBItems = packB.flatitems()
|
|
if packAItems:
|
|
for j in range(self.numReadsIterCoalescedA):
|
|
for n in range(instPerPack):
|
|
packINtems[j].append(packAItems.pop(0))
|
|
if packBItems:
|
|
for j in range(self.numReadsIterCoalescedB):
|
|
for n in range(instPerPack):
|
|
packINtems[j].append(packBItems.pop(0))
|
|
while packAItems:
|
|
for j in range(self.numReadsIterCoalescedA):
|
|
for n in range(instPerPack):
|
|
packINtems[j].append(packAItems.pop(0))
|
|
while packBItems:
|
|
for j in range(self.numReadsIterCoalescedB):
|
|
for n in range(instPerPack):
|
|
packINtems[j].append(packBItems.pop(0))
|
|
for j in range(max(self.numReadsIterCoalescedA,self.numReadsIterCoalescedB)):
|
|
packItems += packINtems.pop(0)
|
|
|
|
macIterItem = macIterCode.flatitems()
|
|
# pop the first code which is s_nop 1 for packing
|
|
item = macIterItem.pop(0)
|
|
|
|
numMfmaPerIter = self.numMfmaPerIter
|
|
curPackIdx = 0
|
|
packAIdx = 0
|
|
packBIdx = 0
|
|
|
|
for i in range(numMfmaPerIter):
|
|
if packItems:
|
|
# how many pack have to be done
|
|
# calculate the data index of this mfma used for A and B
|
|
# if i // kernel["MIWaveTile"][0]==0, mfma will use new A (need to take iu into account)
|
|
# if i % kernel["MIWaveTile"][0]==0, mfma will use new B
|
|
packAIdx += instPerPack if i//(kernel["MIWaveTileA"]+kernel["MIWaveTileA"]*kernel["MIWaveTileB"]*(i//(kernel["MIWaveTileA"]*kernel["MIWaveTileB"]))) == 0 else 0
|
|
packBIdx += instPerPack if i % kernel["MIWaveTileA"] == 0 else 0
|
|
packAIdx = packAIdx if self.needPackA else 0
|
|
packBIdx = packBIdx if self.needPackB else 0
|
|
numPack = (packAIdx + packBIdx)
|
|
iterCode.addComment0("pack scheduling: packAIdx:%u, packBIdx:%u" %(packAIdx,packBIdx))
|
|
# we put 2 pack in each mfma, "2" means A & B
|
|
if packItems:
|
|
for j in range(instPerPack):
|
|
iterCode.addCode(packItems.pop(0))
|
|
curPackIdx += 1
|
|
if packItems:
|
|
for j in range(instPerPack):
|
|
iterCode.addCode(packItems.pop(0))
|
|
curPackIdx += 1
|
|
# since packed register need to wait 2 quad cycle to finish packing
|
|
# we insert pack instruction if we can, or s_nop
|
|
while curPackIdx < numPack+2:
|
|
if packItems:
|
|
for j in range(instPerPack):
|
|
iterCode.addCode(packItems.pop(0))
|
|
curPackIdx += 1
|
|
else:
|
|
iterCode.addInst("s_nop ","0","VALU packing writes to be consumed by matrix instruction")
|
|
curPackIdx += 1
|
|
if i == 0:
|
|
if not packItems:
|
|
tmpVgpr = self.vgprPool.checkOut(1)
|
|
iterCode.addInst("v_mov_b32 ","v%u"%(tmpVgpr),"0x0","valu operation to have different priority")
|
|
self.vgprPool.checkIn(tmpVgpr)
|
|
iterCode.addInst("s_setprio ","3","Raise priority while processing macs")
|
|
item = macIterItem.pop(0)
|
|
iterCode.addCode(item)
|
|
|
|
iterCode.addInst("s_setprio ","1","Raise priority while processing macs")
|
|
if kernel["1LDSBuffer"]:
|
|
barrier = Code.Module()
|
|
barrier.addComment0("1 LDS buffer: read-sync-write")
|
|
barrier.addInst("s_waitcnt lgkmcnt(0)","")
|
|
barrier.addInst(self.syncStr,"")
|
|
iterCode.addCode(barrier)
|
|
iterCode.addCode(localWriteCode)
|
|
iterCode.addCode(pointerLWCode)
|
|
iterCode.addCode(pointerLRCode)
|
|
iterCode.addInst("s_setprio ","2","Raise priority while processing macs")
|
|
pass
|
|
elif self.scheduleIterAlg == 3:
|
|
iterCode.addComment0(" grEndMfmaIndex:%u, lwStartMfmaIndex:%u, lwEndMfmaIndex:%u " %(self.grEndMfmaIndex,self.lwStartMfmaIndex,self.lwEndMfmaIndex))
|
|
printLocalWritePerMfma = kernel["PrefetchGlobalRead"] == 2 and kernel["ScheduleIterAlg"] == 3
|
|
strForLocalWritePerMfma = ", LocalWritePerMfma:%.3f"%(self.numLocalWriteModPerMfma/self.PRECISION) if printLocalWritePerMfma else ""
|
|
iterCode.addComment0(" numMfmaForLR:%u, barrierMfmaIndex:%u%s" %(self.numMfmaForNextLoopLR,self.barrierMfmaIndex,strForLocalWritePerMfma))
|
|
#####
|
|
# Prepare and Assign parameter
|
|
####
|
|
if iteration == 0:
|
|
self.localReadsVacancy = []
|
|
self.localReadsWait = [ [] for j in range(kernel["LoopIters"])]
|
|
self.localReadsWait[iteration] = waitCode
|
|
numMfmaPerIter = self.numMfmaPerIter
|
|
isBarrier = kernel["LoopIters"] - self.numItersPLR
|
|
writeItems = list(localWriteCode.items())
|
|
macIterItems = macIterCode.flatitems()
|
|
skipLocalWriteWaitcnt = 0
|
|
localReadsWaitcnt = 0
|
|
curPackIdx = 0
|
|
packAIdx = 0
|
|
packBIdx = 0
|
|
|
|
#####
|
|
# Prepare localReadCode
|
|
####
|
|
localReadCodeAB = Code.Module()
|
|
for iui in range(kernel["InnerUnroll"]):
|
|
localReadCodeA = localReadCode.findNamedItem("LocalReadDoA_I%s"%(iui))
|
|
localReadCodeB = localReadCode.findNamedItem("LocalReadDoB_I%s"%(iui))
|
|
# In case localReadDo not generate localReadCode Module
|
|
# and findNamedItem will return None type
|
|
# TODO: findNamedItem return Code.Module() if not found
|
|
if not localReadCodeA:
|
|
localReadCodeA = Code.Module()
|
|
if not localReadCodeB:
|
|
localReadCodeB = Code.Module()
|
|
if localReadCodeA.items():
|
|
localReadCodeAB.addCode(localReadCodeA.items().pop(0))
|
|
if localReadCodeB.items():
|
|
localReadCodeAB.addCode(localReadCodeB.items().pop(0))
|
|
while localReadCodeA.items():
|
|
localReadCodeAB.addCode(localReadCodeA.items().pop(0))
|
|
while localReadCodeB.items():
|
|
localReadCodeAB.addCode(localReadCodeB.items().pop(0))
|
|
localReadItems = localReadCodeAB.flatitems()
|
|
localReadItemsThisLoop = localReadItems if iteration < isBarrier else []
|
|
localReadItemsNextLoop = localReadItems if iteration >= isBarrier else []
|
|
|
|
#####
|
|
# Prepare pack Code for B:
|
|
# since the mfma reuse B first => for A: mfma[A][B]
|
|
# we need 1 vector A and 1 vector B for first mfma
|
|
# then we prepare remaining A, then remaining B
|
|
# BF16 or FP16: each packCode is for one 32-bit reg, 1 packing inst: half-to-single x1
|
|
# INT8 : each packCode is for one 32-bit regs, 3 packing inst: byte-to-half x2 + half-to-single x1
|
|
####
|
|
if self.archCaps["HasEccHalf"]:
|
|
instPerRegPack = 1 / kernel["ProblemType"]["DataType"].numRegisters() - 1
|
|
else:
|
|
instPerRegPack = 1 if (kernel["ProblemType"]["DataType"].numRegisters() == 0.25) else 0
|
|
instPerPack = int(kernel["MIInputPerThread"] * kernel["ProblemType"]["DataType"].numRegisters() * instPerRegPack)
|
|
packItems = []
|
|
for iui in range(kernel["InnerUnroll"]):
|
|
packINtems = [ [] for j in range(max(self.numReadsIterCoalescedA,self.numReadsIterCoalescedB)) ]
|
|
packA = packCode.findNamedItem("packA_I%s"%(iui))
|
|
packB = packCode.findNamedItem("packB_I%s"%(iui))
|
|
# In case localReadDo not generate pack Module
|
|
# and findNamedItem will return None type
|
|
# TODO: let all type have pack Module
|
|
if not packA:
|
|
packA = Code.Module()
|
|
packAItems = packA.flatitems()
|
|
if not packB:
|
|
packB = Code.Module()
|
|
packBItems = packB.flatitems()
|
|
if packAItems:
|
|
for j in range(self.numReadsIterCoalescedA):
|
|
for n in range(instPerPack):
|
|
packINtems[j].append(packAItems.pop(0))
|
|
if packBItems:
|
|
for j in range(self.numReadsIterCoalescedB):
|
|
for n in range(instPerPack):
|
|
packINtems[j].append(packBItems.pop(0))
|
|
while packAItems:
|
|
for j in range(self.numReadsIterCoalescedA):
|
|
for n in range(instPerPack):
|
|
packINtems[j].append(packAItems.pop(0))
|
|
while packBItems:
|
|
for j in range(self.numReadsIterCoalescedB):
|
|
for n in range(instPerPack):
|
|
packINtems[j].append(packBItems.pop(0))
|
|
for j in range(max(self.numReadsIterCoalescedA,self.numReadsIterCoalescedB)):
|
|
packItems += packINtems.pop(0)
|
|
|
|
# remove s_nop for packing
|
|
# we will add s_nop if needed
|
|
if macIterItems:
|
|
macIterItems.pop(0)
|
|
|
|
####
|
|
# scheduled local read to previous iterations
|
|
####
|
|
if self.numVgprBuffer >= kernel["LoopIters"]:
|
|
for vacancy in self.localReadsVacancy:
|
|
# {"items","latencyLeft","atIter","atMfmaIndex","noReadsAtThisIter"}
|
|
for localRead in list(localReadItemsThisLoop):
|
|
# issueLatency should not exceed miLatencyLeft
|
|
issueLatency = min(localRead.IssueLatency * 2, self.miLatencyLeft)
|
|
if vacancy["latencyLeft"] >= issueLatency:
|
|
if not localRead.readToTempVgpr:
|
|
vacancy["latencyLeft"] -= issueLatency
|
|
vacancy["items"].addCode(localRead)
|
|
localReadItemsThisLoop.remove(localRead)
|
|
if vacancy["atMfmaIndex"] > self.lwStartMfmaIndex - 1 and kernel["1LDSBuffer"]:
|
|
self.overflowedResources = 5
|
|
# update waitCnt
|
|
if self.numItersPLR:
|
|
for readsIter in range(vacancy["atIter"], iteration + self.numItersPLR):
|
|
if (vacancy["atMfmaIndex"] % numMfmaPerIter == 0 or readsIter != vacancy["atIter"]) and \
|
|
(vacancy["noReadsAtThisIter"] or readsIter <= vacancy["atIter"] + self.numItersPLR):
|
|
if isinstance(self.localReadsWait[readsIter], Code.WaitCnt):
|
|
self.localReadsWait[readsIter].lgkmcnt += 1
|
|
else:
|
|
# make sure the localread sequence remain the same
|
|
vacancy["latencyLeft"] = 0
|
|
numReadsInst = len(localReadItemsThisLoop) if iteration < isBarrier else len(localReadItemsNextLoop)
|
|
|
|
oneBufferScheduling = kernel["1LDSBuffer"] or kernel["DirectToLdsA"] or kernel["DirectToLdsB"]
|
|
|
|
for i in range(numMfmaPerIter):
|
|
mfmaIndex = iteration * numMfmaPerIter + i
|
|
lastMfmaIndex = kernel["LoopIters"] * numMfmaPerIter - 1
|
|
iterCode.addComment0(" mfmaIndex:%u " %(mfmaIndex))
|
|
|
|
####
|
|
# scheduled local read
|
|
####
|
|
readLeft = numReadsInst
|
|
latencyLeft = self.miLatencyLeft
|
|
# with PrefetchLocalRead, localreads can interleave with mfma
|
|
if self.numItersPLR and iteration < isBarrier:
|
|
# take ds_write into account to schedule ds_read, assume A and B localwrite have same width (TLDS=1)
|
|
if (mfmaIndex >= self.lwStartMfmaIndex) and not globalReadCode.countType(Code.GlobalReadInst):
|
|
for j in range(min(len(writeItems),self.numLocalWriteModPerMfma)):
|
|
if writeItems[j].countType(Code.LocalWriteInst):
|
|
# issueLatency should not exceed miLatencyLeft
|
|
latencyLeft -= min((self.tPA["localWriteInstruction"].IssueLatency*2), self.miLatencyLeft)
|
|
readLeftLROPT = 0
|
|
for j in range(len(localReadItemsThisLoop)):
|
|
# issueLatency should not exceed miLatencyLeft
|
|
issueLatency = min(localReadItemsThisLoop[j].IssueLatency*2, self.miLatencyLeft)
|
|
latencyLeft -= issueLatency
|
|
readLeftLROPT += 1 if latencyLeft >= 0 else 0
|
|
# at least 1 instruction
|
|
readLeftLROPT = max(readLeftLROPT,1)
|
|
# evenly schedule localread with each mfma
|
|
readLeftLREven = numReadsInst // numMfmaPerIter
|
|
if (numReadsInst % (numMfmaPerIter)) > i:
|
|
readLeftLREven += 1
|
|
# we want no localreads at first mfma
|
|
if (iteration == 0) and numMfmaPerIter != 1:
|
|
numMfmaForLR = numMfmaPerIter - 1
|
|
if i < numMfmaPerIter - numMfmaForLR:
|
|
readLeftLREven = 0
|
|
readLeftLROPT = 0
|
|
# rest mfma help to schedule those localReads
|
|
else:
|
|
readLeftLREven = numReadsInst // (numMfmaPerIter-1)
|
|
if (numReadsInst % (numMfmaPerIter-1)) >= i:
|
|
readLeftLREven += 1
|
|
# if there are too many localreads, change strategy to even.
|
|
readLeft = max(readLeftLREven,readLeftLROPT)
|
|
if not self.numItersPLR and iteration < isBarrier:
|
|
for j in range(len(localReadItemsThisLoop)):
|
|
# issueLatency should not exceed miLatencyLeft
|
|
issueLatency = min(localReadItemsThisLoop[j].IssueLatency*2, self.miLatencyLeft)
|
|
latencyLeft -= issueLatency
|
|
# if start to schedule localwrite, but still have localreads not scheduled yet,
|
|
# reject to use 1LDSB, since it will write and read same lds buffer at same time.
|
|
# apply same logic to DirectToLds (need to schedule local read as 1LDS buffer)
|
|
# TODO: force to schedule all remaining localreads before start to schedule localwrite.
|
|
if mfmaIndex >= self.lwStartMfmaIndex and mfmaIndex <= max(self.lwEndMfmaIndex,self.barrierMfmaIndex) and \
|
|
localReadItemsThisLoop and localWriteCode.countType(Code.LocalWriteInst) and oneBufferScheduling:
|
|
self.overflowedResources = 5
|
|
# DirectToVgpr case, localReadItemsThisLoop and localWriteCode.countType(Code.LocalWriteInst) do not satisfy at the same time.
|
|
# However, it is still invaid if localReadItemsThisLoop exists when mfmaIndex > lwStartMfmaIndex
|
|
elif (kernel["DirectToVgprA"] or kernel["DirectToVgprB"]) and \
|
|
mfmaIndex > self.lwStartMfmaIndex and mfmaIndex <= max(self.lwEndMfmaIndex,self.barrierMfmaIndex) and \
|
|
localReadItemsThisLoop and oneBufferScheduling:
|
|
self.overflowedResources = 5
|
|
for j in range(readLeft):
|
|
if localReadItemsThisLoop:
|
|
item = localReadItemsThisLoop.pop(0)
|
|
iterCode.addCode(item)
|
|
if (i == 0):
|
|
localReadsWaitcnt += 1
|
|
if not localReadItemsThisLoop and latencyLeft > 0 and iteration < isBarrier and \
|
|
not(mfmaIndex > self.lwStartMfmaIndex and kernel["1LDSBuffer"]):
|
|
item = Code.Module()
|
|
item.addComment0("localReadsVacancy: latencyLeft %d"%(latencyLeft))
|
|
iterCode.addCode(item)
|
|
self.localReadsVacancy.append({ "items": item, \
|
|
"latencyLeft": latencyLeft, \
|
|
"atIter": iteration, \
|
|
"atMfmaIndex": mfmaIndex, \
|
|
"noReadsAtThisIter": numReadsInst == 0, \
|
|
})
|
|
|
|
####
|
|
# scheduled global read
|
|
####
|
|
for j in range(self.numGlobalReadInsPerMfma):
|
|
if globalReadCode.items():
|
|
loadText = str(globalReadCode.items().pop(0))
|
|
if isDTVodd:
|
|
# need to swap Vgpr set for odd code
|
|
loadText = self.flipVregSetForDirectToVgprInGlobalRead(kernel, loadText)
|
|
iterCode.addText(loadText)
|
|
# schedule remaining globalReadInst
|
|
if mfmaIndex == self.grEndMfmaIndex:
|
|
while globalReadCode.items() and \
|
|
(globalReadCode.countType(Code.GlobalReadInst) or kernel["PrefetchGlobalRead"] == 2):
|
|
loadText = str(globalReadCode.items().pop(0))
|
|
if isDTVodd:
|
|
# need to swap Vgpr set for odd code
|
|
loadText = self.flipVregSetForDirectToVgprInGlobalRead(kernel, loadText)
|
|
iterCode.addText(loadText)
|
|
# schedule remaining globalReadIncInst
|
|
if i == numMfmaPerIter - 1:
|
|
while globalReadCode.items():
|
|
loadText = str(globalReadCode.items().pop(0))
|
|
if isDTVodd:
|
|
# need to swap Vgpr set for odd code
|
|
loadText = self.flipVregSetForDirectToVgprInGlobalRead(kernel, loadText)
|
|
iterCode.addText(loadText)
|
|
|
|
####
|
|
# scheduled local write
|
|
####
|
|
if kernel["1LDSBuffer"] and mfmaIndex == self.lwStartMfmaIndex - 1:
|
|
barrier = Code.Module()
|
|
barrier.addComment0("1 LDS buffer: read-sync-write")
|
|
barrier.addInst("s_waitcnt lgkmcnt(0)","")
|
|
barrier.addInst(self.syncStr,"")
|
|
iterCode.addCode(barrier)
|
|
|
|
if kernel["StorePriorityOpt"]:
|
|
flagInsert = False
|
|
if kernel["PrefetchGlobalRead"] == 2:
|
|
lwStartOffset = 0
|
|
if (kernel["DirectToLdsA"] or kernel["DirectToLdsB"]):
|
|
lwStartOffset = 2
|
|
# if (mfmaIndex == self.lwStartMfmaIndex or mfmaIndex == self.barrierMfmaIndex+2):
|
|
if (mfmaIndex == self.lwStartMfmaIndex + lwStartOffset or mfmaIndex == self.barrierMfmaIndex+1) :
|
|
flagInsert = True
|
|
elif kernel["PrefetchGlobalRead"] == 1 and numMfmaPerIter >= 4:
|
|
# this setting is good for fixed clock, but not good for auto clock
|
|
#if (mfmaIndex == self.grEndMfmaIndex or mfmaIndex == self.barrierMfmaIndex+1) :
|
|
withGL = ((not NLLlast) or (self.prefetchAcrossPersistent and kernel["PrefetchAcrossPersistentMode"] == 1))
|
|
withDTLload = (kernel["DirectToLdsA"] or kernel["DirectToLdsB"]) and withGL
|
|
startIndex = 0 if withDTLload else 1
|
|
if (mfmaIndex == startIndex or withGL and mfmaIndex == self.barrierMfmaIndex+1):
|
|
flagInsert = True
|
|
if flagInsert:
|
|
iterCode.addInst("s_setprio 3","store optimization")
|
|
|
|
if (mfmaIndex >= self.lwStartMfmaIndex):
|
|
for j in range(self.numLocalWriteModPerMfma):
|
|
# in case there are localWrite and globalread in same iteration
|
|
# we need to make sure globalRead before localWrite
|
|
if writeItems and not globalReadCode.countType(Code.GlobalReadInst):
|
|
writeItem = writeItems.pop(0)
|
|
# check StoreCInUnrollLoopCodeStart
|
|
if kernel["StoreCInUnroll"]:
|
|
if self.StoreCUnrollStartComment in str(writeItem):
|
|
self.StoreCUnrollLoopCodeStarted = 1 # mark as started
|
|
if self.StoreCUnrollStoreStartComment in str(writeItem):
|
|
# generate all remaining pre code before the first Store C
|
|
while(len(self.StoreCUnrollPreCode.items()) > 0):
|
|
iterCode.addCode(self.StoreCUnrollPreCode.items().pop(0))
|
|
iterCode.addCode(writeItem)
|
|
# if there is localWrite at first mfma, need to skip it in waitcnt.
|
|
if i == 0:
|
|
skipLocalWriteWaitcnt += writeItem.countType(Code.LocalWriteInst)
|
|
if not localReadItemsThisLoop:
|
|
self.perIterLocalWriteCanSkip[iteration] += writeItem.countType(Code.LocalWriteInst)
|
|
if mfmaIndex == self.lwEndMfmaIndex:
|
|
while writeItems:
|
|
writeItem = writeItems.pop(0)
|
|
# generate all remaining pre code before the first Store C
|
|
if kernel["StoreCInUnroll"]:
|
|
if self.StoreCUnrollStoreStartComment in str(writeItem):
|
|
while(len(self.StoreCUnrollPreCode.items()) > 0):
|
|
iterCode.addCode(self.StoreCUnrollPreCode.items().pop(0))
|
|
iterCode.addCode(writeItem)
|
|
if i == 0:
|
|
skipLocalWriteWaitcnt += writeItem.countType(Code.LocalWriteInst)
|
|
if not localReadItemsThisLoop:
|
|
self.perIterLocalWriteCanSkip[iteration] += writeItem.countType(Code.LocalWriteInst)
|
|
|
|
####
|
|
# scheduled pointer
|
|
####
|
|
if mfmaIndex == self.lwEndMfmaIndex:
|
|
iterCode.addCode(pointerLWCode)
|
|
if i == numMfmaPerIter - 1:
|
|
iterCode.addCode(pointerLRCode)
|
|
|
|
####
|
|
# scheduled sync
|
|
####
|
|
if mfmaIndex == self.barrierMfmaIndex and self.numItersPLR:
|
|
iterCode.addCode(waitLWCode)
|
|
iterCode.addCode(syncCode)
|
|
|
|
####
|
|
# scheduled local read for next loop
|
|
# localReads for next loop should after barrier
|
|
####
|
|
latencyLeft = self.miLatencyLeft
|
|
if self.numItersPLR and iteration >= isBarrier:
|
|
readLeftLROPT = 0
|
|
for j in range(len(localReadItemsNextLoop)):
|
|
# issueLatency should not exceed miLatencyLeft
|
|
issueLatency = min(localReadItemsNextLoop[j].IssueLatency*2, self.miLatencyLeft)
|
|
latencyLeft -= issueLatency
|
|
readLeftLROPT += 1 if latencyLeft >= 0 else 0
|
|
# at least 1 instruction
|
|
readLeftLROPT = max(readLeftLROPT,1)
|
|
# evenly schedule localread with each mfma
|
|
readLeftLREven = numReadsInst // numMfmaPerIter
|
|
if (numReadsInst % (numMfmaPerIter)) > i:
|
|
readLeftLREven += 1
|
|
# we want no localreads at barrier mfma
|
|
if (iteration == isBarrier) and numMfmaPerIter != 1:
|
|
numMfmaForLR = self.numMfmaForNextLoopLR
|
|
if i < numMfmaPerIter - numMfmaForLR:
|
|
readLeftLREven = 0
|
|
readLeftLROPT = 0
|
|
# rest mfma help to schedule those localReads
|
|
else:
|
|
readLeftLREven = numReadsInst // (numMfmaPerIter-1)
|
|
if (numReadsInst % (numMfmaPerIter-1)) >= i:
|
|
readLeftLREven += 1
|
|
# if there are too many localreads, change strategy to even.
|
|
readLeft = max(readLeftLREven,readLeftLROPT)
|
|
for j in range(readLeft):
|
|
if localReadItemsNextLoop:
|
|
item = localReadItemsNextLoop.pop(0)
|
|
iterCode.addCode(item)
|
|
if (i == 0):
|
|
localReadsWaitcnt += 1
|
|
|
|
####
|
|
# scheduled wait localReads
|
|
####
|
|
if i == 0:
|
|
iterCode.addCode(waitCode)
|
|
|
|
####
|
|
# scheduled pack
|
|
####
|
|
if packItems:
|
|
# how many pack have to be done
|
|
# calculate the data index of this mfma used for A and B
|
|
# if i // kernel["MIWaveTile"][0]==0, mfma will use new A (need to take iu into account)
|
|
# if i % kernel["MIWaveTile"][0]==0, mfma will use new B
|
|
packAIdx += instPerPack if i//(kernel["MIWaveTileA"]+kernel["MIWaveTileA"]*kernel["MIWaveTileB"]*(i//(kernel["MIWaveTileA"]*kernel["MIWaveTileB"]))) == 0 else 0
|
|
packBIdx += instPerPack if i % kernel["MIWaveTileA"] == 0 else 0
|
|
packAIdx = packAIdx if self.needPackA else 0
|
|
packBIdx = packBIdx if self.needPackB else 0
|
|
numPack = (packAIdx + packBIdx)
|
|
iterCode.addComment0("pack scheduling: packAIdx:%u, packBIdx:%u" %(packAIdx,packBIdx))
|
|
# we put 2 pack in each mfma
|
|
if packItems:
|
|
for j in range(instPerPack):
|
|
iterCode.addCode(packItems.pop(0))
|
|
curPackIdx += 1
|
|
# insert second packing code only if miLatencyLeft is large enough
|
|
if packItems and self.miLatencyLeft > 2:
|
|
for j in range(instPerPack):
|
|
iterCode.addCode(packItems.pop(0))
|
|
curPackIdx += 1
|
|
# since packed register need to wait 2 quad cycle to finish packing
|
|
# we insert pack instruction if we can, or s_nop
|
|
count = 0 # count number of cycle for nop to insert
|
|
while curPackIdx < numPack+2:
|
|
if packItems:
|
|
for j in range(instPerPack):
|
|
iterCode.addCode(packItems.pop(0))
|
|
curPackIdx += 1
|
|
else:
|
|
count += 1
|
|
curPackIdx += 1
|
|
if count:
|
|
# insert 1 nop instruction
|
|
iterCode.addInst("s_nop ",str(count - 1),"VALU packing writes to be consumed by matrix instruction")
|
|
# the final mfma or DirectToVgpr + PGR2 case, we need to generate all remaining pack instructions
|
|
# otherwise, DTV global load can overwrite pack register values
|
|
if i == numMfmaPerIter - 1 or len(list(globalReadCodeDTV.items())) > 0:
|
|
while packItems:
|
|
iterCode.addCode(packItems.pop(0))
|
|
|
|
####
|
|
# scheduled StoreCInUnrollPreProcess
|
|
####
|
|
if kernel["StoreCInUnroll"]:
|
|
if self.StoreCUnrollLoopCodeStarted and len(list(self.StoreCUnrollPreCode.items())) > 0:
|
|
iterCode.addCode(self.StoreCUnrollPreCode.items().pop(0))
|
|
|
|
####
|
|
# scheduled mfma
|
|
####
|
|
iterCode.addCode(macIterItems.pop(0) if macIterItems else Code.Module())
|
|
|
|
####
|
|
# scheduled global read for DirectToVgpr (PGR=2 only)
|
|
####
|
|
numLoadVgpr = len(list(globalReadCodeDTV.items()))
|
|
if numLoadVgpr > 0:
|
|
interval = roundUp(numMfmaPerIter / origLenGlobalReadCodeDTV)
|
|
if kernel["ProblemType"]["DataType"].isComplex():
|
|
# adjustment for complex
|
|
# limit the max of interval up to 4
|
|
interval = min(4, interval)
|
|
else:
|
|
# not complex case
|
|
# adjust(swap) the inner and outer loop index in mfmaIter to make interval as large as possible (swap for DTVA only)
|
|
# interval can be maximum of kernel["MIWaveTile"][innerLoopIndex]
|
|
innerLoopIndex = 1 if self.swapMfmaInnerLoop else 0
|
|
interval = min(kernel["MIWaveTile"][innerLoopIndex], interval)
|
|
# if number of mfma after self.grEndMfmaIndex is smaller than numMfmaPerIter, we need to use smaller interval to insert DTV load.
|
|
# this is to ensure DTV load is generated after lwStartMfmaIndex
|
|
intervalAfterGrEnd = kernel["LoopIters"] * numMfmaPerIter - self.lwStartMfmaIndex
|
|
intervalMfma = min(numMfmaPerIter, intervalAfterGrEnd)
|
|
numInstToInsert = roundUp(origLenGlobalReadCodeDTV / intervalMfma)
|
|
remainingTimesToInsert = roundUp(numLoadVgpr / numInstToInsert)
|
|
insertMfmaIndex = kernel["LoopIters"] * numMfmaPerIter - 1 - interval * (remainingTimesToInsert - 1)
|
|
# avoid insertMfmaIndex getting smaller than (kernel["LoopIters"] - 1) * numMfmaPerIter
|
|
insertMfmaIndex = max(insertMfmaIndex, (kernel["LoopIters"] - 1) * numMfmaPerIter)
|
|
# avoid insertMfmaIndex getting smaller than lwEndMfmaIndex (DTV loads must be generated after non DTV loads)
|
|
insertMfmaIndex = max(insertMfmaIndex, self.lwEndMfmaIndex)
|
|
# if mfmaIndex is the last index, insert all DTV loads
|
|
if mfmaIndex == lastMfmaIndex:
|
|
insertMfmaIndex = mfmaIndex
|
|
numInstToInsert = numLoadVgpr
|
|
if mfmaIndex == insertMfmaIndex:
|
|
for i in range(min(numLoadVgpr, numInstToInsert)):
|
|
loadDTVText = str(globalReadCodeDTV.items().pop(0))
|
|
if isDTVodd:
|
|
# need to swap Vgpr set for odd code
|
|
loadDTVText = self.flipVregSetForDirectToVgprInGlobalRead(kernel, loadDTVText)
|
|
iterCode.addText(loadDTVText)
|
|
|
|
####
|
|
# scheduled StoreCInUnrollPostProcess
|
|
####
|
|
if kernel["StoreCInUnroll"]:
|
|
numItems = len(self.StoreCUnrollPostCode.items())
|
|
# need to make sure all global read inc is already generated
|
|
# (iteration should be the last one)
|
|
if numItems > 0 and iteration == kernel["LoopIters"] - 1 and len(globalReadCode.items()) == 0:
|
|
totalMfma = kernel["LoopIters"] * numMfmaPerIter
|
|
interval = 1
|
|
numInstToInsert = roundUp(numItems / (totalMfma - mfmaIndex))
|
|
remainingTimesToInsert = roundUp(numItems / numInstToInsert)
|
|
insertMfmaIndex = totalMfma - 2 - interval * (remainingTimesToInsert - 1)
|
|
if mfmaIndex >= insertMfmaIndex:
|
|
for i in range(numInstToInsert):
|
|
iterCode.addCode(self.StoreCUnrollPostCode.items().pop(0))
|
|
|
|
if kernel["StorePriorityOpt"]:
|
|
flagInsert = False
|
|
if kernel["PrefetchGlobalRead"] == 2:
|
|
# if (mfmaIndex == self.barrierMfmaIndex or mfmaIndex == (kernel["LoopIters"] * numMfmaPerIter - 1)):
|
|
if (mfmaIndex == self.barrierMfmaIndex - 1 or (not NLLlast) and mfmaIndex == (kernel["LoopIters"] * numMfmaPerIter - 1)) :
|
|
flagInsert = True
|
|
elif kernel["PrefetchGlobalRead"] == 1 and numMfmaPerIter >= 4:
|
|
# this setting is good for fixed clock, but not good for auto clock
|
|
#if (mfmaIndex == mfmaIndex == self.barrierMfmaIndex - 1 or mfmaIndex == (kernel["LoopIters"] * numMfmaPerIter - 1)) :
|
|
insertPos1 = self.grEndMfmaIndex
|
|
if not kernel["NoLdsWriteCode"]:
|
|
insertPos1 = self.lwStartMfmaIndex - 1
|
|
withGL = ((not NLLlast) or (self.prefetchAcrossPersistent and kernel["PrefetchAcrossPersistentMode"] == 1))
|
|
if withGL and (mfmaIndex == insertPos1 or (not NLLlast) and mfmaIndex == (kernel["LoopIters"] * numMfmaPerIter - 1)) or \
|
|
(not withGL) and mfmaIndex == (kernel["LoopIters"] * numMfmaPerIter // 2 - 1):
|
|
flagInsert = True
|
|
if flagInsert:
|
|
iterCode.addInst("s_setprio 0","store optimization")
|
|
else:
|
|
assert 0, "Unsupported scheduleIterAlg=%u"%self.scheduleIterAlg
|
|
|
|
if isinstance(waitCode, Code.WaitCnt):
|
|
|
|
# Set the waitCount, based on the new iter schedule
|
|
lgkmcnt = waitCode.lgkmcnt
|
|
localReads = 0
|
|
localWrites = 0
|
|
if kernel["EnableMatrixInstruction"]:
|
|
# dataAtIter : the data we wait is read at which iteration
|
|
# numReadsIter : in this loop, number of iteration we have read (data used in current loop)
|
|
dataAtIterA = iteration//self.numIterPerCoalescedReadA - self.numItersPLR
|
|
dataAtIterB = iteration//self.numIterPerCoalescedReadB - self.numItersPLR
|
|
numReadsIterA = min(iteration+1, kernel["LoopIters"]//self.numIterPerCoalescedReadA - self.numItersPLR)
|
|
numReadsIterB = min(iteration+1, kernel["LoopIters"]//self.numIterPerCoalescedReadB - self.numItersPLR)
|
|
skipReadsIterA = numReadsIterA - dataAtIterA - 1 if not dataAtIterA < max(dataAtIterA,dataAtIterB) else 0
|
|
skipReadsIterB = numReadsIterB - dataAtIterB - 1 if not dataAtIterB < max(dataAtIterA,dataAtIterB) else 0
|
|
# numPrefetchIter : in this loop, number of prefetch iteration we have read (data used in next loop)
|
|
# currently we have localReadA and localReadB if iteration >= isBarrier
|
|
# some case will not have localReads if PGR=0 or NoLoadLoop
|
|
# known bug: wider localread + numItersPLR>1 may have chance to fail.
|
|
numPrefetchIter = (iteration//(kernel["LoopIters"]-self.numItersPLR))*((iteration+1)-(kernel["LoopIters"]-self.numItersPLR)) if kernel["PrefetchGlobalRead"] else 0
|
|
numPrefetchIter = 0 if iteration >= isBarrier and not hasLocalRead else numPrefetchIter
|
|
skipReadsIterA += numPrefetchIter
|
|
skipReadsIterB += numPrefetchIter
|
|
# here the reads are prefetches so can skip them in the waitcnt
|
|
# how many localreads can skip is based on how many iterations we prefetch.
|
|
localReadsA = 0 if kernel["DirectToVgprA"] else self.numReadsPerIterA * skipReadsIterA
|
|
localReadsB = 0 if kernel["DirectToVgprB"] else self.numReadsPerIterB * skipReadsIterB
|
|
localReads += localReadsA + localReadsB
|
|
# some of localReads is interleaved after waitcnt in SIA3
|
|
if kernel["ScheduleIterAlg"] == 3 and self.numItersPLR and\
|
|
(iteration < numReadsIterA or iteration < numReadsIterB or numPrefetchIter) and \
|
|
self.enable["LocalRead"]:
|
|
if ((iteration//self.numIterPerCoalescedReadA < numReadsIterA and not dataAtIterA < max(dataAtIterA,dataAtIterB)) or numPrefetchIter) and (not kernel["DirectToVgprA"]):
|
|
localReads -= self.numReadsPerIterA
|
|
if ((iteration//self.numIterPerCoalescedReadB < numReadsIterB and not dataAtIterB < max(dataAtIterA,dataAtIterB)) or numPrefetchIter) and (not kernel["DirectToVgprB"]):
|
|
localReads -= self.numReadsPerIterB
|
|
localReads += localReadsWaitcnt
|
|
lgkmcnt += localReads
|
|
iterCode.addComment0("numPrefetchIter=%u" % numPrefetchIter)
|
|
iterCode.addComment0("dataAtIterA=%u numReadsIterA=%u skipReadsIterA=%u readsPerIterA=%u" % (dataAtIterA, numReadsIterA, skipReadsIterA, self.numReadsPerIterA))
|
|
iterCode.addComment0("dataAtIterB=%u numReadsIterB=%u skipReadsIterB=%u readsPerIterB=%u" % (dataAtIterB, numReadsIterB, skipReadsIterB, self.numReadsPerIterB))
|
|
if kernel["ScheduleIterAlg"] == 0 or kernel["ScheduleIterAlg"] == 1:
|
|
# adjust the initial value of loop counter for DirectToVgpr
|
|
adj = 1 if (kernel["DirectToVgprA"] or kernel["DirectToVgprB"]) else 0
|
|
for i in range (max(dataAtIterA,dataAtIterB)+adj,iteration+1):
|
|
localWrites += self.perIterLocalWriteCode[i].countType(Code.LocalWriteInst)
|
|
# ScheduleIterAlg=2, localwrite is after waitCnt, no need to count it's current iteration.
|
|
if kernel["ScheduleIterAlg"] == 3:
|
|
for i in range (max(dataAtIterA,dataAtIterB)+1,iteration):
|
|
localWrites += self.perIterLocalWriteCode[i].countType(Code.LocalWriteInst)
|
|
if kernel["ScheduleLocalWrite"] > 0:
|
|
# current iteration localWrite count
|
|
localWrites += skipLocalWriteWaitcnt
|
|
# dataAtIter iteration localWrite count
|
|
if self.numItersPLR:
|
|
skipPreIterLW = self.perIterLocalWriteCanSkip[max(dataAtIterA,dataAtIterB)]
|
|
if kernel["PrefetchGlobalRead"] == 2 and oneBufferScheduling:
|
|
# PGR==2 and oneBufferScheduling case, count local write before max(dataAtIterA,dataAtIterB)
|
|
# NOTE: This logic assumes that local write is scheduled after local read.
|
|
for up in range(max(dataAtIterA,dataAtIterB)):
|
|
skipPreIterLW += self.perIterLocalWriteCanSkip[up]
|
|
localWrites += skipPreIterLW
|
|
lgkmcnt += localWrites
|
|
else:
|
|
for item in list(iterCode.items()):
|
|
localReads = item.countType(Code.LocalReadInst)
|
|
localWrites = item.countType(Code.LocalWriteInst)
|
|
if self.numVgprBuffer:
|
|
# SQ: If PrefetchLocalRead = 1 and DepthU == LocalSplitU, then there is no double
|
|
# buffering and we must wait for all localReads but not localWrites.
|
|
# In that case, LoopIters == 1:
|
|
if kernel["LoopIters"] > 1:
|
|
# here the reads are prefetches so can skip them in the waitcnt
|
|
lgkmcnt += localReads
|
|
# and the writes are targetting another section of LDS and are
|
|
# synchronized through a different waitcnt than this one
|
|
# (which is always just before the macs)
|
|
lgkmcnt += localWrites
|
|
else:
|
|
# if UnrollLoopEfficiencyEnable == True use waitCode passed lgkmCnt
|
|
# else:
|
|
# we need to wait for all preceding reads before the macs
|
|
# so only opportunity for optimization is if the writes are at the end
|
|
if globalParameters["UnrollLoopEfficiencyEnable"]:
|
|
lgkmcnt = waitCode.lgkmcnt
|
|
else:
|
|
if localReads:
|
|
lgkmcnt = 0 # reset to wait for all reads
|
|
else:
|
|
lgkmcnt = localWrites # this only survives if writes are at the end
|
|
|
|
waitCode.comment += " old=%u, new=%u newLW=%u newLR=%u" % (waitCode.lgkmcnt, lgkmcnt,localWrites,localReads)
|
|
waitCode.lgkmcnt = lgkmcnt
|
|
|
|
return iterCode
|
|
|
|
##############################################################################
|
|
# returns list of modules or text
|
|
# papIter indicates this is the setup for the "prefetchAcrossPersistent"
|
|
# (aka pap) iteration
|
|
##############################################################################
|
|
def setupNewTile(self, kernel, tensorParametersA, tensorParametersB, isPap, isOptNLL=False, forceNoTileCode=False, forceNoGRCode=False):
|
|
kl = []
|
|
kl_LW = [] # generate tile assignment code + local write code separately for init code optimization
|
|
|
|
if self.enable["PreLoop"]:
|
|
####################################
|
|
# Global Read Addresses
|
|
####################################
|
|
kl.append(self.comment3("Begin setupNewTile, isPap=%s") % isPap)
|
|
|
|
# work-group assignments
|
|
kl.append(self.comment("global read addresses: work-group"))
|
|
if not forceNoTileCode:
|
|
kl.append(self.graWorkGroup(kernel, isPap))
|
|
|
|
needShift = False
|
|
if (kernel["EdgeType"] == "ShiftPtr") and \
|
|
(not (kernel["BufferLoad"] and kernel["GuaranteeNoPartialA"])) or \
|
|
(not (kernel["BufferLoad"] and kernel["GuaranteeNoPartialB"])):
|
|
needShift = True
|
|
|
|
# some case (PAP), we don't have to append the code for duplicated calculation
|
|
# only those calculation related to WorkGroupID need to be generated. otherwise it's just redundant
|
|
# default dontAppendCode = False, means need to append code
|
|
self.dontAppendCode = False
|
|
|
|
# 1. during isPap, this is actually no needed, so we can skip this.
|
|
# but since there are some vgpr value is used in the later lwaFirstOffset (when not OptNLL, such as "lwoT")
|
|
# so we still do this part when "isPap & not OptNLL"
|
|
# 2. if tile edge, then we still need to add all these codes even isPap
|
|
|
|
self.dontAppendCode = isPap and kernel["PrefetchAcrossPersistentMode"] == 1 and ((not needShift) or self.useGlobalReadTileVgpr)
|
|
self.dontAppendCode = self.dontAppendCode or forceNoTileCode
|
|
# tile assignments
|
|
kl_LW.append(self.comment("global read addresses: tile offset assignment a"))
|
|
kl_LW.append(self.graTileAssignment(kernel, tensorParametersA))
|
|
kl_LW.append(self.comment("global read addresses: tile offset assignment b"))
|
|
kl_LW.append(self.graTileAssignment(kernel, tensorParametersB))
|
|
# init code optimization
|
|
# not init code opt case, add tile assignments code here
|
|
# init code opt case, not insert tile assignments code here and return tile assignments code separately for replacement
|
|
if not self.isInitCodeOptLW:
|
|
kl += kl_LW
|
|
kl_LW = []
|
|
|
|
self.dontAppendCode = isPap and (not needShift)
|
|
self.dontAppendCode = self.dontAppendCode or forceNoTileCode
|
|
# unroll assignments
|
|
kl.append(self.comment("global read addresses: unroll assignment a"))
|
|
kl.append(self.graUnrollAssignment(kernel, tensorParametersA))
|
|
kl.append(self.comment("global read addresses: unroll assignment b"))
|
|
kl.append(self.graUnrollAssignment(kernel, tensorParametersB))
|
|
self.dontAppendCode = False
|
|
self.dontAppendCode = self.dontAppendCode or forceNoTileCode
|
|
|
|
# other free indices
|
|
if kernel["ProblemType"]["NumIndicesC"] > 2:
|
|
kl.append(self.comment("global read addresses: other free assignments"))
|
|
kl.append(self.graOtherFreeAssignments(kernel))
|
|
|
|
# other summation indices
|
|
if self.otherSummations:
|
|
kl.append(self.comment("global read addresses: other summation assignments"))
|
|
kl.append(self.graOtherSummationAssignments(kernel))
|
|
|
|
self.dontAppendCode = isPap and ((not needShift) or self.useGlobalReadTileVgpr)
|
|
self.dontAppendCode = self.dontAppendCode or forceNoTileCode
|
|
# tile offsets
|
|
kl.append(self.comment("global read addresses: tile offsets a"))
|
|
kl.append(self.graTileOffsets(kernel, tensorParametersA))
|
|
kl.append(self.comment("global read addresses: tile offsets b"))
|
|
kl.append(self.graTileOffsets(kernel, tensorParametersB))
|
|
|
|
# unroll offsets
|
|
kl.append(self.comment("global read addresses: unroll offsets a"))
|
|
kl.append(self.graUnrollOffsets(kernel, tensorParametersA))
|
|
kl.append(self.comment("global read addresses: unroll offsets b"))
|
|
kl.append(self.graUnrollOffsets(kernel, tensorParametersB))
|
|
self.dontAppendCode = False
|
|
self.dontAppendCode = self.dontAppendCode or forceNoTileCode
|
|
|
|
# tile edges
|
|
if kernel["EdgeType"] == "ShiftPtr":
|
|
# Shift here has two purposes:
|
|
# 1. Ensure the loads are in-bounds to prevent fault.
|
|
# BufferLoad uses the buffer limit hardware and does not require bounds checking for this case
|
|
# 2. Shift-left a wide vector load to ensure it is completely in-bounds.
|
|
# If this occurs we need to 'unshift' the C values (see shiftVectorComponents)
|
|
# BufferLoad does support this shifting, but if GuaranteeNoPartial=1 then
|
|
# it can be guaranteed that no shifting is required.
|
|
if not (kernel["BufferLoad"] and kernel["GuaranteeNoPartialA"]) and not forceNoTileCode:
|
|
kl.append(self.comment("global read addresses: shift a"))
|
|
kl.append(self.graShift(kernel, tensorParametersA))
|
|
if not (kernel["BufferLoad"] and kernel["GuaranteeNoPartialB"]) and not forceNoTileCode:
|
|
kl.append(self.comment("global read addresses: shift b"))
|
|
kl.append(self.graShift(kernel, tensorParametersB))
|
|
elif kernel["EdgeType"] == "Branch":
|
|
kl.append(self.comment("global read addresses: branch a"))
|
|
kl.append(self.graBranch(kernel, tensorParametersA))
|
|
kl.append(self.comment("global read addresses: branch b"))
|
|
kl.append(self.graBranch(kernel, tensorParametersB))
|
|
|
|
self.dontAppendCode = isPap and (not needShift)
|
|
self.dontAppendCode = self.dontAppendCode or forceNoTileCode
|
|
# final offsets
|
|
kl.append(self.comment("global read addresses: final offsets a"))
|
|
kl.append(self.graFinalOffsets(kernel, tensorParametersA))
|
|
kl.append(self.comment("global read addresses: final offsets b"))
|
|
kl.append(self.graFinalOffsets(kernel, tensorParametersB))
|
|
self.dontAppendCode = False
|
|
self.dontAppendCode = self.dontAppendCode or forceNoTileCode
|
|
|
|
# addresses
|
|
if not forceNoTileCode:
|
|
kl.append(self.comment("global read addresses: addresses a"))
|
|
kl.append(self.graAddresses(kernel, tensorParametersA, isPap))
|
|
kl.append(self.comment("global read addresses: addresses b"))
|
|
kl.append(self.graAddresses(kernel, tensorParametersB, isPap))
|
|
|
|
self.dontAppendCode = isPap
|
|
self.dontAppendCode = self.dontAppendCode or forceNoTileCode
|
|
# increments
|
|
kl.append(self.comment("global read addresses: increments a"))
|
|
for i in reversed(range(kernel["ProblemType"]["NumIndicesSummation"])):
|
|
kl.append(self.graIncrements(kernel, i, tensorParametersA))
|
|
kl.append(self.comment("global read addresses: increments b"))
|
|
for i in reversed(range(kernel["ProblemType"]["NumIndicesSummation"])):
|
|
kl.append(self.graIncrements(kernel, i, tensorParametersB))
|
|
self.dontAppendCode = False
|
|
self.dontAppendCode = self.dontAppendCode or forceNoTileCode
|
|
|
|
####################################
|
|
# Local Write Addresses
|
|
####################################
|
|
kl_LW.append(self.comment3("Local Write Addresses"))
|
|
|
|
# tile assignments
|
|
kl_LW.append(self.lwaTileAssignment(kernel, tensorParametersA))
|
|
kl_LW.append(self.lwaTileAssignment(kernel, tensorParametersB))
|
|
|
|
# unroll assignments
|
|
kl_LW.append(self.lwaUnrollAssignment(kernel, tensorParametersA))
|
|
kl_LW.append(self.lwaUnrollAssignment(kernel, tensorParametersB))
|
|
|
|
# if PAP, no need to reset LWA, but if not OptNLL, we still do this (due to TailLoop)
|
|
|
|
self.dontAppendCode = isPap and kernel["PrefetchAcrossPersistentMode"] == 1
|
|
self.dontAppendCode = self.dontAppendCode or forceNoTileCode
|
|
# first offsets
|
|
kl_LW.append(self.comment("local write addresses: first offset a"))
|
|
kl_LW.append(self.lwaFirstOffset(kernel, tensorParametersA))
|
|
kl_LW.append(self.comment("local write addresses: first offset b"))
|
|
kl_LW.append(self.lwaFirstOffset(kernel, tensorParametersB))
|
|
self.dontAppendCode = False
|
|
self.dontAppendCode = self.dontAppendCode or forceNoTileCode
|
|
|
|
# final offsets
|
|
kl_LW.append(self.lwaFinalOffsets(kernel, tensorParametersA))
|
|
kl_LW.append(self.lwaFinalOffsets(kernel, tensorParametersB))
|
|
|
|
# declare addresses
|
|
kl_LW.append(self.lwaDeclareAddresses(kernel, tensorParametersA))
|
|
kl_LW.append(self.lwaDeclareAddresses(kernel, tensorParametersB))
|
|
|
|
# init pointers
|
|
kl_LW.append(self.localWriteInitPointers(kernel, tensorParametersA))
|
|
kl_LW.append(self.localWriteInitPointers(kernel, tensorParametersB))
|
|
|
|
# init code optimization
|
|
if self.isInitCodeOptLW:
|
|
# init code optimization case, release lwaVgpr here after all lwa code is generated
|
|
# (to avoid lwa vgpr overwritten by remaining lwa code)
|
|
self.lwaReleaseTileVgpr(kernel, tensorParametersA)
|
|
self.lwaReleaseTileVgpr(kernel, tensorParametersB)
|
|
else:
|
|
# not init code opt case, add local write code here
|
|
# init code opt case, not insert local write code here and return local write code separately for replacement
|
|
kl += kl_LW
|
|
kl_LW = []
|
|
|
|
###########################################################################
|
|
# summations loops: open
|
|
###########################################################################
|
|
|
|
# declare loop num iter
|
|
if not forceNoTileCode:
|
|
kl.append(self.comment1("declare loop num iterations"))
|
|
kl.append(self.declareLoopNumIter(kernel))
|
|
|
|
# perform initC in the shadow of the prefetch
|
|
# Prefetch occurs at start of unroll loop
|
|
# If we have multiple summation indices (otherSummationLoops>0),
|
|
# we can't init in shadow of this prefetch
|
|
# since that would initC inside the other summation loops
|
|
|
|
if self.doShadowInit != 2:
|
|
kl.append(self.initC(kernel))
|
|
|
|
# open non-unrolled summation loops
|
|
if not forceNoTileCode:
|
|
for i in range(kernel["ProblemType"]["NumIndicesSummation"]-1):
|
|
kl.append(self.comment("summation loop %u"%i))
|
|
kl.append(self.calculateLoopNumIter(kernel, i, isPap))
|
|
if self.actualSummationLoops>1:
|
|
kl.append(self.openLoop(kernel, i))
|
|
kl.append(self.calculateLoopNumIter(kernel, self.unrollIdx, isPap))
|
|
|
|
if not forceNoTileCode:
|
|
if self.staggerU:
|
|
kl.append(self.declareStaggerParms(kernel))
|
|
kl.append(self.calculateStagger(kernel, tensorParametersA))
|
|
kl.append(self.calculateStagger(kernel, tensorParametersB))
|
|
|
|
# isPap don't init the read pointers - we want to continue to use the double-buffer
|
|
# LRO and LWA as assigned
|
|
if self.enable["PreLoop"] and not isPap:
|
|
# init lds read pointers before each unrolled loop
|
|
kl.append(self.comment1("local read addresses: init pointers a"))
|
|
kl.append(self.localReadInitPointers(kernel, tensorParametersA))
|
|
kl.append(self.comment1("local read addresses: init pointers b"))
|
|
kl.append(self.localReadInitPointers(kernel, tensorParametersB))
|
|
|
|
if isPap and not isOptNLL:
|
|
# init lds read pointers before each unrolled loop
|
|
kl.append(self.comment1("local read addresses: reset offset a"))
|
|
kl.append(self.localReadResetOffsets(kernel, tensorParametersA))
|
|
kl.append(self.comment1("local read addresses: reset offset b"))
|
|
kl.append(self.localReadResetOffsets(kernel, tensorParametersB))
|
|
|
|
####################################
|
|
# prefetch: unrolled loop prefix
|
|
####################################
|
|
if kernel["PrefetchGlobalRead"]:
|
|
pfi = 1
|
|
kl.append(self.comment("prefetch: global -> local"))
|
|
kl.append(self.openSumAtLeastUnroll(kernel, prefetch=True, isOptNLL=isOptNLL, isPap=isPap))
|
|
if isPap and isOptNLL:
|
|
# forceNoGRCode case, reset and not generate global read A/B code
|
|
if self.enable["GlobalRead"] and (not forceNoGRCode):
|
|
# if DirectToVgpr is enabled and swapGlobalRead is true, swap the order of global read (B->A)
|
|
tensorParameters1st = tensorParametersA
|
|
tensorParameters2nd = tensorParametersB
|
|
if self.isSwapGlobalReadOrderForDtvOrDtl(kernel):
|
|
tensorParameters1st, tensorParameters2nd = tensorParameters2nd, tensorParameters1st
|
|
self.dtlsM0UpdateACode = self.directToLdsM0Update(kernel, 0, tensorParameters1st, usePlaceHolder=isPap)
|
|
self.globalReadACode = self.globalReadDo(kernel, 0, tensorParameters1st, 0)
|
|
self.dtlsM0UpdateBCode = self.directToLdsM0Update(kernel, 0, tensorParameters2nd, usePlaceHolder=isPap)
|
|
self.globalReadBCode = self.globalReadDo(kernel, 0, tensorParameters2nd, 0)
|
|
else:
|
|
self.dtlsM0UpdateACode = Code.StructuredModule()
|
|
self.globalReadACode = Code.StructuredModule() # empty
|
|
self.dtlsM0UpdateBCode = Code.StructuredModule()
|
|
self.globalReadBCode = Code.StructuredModule() # empty
|
|
|
|
if self.enable["GlobalReadInc"]:
|
|
self.globalReadIncrements = self.globalReadIncrementAB(kernel, self.unrollIdx, pfi)
|
|
else:
|
|
self.globalReadIncrements = Code.Module()
|
|
self.globalReadIncrements.addCode(Code.Module("globalReadIncrementA"))
|
|
self.globalReadIncrements.addCode(Code.Module("globalReadIncrementB"))
|
|
|
|
else:
|
|
if self.enable["GlobalRead"]:
|
|
# if DirectToVgpr is enabled and swapGlobalRead is true, swap the order of global read (B->A)
|
|
tensorParameters1st = tensorParametersA
|
|
tensorParameters2nd = tensorParametersB
|
|
if self.isSwapGlobalReadOrderForDtvOrDtl(kernel):
|
|
tensorParameters1st, tensorParameters2nd = tensorParameters2nd, tensorParameters1st
|
|
tmpStr = str(self.directToLdsM0Update(kernel, 0, tensorParameters1st, usePlaceHolder=isPap))
|
|
tmpStr = tmpStr.replace("__placeholder__", str(0))
|
|
kl.append(tmpStr)
|
|
kl.append(str(self.globalReadDo(kernel, 0, tensorParameters1st, 0)))
|
|
tmpStr = str(self.directToLdsM0Update(kernel, 0, tensorParameters2nd, usePlaceHolder=isPap))
|
|
tmpStr = tmpStr.replace("__placeholder__", str(0))
|
|
kl.append(tmpStr)
|
|
kl.append(str(self.globalReadDo(kernel, 0, tensorParameters2nd, 0)))
|
|
if self.enable["GlobalReadInc"]:
|
|
kl.append(self.globalReadIncrementAB(kernel, self.unrollIdx, pfi))
|
|
|
|
kl.append(self.comment3("End setupNewTile, isPap=%s") % isPap)
|
|
|
|
return kl, kl_LW
|
|
|
|
|
|
##############################################################################
|
|
# get conditions to skip local read write wait
|
|
##############################################################################
|
|
def getConditionToSkipLocalReadWriteWait( self, kernel , isPap, u, lastU):
|
|
# not generate wait code here if u == 0 u != lastU and DirectToVgpr + DirectToLds is enabled
|
|
# (to remove redundant wait. isPap case only)
|
|
# exception is PGR=2. wait is necessary for u = 0 in PGR=2 case
|
|
cond1 = not (isPap and u == 0 and u != lastU and kernel["PrefetchLocalRead"] != 0 and \
|
|
(kernel["DirectToVgprA"] and kernel["DirectToLdsB"] or kernel["DirectToVgprB"] and kernel["DirectToLdsA"])) \
|
|
or kernel["PrefetchGlobalRead"]==2
|
|
# no need local read wait if LocalReadVectorWidth>1 and u%numReadsIterCoalescedB is not 0
|
|
# In that case, Prefetch local read covers both u = 0 and 1 (limit to MFMA+double+DirectToVgpr only)
|
|
# (The other side of numReadsIterCoalesced must be 0 to skip local read wait)
|
|
condSkip = (u%self.numReadsIterCoalescedB != 0) and kernel["EnableMatrixInstruction"] and \
|
|
((kernel["DirectToVgprA"] and (not kernel["ProblemType"]["TLUB"])) or \
|
|
(kernel["DirectToVgprB"] and (not kernel["ProblemType"]["TLUA"])))
|
|
# another skip condition
|
|
# skip wait for SIA=3 and 1LDSBuffer and PLR > LoopIters and u > localWriteStartIter
|
|
# in this case, all local read is executed before 1LDSBuffer sync and no need to wait for local read
|
|
if (kernel["ScheduleIterAlg"] == 3 and kernel["1LDSBuffer"] and kernel["PrefetchLocalRead"] > kernel["LoopIters"]):
|
|
localWriteStartIter = self.lwStartMfmaIndex//self.numMfmaPerIter
|
|
if u > localWriteStartIter:
|
|
condSkip = True
|
|
# no local write wait is necessary in DirectToVgprA + DirectToVgprB case
|
|
cond2 = not (kernel["DirectToVgprA"] and kernel["DirectToVgprB"])
|
|
return cond1 and (not condSkip) and cond2
|
|
|
|
##############################################################################
|
|
# No Load Loop Body
|
|
##############################################################################
|
|
def noLoadLoopBody( self, kernel, tensorParametersA, tensorParametersB, kl, pack, isOptNLL, isPap, isNGLL, NLLfirst, NLLlast, isDTVodd=False):
|
|
expand = kernel["ExpandPointerSwap"]
|
|
lastuIdx = False
|
|
pflr = self.numItersPLR
|
|
localWriteEndIter = kernel["LoopIters"] - self.numItersPLR - 1
|
|
# noTailLoop optimization
|
|
# if this is the last NoLoadLoop(NLLlast) and self.tailLoopInNLL case, set tail=True for mfmaIter
|
|
needTailCode = NLLlast and self.tailLoopInNLL
|
|
|
|
# vregSetIdx for DTV
|
|
# NGLL case, use first set. NLL case, use second set
|
|
vregSetIdxMFMA = 0 if isNGLL else 1
|
|
# flip vregSetIdx if isDTVodd is True
|
|
if isDTVodd:
|
|
vregSetIdxMFMA = 1 - vregSetIdxMFMA
|
|
|
|
for uIdx in range(0, kernel["LoopIters"]*kernel["DepthULdsDivisor"]):
|
|
u = uIdx % kernel["LoopIters"] # u: index in compute loop (in contrast to the notion of global read loop)
|
|
uDu = uIdx // kernel["LoopIters"] # uDu: index of compute loop
|
|
isLastLoop = (uDu == kernel["DepthULdsDivisor"] -1 ) and not isNGLL
|
|
if u == 0:
|
|
if uDu > 0:
|
|
if self.enable["GlobalRead"]:
|
|
assert len(self.globalReadACode.items()) > 0 and len(self.globalReadBCode.items()) > 0 # already issued in first uDu
|
|
self.globalReadACode = Code.StructuredModule() # empty
|
|
self.globalReadBCode = Code.StructuredModule() # empty
|
|
if self.enable["GlobalReadInc"]:
|
|
self.globalReadIncrements = Code.Module() # empty
|
|
self.globalReadIncrements.addCode(Code.Module("globalReadIncrementA"))
|
|
self.globalReadIncrements.addCode(Code.Module("globalReadIncrementB"))
|
|
if not isLastLoop:
|
|
self.localWriteACode = self.localWriteDo(kernel, tensorParametersA, (uDu+1)%kernel["DepthULdsDivisor"]) # local write in loopcnt N targets data for loopcnt N+1
|
|
self.localWriteBCode = self.localWriteDo(kernel, tensorParametersB, (uDu+1)%kernel["DepthULdsDivisor"])
|
|
else:
|
|
self.localWriteACode = Code.Module()
|
|
self.localWriteBCode = Code.Module()
|
|
|
|
# TODO schedule waitcnt/barrier in makeSubIterSchedule()
|
|
if kernel["PrefetchGlobalRead"] and kernel["LoopIters"] in [1, 2] and uDu > 0:
|
|
if self.enable["Wait"]:
|
|
kl.append(self.wait(kernel, tensorParametersA, tensorParametersB, 1, 0, -1, "wait for local write"))
|
|
if self.enable["Sync"]:
|
|
kl.append(self.syncThreads(kernel, "sync for local read after write"))
|
|
|
|
if not isNGLL or isPap:
|
|
# PAP would have GlobalRead and GlobalInc, but no localWrite
|
|
# Get the perIterGlobalReadCode code for PAP (if PAP=On), else would be empty
|
|
# NGLL (PGR=2) and isPap case, we do not need globalInc code. Set skip flag in that case
|
|
skipGlobalReadInc = isNGLL and isPap
|
|
self.makeSchedule(kernel, tensorParametersA, tensorParametersB, localWriteEndIter, uDu, skipGlobalReadInc=skipGlobalReadInc, lastLoop=NLLlast)
|
|
kl.append(str(self.unrollLoopHeaderCode))
|
|
|
|
# which loop iteration to reset the LRO,
|
|
# note if PLR=0, isResetLroIter is False for all u
|
|
isResetLroIter = (u == localWriteEndIter)
|
|
isSwapAndResetLwoIter = isResetLroIter
|
|
isSwapLroIter = isResetLroIter
|
|
if kernel["ScheduleIterAlg"] == 3:
|
|
isSwapAndResetLwoIter = (u == self.lwEndMfmaIndex//(self.numMfmaPerIter))
|
|
|
|
extraComment = ""
|
|
if isLastLoop:
|
|
extraComment += " (last unrolled loop)"
|
|
else:
|
|
if kernel.enabledSplitLDS:
|
|
extraComment += f" (uDu={uDu}) "
|
|
if isResetLroIter:
|
|
extraComment += " (reset local read pointers iteration) "
|
|
if isSwapAndResetLwoIter:
|
|
extraComment += " (swap and reset local write pointers iteration) "
|
|
if isSwapLroIter:
|
|
extraComment += " (swap local read pointers iteration) "
|
|
|
|
kl.append(self.comment("iter %u%s"%(u,extraComment)))
|
|
plrIdx = ((u+pflr) % (self.numVgprBuffer+1)) % kernel["LoopIters"]
|
|
plrIdxDTV = (u+pflr) % kernel["LoopIters"]
|
|
localReads = Code.Module()
|
|
|
|
pointerLWCode = Code.Module()
|
|
pointerLRCode = Code.Module()
|
|
waitCode = Code.Module() # may be overwritten (not added to) below
|
|
macIterCode = Code.Module()
|
|
waitLWCode = Code.Module()
|
|
syncCode = Code.Module()
|
|
|
|
# vregSetIdx for DTV
|
|
vregSetIdxLR = vregSetIdxMFMA
|
|
if kernel["LoopIters"] > 1 and u == kernel["LoopIters"] - 1 and \
|
|
not ( isNGLL and (not isDTVodd) and self.generateDTVodd):
|
|
# use next vregSet for the last loopIter
|
|
# exception for isNGLL and not isDTVodd and odd code enabled case
|
|
# in this case, we need to flip twice (for NGLL to NLL, even to odd)
|
|
# then, we do not need to flip vregSetIdxLR
|
|
vregSetIdxLR = 1 - vregSetIdxLR
|
|
|
|
if self.enable["LocalRead"]:
|
|
hasLiveLdsData = kernel["PrefetchGlobalRead"] or (uDu < kernel["DepthULdsDivisor"]-1)
|
|
hasLiveLdsData = hasLiveLdsData and not isLastLoop
|
|
# for DirectToVgpr + DTVodd
|
|
# need to call localReadDo to allocate tmpVgpr for the next DTVeven case (no actual asm code generated for DTV)
|
|
needExtraLocalReadDo = (NLLlast and isDTVodd and u > localWriteEndIter)
|
|
# DTV pack case, generate local read code for DTVodd case
|
|
hasLiveLdsData = hasLiveLdsData or needExtraLocalReadDo
|
|
# reads for current loop are done in previous iteration because of wider local read
|
|
doReadA = (u < kernel["LoopIters"]/self.numIterPerCoalescedReadA - self.numItersPLR)
|
|
doReadB = (u < kernel["LoopIters"]/self.numIterPerCoalescedReadB - self.numItersPLR)
|
|
# reads for next loop
|
|
doReadA = doReadA or (hasLiveLdsData and u > localWriteEndIter)
|
|
doReadB = doReadB or (hasLiveLdsData and u > localWriteEndIter)
|
|
for iui in range(0,kernel["InnerUnroll"]):
|
|
doReadA = doReadA and iui*self.numReadsIterCoalescedA < kernel["InnerUnroll"]
|
|
doReadB = doReadB and iui*self.numReadsIterCoalescedB < kernel["InnerUnroll"]
|
|
if doReadA:
|
|
# needExtraLocalReadDo only case, no need to generate actual code
|
|
# just need to call localReadDo to allocate tmpVgpr
|
|
if not needExtraLocalReadDo:
|
|
localReads.addText(self.comment("local read a"))
|
|
bufferIdx = plrIdx*self.numIterPerCoalescedReadA
|
|
if self.packDTVA:
|
|
# DTV + pack case, offset bufferIdx for local read packing instructions
|
|
bufferIdx = plrIdxDTV*self.numIterPerCoalescedReadA + vregSetIdxLR * kernel["LoopIters"]
|
|
localReadCodeA, packCodeA = self.localReadDo(kernel, bufferIdx, iui*self.numReadsIterCoalescedA, 0, tensorParametersA)
|
|
if not needExtraLocalReadDo:
|
|
localReads.addCode(localReadCodeA)
|
|
pack[plrIdx*self.numIterPerCoalescedReadA].addCode(packCodeA)
|
|
if needExtraLocalReadDo and (self.packDTVA or self.packDTVB):
|
|
# packDTV + needExtraLocalReadDo, need to update pack code to flip vregSetIdx
|
|
pack[plrIdx*self.numIterPerCoalescedReadA].addCode(packCodeA)
|
|
if doReadB:
|
|
# needExtraLocalReadDo only case, no need to generate actual code
|
|
# just need to call localReadDo to allocate tmpVgpr
|
|
if not needExtraLocalReadDo:
|
|
localReads.addText(self.comment("local read b"))
|
|
bufferIdx = plrIdx*self.numIterPerCoalescedReadB
|
|
if self.packDTVB:
|
|
# DTV + pack case, offset bufferIdx for local read packing instructions
|
|
bufferIdx = plrIdxDTV*self.numIterPerCoalescedReadB + vregSetIdxLR * kernel["LoopIters"]
|
|
localReadCodeB, packCodeB = self.localReadDo(kernel, bufferIdx, iui*self.numReadsIterCoalescedB, 0, tensorParametersB)
|
|
if not needExtraLocalReadDo:
|
|
localReads.addCode(localReadCodeB)
|
|
pack[plrIdx*self.numIterPerCoalescedReadB].addCode(packCodeB)
|
|
if needExtraLocalReadDo and (self.packDTVA or self.packDTVB):
|
|
# packDTV + needExtraLocalReadDo, need to update pack code to flip vregSetIdx
|
|
pack[plrIdx*self.numIterPerCoalescedReadB].addCode(packCodeB)
|
|
if (not isResetLroIter or iui != kernel["InnerUnroll"]-1):
|
|
if doReadA:
|
|
localReads.addText(self.comment("local read increment a"))
|
|
localReads.addText(self.localReadInc(kernel, iui, tensorParametersA))
|
|
if doReadB:
|
|
localReads.addText(self.comment("local read increment b"))
|
|
localReads.addText(self.localReadInc(kernel, iui, tensorParametersB))
|
|
|
|
if not isLastLoop:
|
|
if kernel["PrefetchGlobalRead"]:
|
|
# put barrier at localWriteEndIter+1
|
|
if u == localWriteEndIter+1 or (u == (localWriteEndIter+1)%kernel["LoopIters"] and kernel["ScheduleIterAlg"] == 2):
|
|
if self.enable["Wait"]:
|
|
# skip local write wait if DirectToVgpr + DirectToLds is enabled
|
|
if not kernel["NoLdsWriteCode"]:
|
|
waitLWCode.addCode(self.wait(kernel, tensorParametersA, tensorParametersB, -1, 0, -1, "3wait for local write"))
|
|
if (kernel["DirectToVgprA"] or kernel["DirectToVgprB"]) and (kernel["DirectToLdsA"] or kernel["DirectToLdsB"]):
|
|
# DirectToVgpr + DirectToLds case, add waitcnt vmcnt before thread sync
|
|
# Except for PGR=2 and Load C (StoreCInUnroll) case. In that case, Load C is executed after necessary Load A and B.
|
|
# Wait for Load C is already done here in PGR=2 case.
|
|
needLoadC = kernel["StoreCInUnroll"] and (not kernel["AtomicAddC"]) and kernel["ProblemType"]["UseBeta"]
|
|
if not (kernel["PrefetchGlobalRead"]==2 and needLoadC):
|
|
retStr = self.getWaitcntCodeForDirectToVgpr(kernel, localWriteEndIter, u, firstIter=False, beforeBarrier=True)
|
|
waitLWCode.addCode(retStr)
|
|
if self.enable["Sync"]:
|
|
if kernel["PrefetchGlobalRead"]==2 and (kernel["DirectToLdsA"] and kernel["DirectToLdsB"]):
|
|
# PGR=2 and DTLA+B case, wait for global read needs to be added (wait is not generated with local write)
|
|
syncCode.addCode(self.wait(kernel, tensorParametersA, tensorParametersB, 0, -1, -1, "wait for global read with lds"))
|
|
syncCode.addCode(self.syncThreads(kernel))
|
|
|
|
if isSwapAndResetLwoIter: # ResetLroIter
|
|
if self.enable["LocalWrite"]:
|
|
# local write for next iter, used to have local writes here
|
|
pointerLWCode.addText(self.comment("local write swap offsets a"))
|
|
pointerLWCode.addText(self.localWriteSwapOffsets(kernel, expand, tensorParametersA))
|
|
pointerLWCode.addText(self.comment("local write swap offsets b"))
|
|
pointerLWCode.addText(self.localWriteSwapOffsets(kernel, expand, tensorParametersB))
|
|
pointerLWCode.addText(self.localWriteInitPointers(kernel, tensorParametersA))
|
|
pointerLWCode.addText(self.localWriteInitPointers(kernel, tensorParametersB))
|
|
|
|
if isSwapLroIter: # ResetLroIter
|
|
if self.enable["LocalRead"]:
|
|
# Swap, reset, or increment the LRO:
|
|
# force internalPointerSwap = False in NGLL case
|
|
internalPointerSwap = expand and not isNGLL
|
|
pointerLRCode.addText(self.comment("local read swap offsets a"))
|
|
pointerLRCode.addText(self.localReadSwapOffsets(kernel, internalPointerSwap, tensorParametersA))
|
|
pointerLRCode.addText(self.comment("local read swap offsets b"))
|
|
pointerLRCode.addText(self.localReadSwapOffsets(kernel, internalPointerSwap, tensorParametersB))
|
|
|
|
if isResetLroIter: # ResetLroIter
|
|
if self.enable["LocalRead"]:
|
|
pointerLRCode.addText(self.comment("local read init pointers a"))
|
|
pointerLRCode.addText(self.localReadInitPointers(kernel, tensorParametersA))
|
|
pointerLRCode.addText(self.comment("local read init pointers b"))
|
|
pointerLRCode.addText(self.localReadInitPointers(kernel, tensorParametersB))
|
|
|
|
# we initiate lgkmcnt to 0, then assigning it correct value in makeSubIterSchedule()
|
|
if self.enable["Wait"]:
|
|
if self.getConditionToSkipLocalReadWriteWait(kernel, isPap, u, kernel["LoopIters"] - 1):
|
|
waitCode = self.wait(kernel, tensorParametersA, tensorParametersB, \
|
|
-1, 0, 0, \
|
|
"wait for prior local read local write")
|
|
# DirectToVgpr case, wait for global read as well as local read/write
|
|
if kernel["DirectToVgprA"] or kernel["DirectToVgprB"]:
|
|
# not generate wait here
|
|
# 1) local write code in previous u (u-1) has waitcnt vmcnt
|
|
prevVmcnt = False
|
|
prevLocalWrite = ""
|
|
if (u > 0):
|
|
prevLocalWrite = ' '.join([str(x) for x in self.perIterLocalWriteCode[u-1].flatitems()])
|
|
prevVmcnt = "vmcnt" in prevLocalWrite
|
|
if not prevVmcnt:
|
|
retStr = self.getWaitcntCodeForDirectToVgpr(kernel, localWriteEndIter, u, False, isPap or isNGLL, NLLlast=NLLlast)
|
|
kl.append(retStr)
|
|
|
|
# generate StoreCInUnroll post loop if it is enabled (only PAP case (but not NGLL))
|
|
if u == localWriteEndIter+1:
|
|
if kernel["StoreCInUnrollPostLoop"] and isPap and not isNGLL:
|
|
kl.append(self.generateStoreInUnrollPostLoop(kernel, isOptNLL, isDTVodd))
|
|
|
|
luIdx = (u) % (self.numVgprBuffer+1) # local to use for MACs
|
|
if self.enable["MAC"]:
|
|
if kernel["EnableMatrixInstruction"]:
|
|
if ((uIdx+1) == kernel["LoopIters"]*kernel["DepthULdsDivisor"]) and \
|
|
(kernel["StoreCInUnroll"]):
|
|
lastuIdx = (isOptNLL or self.enableSingleNLLOpt) and not isNGLL # do not apply lastuIdx for not isOptNLL case
|
|
macIterCode.addCode(self.mfmaIter(kernel, u, kernel["InnerUnroll"], vregSetIdxMFMA,lastuIdx,tail=needTailCode))
|
|
else:
|
|
macIterCode.addCode(self.macIter(kernel, luIdx, kernel["InnerUnroll"], True ))
|
|
|
|
subIterCode = self.makeSubIterSchedule(kernel, localReads, \
|
|
u, pointerLWCode, pointerLRCode, waitCode, macIterCode, waitLWCode, syncCode, pack[luIdx], isDTVodd, NLLlast)
|
|
kl.append(subIterCode)
|
|
# vgpr.checkin for all the checked-out vgpr in LocalRead
|
|
for item in list(pack[luIdx].items()):
|
|
if item.tempVgpr != None:
|
|
self.vgprPool.checkIn(item.tempVgpr)
|
|
item.tempVgpr = None
|
|
pack[luIdx] = Code.Module()
|
|
|
|
# tail loop in NoLoadLoop case, generate close loop code for TailLoop here (except for last loop iteration)
|
|
if needTailCode:
|
|
finalLoop = (u == kernel["LoopIters"] - 1)
|
|
skipJump = finalLoop or self.noEarlyExitForTailLoopInNLL
|
|
kl.append(self.closeLoop(kernel, -1, finalLoop, 1, oddLabel=isDTVodd, skipCondJumpCounter=u, isOptNLL=isOptNLL, skipJump=skipJump))
|
|
|
|
##############################################################################
|
|
# noLoadLoop
|
|
# Create the no load loop (NLL)
|
|
#
|
|
# isOptNLL : the NLL is to be optimized for the alpha=1 and non-edge case
|
|
##############################################################################
|
|
def noLoadLoop( self, kernel, tensorParametersA, tensorParametersB, isOptNLL, isPap, isNGLL, pack ):
|
|
kl = []
|
|
if isNGLL:
|
|
LoopNameComment = "NoGlobalLoadLoop"
|
|
else:
|
|
LoopNameComment = "NoLoadLoop"
|
|
if isOptNLL:
|
|
PAPcomment = "Opt. %s %s PAP - Begin " % (LoopNameComment, "With" if isPap else "Without")
|
|
else:
|
|
PAPcomment = "Ord. %s - Begin " % (LoopNameComment)
|
|
kl.append(self.comment3("%s")%PAPcomment)
|
|
NLLfirst = True
|
|
NLLlast = True
|
|
if kernel["PrefetchGlobalRead"] == 2:
|
|
# PGR=2 case NoLoadLoop(NLL) is generated twice
|
|
# we need to distinguish them to generate proper code at each NLL
|
|
if isNGLL:
|
|
NLLlast = False
|
|
else:
|
|
# PGR=2 and not isNGLL means second NoLoadLoop for PGR2.
|
|
# Need to avoid generating duplicated code which is already generated in NGLL(first NoLoadLoop for PGR=2)
|
|
NLLfirst = False
|
|
if isNGLL:
|
|
self.perIterLocalWriteCode = self.perIterLocalWriteCodeNGLL
|
|
self.perIterLocalWriteCanSkip = [ 0 for i in range (kernel["LoopIters"]) ]
|
|
#else:
|
|
if not isNGLL or isPap:
|
|
self.dtlsM0UpdateACode = Code.StructuredModule()
|
|
self.globalReadACode = Code.StructuredModule() # empty
|
|
self.dtlsM0UpdateBCode = Code.StructuredModule()
|
|
self.globalReadBCode = Code.StructuredModule() # empty
|
|
self.globalReadIncrements = Code.Module()
|
|
self.globalReadIncrements.addCode(Code.Module("globalReadIncrementA"))
|
|
self.globalReadIncrements.addCode(Code.Module("globalReadIncrementB"))
|
|
self.localWriteACode = Code.Module()
|
|
self.localWriteBCode = Code.Module()
|
|
|
|
# the scheduled GlobalRead,Inc code of PAP is inside openSumAtLeastUnroll (if PAP=on)
|
|
isPapTmp = isPap
|
|
if kernel["PrefetchGlobalRead"]==2:
|
|
# PGR=2 case, set isPap only if isNGLL is True. This is to generate NewTile code at NGLL in PAP + PGR=2 case
|
|
isPapTmp = isPap and not isNGLL
|
|
kStrOpenSum = self.openSumAtLeastUnroll(kernel, prefetch=False, isOptNLL=isOptNLL, isPap=isPapTmp)
|
|
|
|
#if self.prefetchAcrossPersistent and kernel["PrefetchAcrossPersistentMode"] == 1 and isPap:
|
|
if self.prefetchAcrossPersistent and isPap:
|
|
#if self.prefetchAcrossPersistent and isPap \
|
|
# and (kernel["PrefetchAcrossPersistentMode"] == 0 or isOptNLL):
|
|
kStr = ""
|
|
#kStr += str(self.openPrefetchAcrossPersistent(kernel, isOptNLL=False, useBufferOOB=True))
|
|
# For PAPMode 1, using isOptNLL true to generate prefetch code
|
|
|
|
if kernel["PrefetchAcrossPersistentMode"] == 0:
|
|
# generate openSumAtLeastUnroll code here
|
|
kStr += kStrOpenSum
|
|
kStrOpenSum = "" # empty OpenSum str to avoid inserting it again
|
|
|
|
# isPap and kernel["PrefetchAcrossPersistentMode"] == 1 and isOptNLL==False,
|
|
# no need to append NewTile code because it is already generated in OptNLL code
|
|
# also, NGLL second NoLoadLoop case, we do not append code for NewTile
|
|
forceNoTileCode = False
|
|
if (isOptNLL==False or (not NLLfirst)):
|
|
forceNoTileCode = True
|
|
# PGR=2 and last loop case, we do not need GlobalRead code
|
|
forceNoGRCode = False
|
|
if kernel["PrefetchGlobalRead"] == 2 and NLLlast:
|
|
forceNoGRCode = True
|
|
|
|
newTileCodes, _ = self.setupNewTile(kernel, self.tPA, self.tPB, isPap=True, isOptNLL=True, \
|
|
forceNoTileCode=forceNoTileCode, forceNoGRCode = forceNoGRCode)
|
|
codes = '\n'.join([str(x) for x in newTileCodes])
|
|
kStr += codes
|
|
# openPrefetchAcrossPersistent should be after newTileCodes to set correct values to ShadowLimit
|
|
# also, NGLL second NoLoadLoop case, we do not append code for Open/Close PAP
|
|
if isOptNLL:
|
|
if NLLfirst:
|
|
kStr += str(self.openPrefetchAcrossPersistent(kernel, isOptNLL=False, useBufferOOB=True))
|
|
kStr += str(self.closePrefetchAcrossPersistent(kernel, isOptNLL=False, useBufferOOB=True))
|
|
kl.append(kStr)
|
|
|
|
# skip generating OpenSum code here for SingleNLLOpt
|
|
if not (isOptNLL and self.enableSingleNLLOpt):
|
|
kl.append(kStrOpenSum)
|
|
kStrOpenSum = "" # empty OpenSum str to avoid inserting it again
|
|
|
|
# re-calculate loop counter for tailLoopInNLL (without noEarlyExitForTailLoopInNLL)
|
|
if NLLlast and self.tailLoopInNLL and not self.noEarlyExitForTailLoopInNLL:
|
|
kl.append(self.calculateLoopNumIter(kernel, -1, False))
|
|
|
|
if not self.numItersPLR:
|
|
if self.enable["Wait"]:
|
|
if kernel["DirectToLdsA"] or kernel["DirectToLdsB"]:
|
|
kl.append(self.wait(kernel, tensorParametersA, tensorParametersB, 0, -1, -1, "10wait for global read"))
|
|
# TODO: need to check if we correctly checked-in the temp VGPR used for Int8 LocalWrite (uDu, PGR=2)
|
|
kl.append(self.wait(kernel, tensorParametersA, tensorParametersB, -1, 0, -1, "4wait for local write"))
|
|
if self.enable["Sync"]:
|
|
kl.append(self.syncThreads(kernel))
|
|
|
|
# if DirectToVgpr and ASEM/GSU is not multiple of DepthU*2, generate noLoadLoopBody twice for odd and even exit separately
|
|
if self.generateDTVodd:
|
|
# generate additional No Load Loop Body code for odd case (to use the other Vreg set for DirectToVgpr)
|
|
# 1. generate odd check
|
|
name = ""
|
|
if isNGLL:
|
|
name += "NoGlobalLoadLoop"
|
|
else:
|
|
name += "NoLoadLoop"
|
|
if isOptNLL:
|
|
name += "Opt"
|
|
else:
|
|
name += "Ord"
|
|
kl.append(self.openOddNoLoadLoopForDTV(kernel, isNGLL, name))
|
|
# 2. generate no Load Loop Body code for odd
|
|
# backup
|
|
self.saveLocalPointers(kernel)
|
|
# copy pack
|
|
if isNGLL or (self.packDTVA or self.packDTVB):
|
|
# NGLL or pack DTV case, no deep copy for pack
|
|
# pack code for local prefetch is generated in noLoadLoopBody and used for DTV even
|
|
deepCopyPack = pack
|
|
else:
|
|
# deepCopy packCode for OptNLL noLoadLoop
|
|
deepCopyPack = copy.deepcopy(pack)
|
|
# keep StoreCInUnroll related code for the next noLoadLoop
|
|
if kernel["StoreCInUnroll"]:
|
|
self.backupStoreCInUnrollRelatedCode()
|
|
self.noLoadLoopBody(kernel, tensorParametersA, tensorParametersB, kl, deepCopyPack, isOptNLL, isPap, isNGLL, NLLfirst, NLLlast, isDTVodd=True)
|
|
# restore
|
|
self.restoreLocalPointers(kernel)
|
|
# restore StoreCInUnroll related code
|
|
if kernel["StoreCInUnroll"]:
|
|
self.restoreStoreCInUnrollRelatedCode()
|
|
# 3. PAP enabled and isLast and odd code case, the last global load for DirectToVgpr is the seconde reg set.
|
|
# Need to copy to the first set for the next PK loop
|
|
if isPap and NLLlast:
|
|
kl.append(self.getWaitcntCodeForDirectToVgpr(kernel, 0, 0, False, oddLast=True))
|
|
kl.append(self.generateOddEndVgprCopyForDTV(kernel))
|
|
# 4. generate even start label
|
|
kl.append(self.closeOddNoLoadLoopForDTV(kernel, isNGLL, name))
|
|
# 5. generate no Load Loop Body code for odd
|
|
# need to re-initialize perIterLocalWriteCanSkip to avoid having incorrect lgkmcnt
|
|
self.perIterLocalWriteCanSkip = [ 0 for i in range (kernel["LoopIters"]) ]
|
|
self.noLoadLoopBody(kernel, tensorParametersA, tensorParametersB, kl, pack, isOptNLL, isPap, isNGLL, NLLfirst, NLLlast)
|
|
# 6. generate even end label
|
|
kl.append(self.generateEvenEndLabeNoLoadLoopForDTV(kernel, isNGLL, name))
|
|
else:
|
|
# generate no Load Loop Body code
|
|
self.noLoadLoopBody(kernel, tensorParametersA, tensorParametersB, kl, pack, isOptNLL, isPap, isNGLL, NLLfirst, NLLlast)
|
|
|
|
# tail loop in NLL and early exit case, need to wait for all prefetch local read (and global read for DirectToVgpr)
|
|
if NLLlast and self.tailLoopInNLL and (not self.noEarlyExitForTailLoopInNLL):
|
|
if self.enable["Wait"]:
|
|
kl.append(self.wait(kernel, tensorParametersA, tensorParametersB, -1, -1, 0, "13wait for remaining local read for tail loop in NLL"))
|
|
if kernel["DirectToVgprA"] or kernel["DirectToVgprB"]:
|
|
kl.append(self.wait(kernel, tensorParametersA, tensorParametersB, 0, -1, -1, "14wait for remaining DirectToVgpr global read for tail loop in NLL"))
|
|
|
|
if NLLlast and isPap:
|
|
# reset or swap local write offset
|
|
# If DirectToLds is True, first LDS buffer is already used by lds global read and offset already points first one
|
|
# Swap/Reset is not necessary
|
|
# If DirectToLds is False, first LDS buffer is no used yet, need reset.
|
|
if kernel["ExpandPointerSwap"]:
|
|
if not kernel["DirectToLdsA"]:
|
|
kl.append(self.comment("local write reset offsets a"))
|
|
kl.append(self.localWriteResetOffsets(kernel, False, tensorParametersA))
|
|
if not kernel["DirectToLdsB"]:
|
|
kl.append(self.comment("local write reset offsets b"))
|
|
kl.append(self.localWriteResetOffsets(kernel, False, tensorParametersB))
|
|
kl.append(self.localReadResetOffsets(kernel, tensorParametersA))
|
|
kl.append(self.localReadResetOffsets(kernel, tensorParametersB))
|
|
|
|
# add OpenSum code here if it is not empty
|
|
if kStrOpenSum != "":
|
|
kl.append(kStrOpenSum)
|
|
|
|
# Close code is necessary for both first and last (NGLL case(=NLLfirst) needs label)
|
|
kl.append(self.closeSumAtLeastUnroll(kernel, prefetch=False, isOptNLL=isOptNLL, isPap=isPap, isNGLL=isNGLL))
|
|
|
|
return kl
|
|
|
|
##############################################################################
|
|
# Loop Body
|
|
##############################################################################
|
|
def loopBody( self, kernel, tensorParametersA, tensorParametersB, kl, pack, lc, loopCopies, finalLoop, firstIter=False ):
|
|
expand = kernel["ExpandPointerSwap"]
|
|
# firstIter flag for waitcnt
|
|
# if useInitAccVgprOpt is not used, waitcnt of the first iteration should be same as firstIter
|
|
# (does not include storeC from the previous PK loop)
|
|
firstIterForWait = firstIter or ((not self.useInitAccVgprOpt) and lc == 0)
|
|
|
|
# generate storeC code for StoreCInUnroll (need to call for not StoreCInUnroll case as well)
|
|
self.generateStoreCCodeInUnrollLoop(kernel, lc & 1)
|
|
|
|
# not generate openLoop for firstIter
|
|
if not firstIter:
|
|
kl.append(self.comment3("Unrolled Loop %u/%u - Begin" % (lc+1, loopCopies)))
|
|
kl.append(self.openLoopCopy(kernel, lc))
|
|
if kernel["PrefetchGlobalRead"] and not self.numItersPLR and not kernel["ScheduleIterAlg"] == 2:
|
|
if self.enable["Wait"]:
|
|
if kernel["DirectToLdsA"] or kernel["DirectToLdsB"]:
|
|
kl.append(self.wait(kernel, tensorParametersA, tensorParametersB, 0, -1, -1, "11wait for global read"))
|
|
kl.append(self.wait(kernel, tensorParametersA, tensorParametersB, 1, 0, -1, "1wait for local write"))
|
|
if self.enable["Sync"]:
|
|
kl.append(self.syncThreads(kernel, "4sync for global read"))
|
|
|
|
kl.append(self.comment("Begin Each Unroll: Check VGPR.checkin for INT8 LW"))
|
|
|
|
if self.enable["GlobalRead"]:
|
|
# if DirectToVgpr is enabled and swapGlobalRead is true, swap the order of global read (B->A)
|
|
tensorParameters1st = tensorParametersA
|
|
tensorParameters2nd = tensorParametersB
|
|
tc1 = 'A'
|
|
tc2 = 'B'
|
|
if self.isSwapGlobalReadOrderForDtvOrDtl(kernel):
|
|
tensorParameters1st, tensorParameters2nd = tensorParameters2nd, tensorParameters1st
|
|
tc1, tc2 = tc2, tc1
|
|
# unrolled loop: global read A, B
|
|
# M0 update for directToLds
|
|
vregSetIdxGR = 0
|
|
if (kernel["DirectToVgpr%s"%tc1]):
|
|
vregSetIdxGR = (kernel["PrefetchGlobalRead"] + lc ) % 2 # toggle vreg set for DirectToVgpr.
|
|
self.dtlsM0UpdateACode = self.directToLdsM0Update(kernel, 1, tensorParameters1st, usePlaceHolder=True)
|
|
self.globalReadACode = self.globalReadDo(kernel, 1, tensorParameters1st, vregSetIdxGR)
|
|
vregSetIdxGR = 0
|
|
if (kernel["DirectToVgpr%s"%tc2]):
|
|
vregSetIdxGR = (kernel["PrefetchGlobalRead"] + lc ) % 2 # toggle vreg set for DirectToVgpr.
|
|
self.dtlsM0UpdateBCode = self.directToLdsM0Update(kernel, 1, tensorParameters2nd, usePlaceHolder=True)
|
|
self.globalReadBCode = self.globalReadDo(kernel, 1, tensorParameters2nd, vregSetIdxGR)
|
|
else:
|
|
self.dtlsM0UpdateACode = Code.StructuredModule()
|
|
self.globalReadACode = Code.StructuredModule() # empty
|
|
self.dtlsM0UpdateBCode = Code.StructuredModule()
|
|
self.globalReadBCode = Code.StructuredModule() # empty
|
|
|
|
if self.enable["GlobalReadInc"]:
|
|
# unrolled loop: increment global read addresses
|
|
self.globalReadIncrements = self.globalReadIncrementAB(kernel, self.unrollIdx, 0)
|
|
else:
|
|
self.globalReadIncrements = Code.Module()
|
|
self.globalReadIncrements.addCode(Code.Module("globalReadIncrementA"))
|
|
self.globalReadIncrements.addCode(Code.Module("globalReadIncrementB"))
|
|
|
|
if self.enable["LocalWrite"] and not kernel["NoLdsWriteCode"]:
|
|
self.localWriteACode = self.localWriteDo(kernel, tensorParametersA)
|
|
self.localWriteBCode = self.localWriteDo(kernel, tensorParametersB)
|
|
else:
|
|
self.localWriteACode = Code.Module()
|
|
self.localWriteBCode = Code.Module()
|
|
|
|
# localWriteEndIter is used to determine which iteration to put sync
|
|
# if PGR=0, GR,LW,sync,LR will put at front of loop.
|
|
localWriteEndIter = kernel["LoopIters"] - self.numItersPLR - 1
|
|
|
|
# Schedule the global read, global read inc, and writes:
|
|
unrollLoopHeaderCodeScheduled = False
|
|
if not kernel["PrefetchGlobalRead"]:
|
|
unrollLoopHeaderCodeScheduled = True
|
|
self.makeSchedule(kernel, tensorParametersA, tensorParametersB, localWriteEndIter, firstIter=firstIter)
|
|
kl.append(str(self.unrollLoopHeaderCode))
|
|
|
|
# if not prefetch global, localWrite before mac's
|
|
if not kernel["PrefetchGlobalRead"]:
|
|
# unrolled loop: local write A, B
|
|
if self.enable["Wait"]:
|
|
kl.append(self.wait(kernel, tensorParametersA, tensorParametersB, 0, -1, -1, "5wait for global read"))
|
|
if self.enable["Sync"]:
|
|
kl.append(self.syncThreads(kernel, "PGR=0, prior iter done reading lds"))
|
|
if self.enable["LocalWrite"] and not kernel["NoLdsWriteCode"]:
|
|
kl.append(self.comment("local write a"))
|
|
tempLWCodeModA = self.localWriteDo(kernel, tensorParametersA)
|
|
kl.append(tempLWCodeModA)
|
|
kl.append(self.comment("local write b"))
|
|
tempLWCodeModB = self.localWriteDo(kernel, tensorParametersB)
|
|
kl.append(tempLWCodeModB)
|
|
if self.enable["Wait"]:
|
|
kl.append(self.wait(kernel, tensorParametersA, tensorParametersB, -1, 0, -1, "2prefetch wait for local write"))
|
|
if self.enable["Sync"]:
|
|
kl.append(self.syncThreads(kernel))
|
|
# debug Local state
|
|
"""
|
|
kl.append(" /* print Local state */" + self.endLine)
|
|
kl.append(" for (unsigned int i = serial; i < LDS_NUM_ELEMENTS; i+=NUM_THREADS) {%s" % self.endLine)
|
|
kl.append(" printf(\\\"localMemory[%%06u] = %%.0f\\\\n\\\", i, localMemory[i]);%s" )
|
|
% self.endLine
|
|
kl.append(" }" + self.endLine)
|
|
"""
|
|
|
|
# unrolled loop: prefetch local
|
|
if self.numItersPLR and not kernel["PrefetchGlobalRead"]:
|
|
if self.enable["LocalRead"]:
|
|
for plrIdx in range(0, self.numItersPLR):
|
|
pack[plrIdx] = Code.Module()
|
|
for iui in range(0,kernel["InnerUnroll"]):
|
|
if iui*self.numReadsIterCoalescedA < kernel["InnerUnroll"]:
|
|
kl.append(self.comment("prefetch local a"))
|
|
localReadCodeA, packCodeA = self.localReadDo(kernel, plrIdx*self.numIterPerCoalescedReadA, iui*self.numReadsIterCoalescedA, 0, tensorParametersA)
|
|
kl.append(localReadCodeA)
|
|
pack[plrIdx].addCode(packCodeA)
|
|
if iui*self.numReadsIterCoalescedB < kernel["InnerUnroll"]:
|
|
kl.append(self.comment("prefetch local b"))
|
|
localReadCodeB, packCodeB = self.localReadDo(kernel, plrIdx*self.numIterPerCoalescedReadB, iui*self.numReadsIterCoalescedB, 0, tensorParametersB)
|
|
kl.append(localReadCodeB)
|
|
pack[plrIdx].addCode(packCodeB)
|
|
if iui*self.numReadsIterCoalescedA < kernel["InnerUnroll"]:
|
|
kl.append(self.comment1("local read increment a"))
|
|
kl.append(self.localReadInc(kernel, iui, tensorParametersA))
|
|
if iui*self.numReadsIterCoalescedB < kernel["InnerUnroll"]:
|
|
kl.append(self.comment1("local read increment b"))
|
|
kl.append(self.localReadInc(kernel, iui, tensorParametersB))
|
|
|
|
pflr = self.numItersPLR # how many pf already done above
|
|
# vregSetIdx for DTV
|
|
vregSetIdxMFMA = lc
|
|
|
|
############################################################################
|
|
# unrolled loop: mac iterations
|
|
############################################################################
|
|
|
|
# double/quadruple the number of compute loop for each DepthU's worth of data read
|
|
for uIdx in range(0, kernel["LoopIters"]*kernel["DepthULdsDivisor"]):
|
|
u = uIdx % kernel["LoopIters"] # u: index in compute loop (in contrast to the notion of global read loop)
|
|
uDu = uIdx // kernel["LoopIters"] # uDu: index of compute loop
|
|
if u==0: # if at start of subloop...
|
|
# ...update local write code
|
|
if self.enable["LocalWrite"] and not kernel["NoLdsWriteCode"]:
|
|
self.localWriteACode = self.localWriteDo(kernel, tensorParametersA, (uDu+1)%kernel["DepthULdsDivisor"]) # local write in loopcnt N targets data for loopcnt N+1
|
|
self.localWriteBCode = self.localWriteDo(kernel, tensorParametersB, (uDu+1)%kernel["DepthULdsDivisor"])
|
|
else:
|
|
self.localWriteACode = Code.Module()
|
|
self.localWriteBCode = Code.Module()
|
|
|
|
# TODO schedule waitcnt/barrier in makeSubIterSchedule()
|
|
if kernel["PrefetchGlobalRead"] and kernel["LoopIters"] in [1, 2] and uDu > 0:
|
|
if self.enable["Wait"]:
|
|
kl.append(self.wait(kernel, tensorParametersA, tensorParametersB, 1, 0, -1, "wait for local write"))
|
|
if self.enable["Sync"]:
|
|
kl.append(self.syncThreads(kernel, "sync for local read after write"))
|
|
|
|
if not unrollLoopHeaderCodeScheduled:
|
|
self.makeSchedule(kernel, tensorParametersA, tensorParametersB, localWriteEndIter, uDu, firstIter=firstIter, lastLoop=False, lastLc=(lc==loopCopies-1))
|
|
kl.append(str(self.unrollLoopHeaderCode))
|
|
|
|
# for PGR=0 where generator can't schedule the instructions (yet),
|
|
# we duplicate the local write codegen and append to string list directly
|
|
if not kernel["PrefetchGlobalRead"]:
|
|
doWrite = False
|
|
if uDu<kernel["DepthULdsDivisor"]-1 and u==kernel["LoopIters"]-self.numItersPLR:
|
|
doWrite = True
|
|
writeForNextLoop = 1
|
|
if uDu>0 and self.numItersPLR==0 and u==0:
|
|
assert doWrite==False # should be exclusive with the previous condition
|
|
doWrite = True
|
|
writeForNextLoop = 0
|
|
# unrolled loop: local write A, B
|
|
if doWrite:
|
|
if self.enable["Wait"]:
|
|
kl.append(self.wait(kernel, tensorParametersA, tensorParametersB, -1, -1, 0, "5wait for local read"))
|
|
if self.enable["Sync"]:
|
|
kl.append(self.syncThreads(kernel, "PGR=0, prior iter done reading lds"))
|
|
if self.enable["LocalWrite"] and not kernel["NoLdsWriteCode"]:
|
|
kl.append(self.comment("local write a"))
|
|
tempLWCodeModA = self.localWriteDo(kernel, tensorParametersA, (uDu+writeForNextLoop)%kernel["DepthULdsDivisor"])
|
|
kl.append(tempLWCodeModA)
|
|
kl.append(self.comment("local write b"))
|
|
tempLWCodeModB = self.localWriteDo(kernel, tensorParametersB, (uDu+writeForNextLoop)%kernel["DepthULdsDivisor"])
|
|
kl.append(tempLWCodeModB)
|
|
if self.enable["Wait"]:
|
|
kl.append(self.wait(kernel, tensorParametersA, tensorParametersB, -1, 0, -1, "2prefetch wait for local write"))
|
|
if self.enable["Sync"]:
|
|
kl.append(self.syncThreads(kernel))
|
|
|
|
# which loop iteration to reset the LRO,
|
|
# note if PLR=0, isResetLroIter is False for all u
|
|
isResetLroIter = (u == localWriteEndIter)
|
|
isSwapAndResetLwoIter = isResetLroIter
|
|
isSwapLroIter = isResetLroIter
|
|
if kernel["ScheduleIterAlg"] == 3:
|
|
isSwapAndResetLwoIter = (u == self.lwEndMfmaIndex//(self.numMfmaPerIter))
|
|
extraComment = ""
|
|
if kernel.enabledSplitLDS:
|
|
extraComment += f" (uDu={uDu}) "
|
|
if isResetLroIter:
|
|
extraComment += " (reset local read pointers iteration) "
|
|
if isSwapAndResetLwoIter:
|
|
extraComment += " (swap and reset local write pointers iteration) "
|
|
if isSwapLroIter:
|
|
extraComment += " (swap local read pointers iteration) "
|
|
|
|
kl.append(self.comment("iter %u%s"%(u,extraComment)))
|
|
plrIdx = ((u+pflr) % (self.numVgprBuffer+1)) % kernel["LoopIters"]
|
|
plrIdxDTV = (u+pflr) % kernel["LoopIters"]
|
|
|
|
localReads = Code.Module()
|
|
localReadsA = Code.Module()
|
|
localReadsB = Code.Module()
|
|
|
|
pointerLWCode = Code.Module()
|
|
pointerLRCode = Code.Module()
|
|
waitCode = Code.Module() # may be overwritten (not added to) below
|
|
macIterCode = Code.Module()
|
|
waitLWCode = Code.Module()
|
|
syncCode = Code.Module()
|
|
|
|
# vregSetIdx for DTV
|
|
vregSetIdxLR = vregSetIdxMFMA
|
|
if kernel["LoopIters"] > 1 and u == kernel["LoopIters"] - 1:
|
|
# use next vregSet for the last loopIter (exception: LoopIters==1)
|
|
# LoopIters==1 case, local read is for the current iteration and not for the next iteration
|
|
vregSetIdxLR = (vregSetIdxLR + 1) % loopCopies
|
|
# final loop case, use vregSetIdx for noLoadLoop (NGLL(PGR2) or NLL(PGR1))
|
|
if finalLoop:
|
|
# finalLoop case, this is for NoLoadLoop (NGLL(PGR2) or NLL(PGR1))
|
|
# PGR2 case, next is NGLL. Use first set
|
|
# PGR1 case, next is NLL. Use second set
|
|
vregSetIdxLR = 0 if kernel["PrefetchGlobalRead"] == 2 else 1
|
|
# flip vregSetIdx if isDTVodd is True
|
|
if self.generateDTVodd:
|
|
vregSetIdxLR = 1 - vregSetIdxLR
|
|
|
|
if self.enable["LocalRead"]:
|
|
hasLiveLdsData = kernel["PrefetchGlobalRead"] or (uDu < kernel["DepthULdsDivisor"]-1)
|
|
# reads for current loop are done in previous iteration because of wider local read
|
|
doReadA = (u < kernel["LoopIters"]/self.numIterPerCoalescedReadA - self.numItersPLR)
|
|
doReadB = (u < kernel["LoopIters"]/self.numIterPerCoalescedReadB - self.numItersPLR)
|
|
# reads for next loop
|
|
doReadA = doReadA or (hasLiveLdsData and u > localWriteEndIter)
|
|
doReadB = doReadB or (hasLiveLdsData and u > localWriteEndIter)
|
|
# disable LocalRead if DirectToVgpr is enabled
|
|
for iui in range(0,kernel["InnerUnroll"]):
|
|
doReadA = doReadA and iui*self.numReadsIterCoalescedA < kernel["InnerUnroll"]
|
|
doReadB = doReadB and iui*self.numReadsIterCoalescedB < kernel["InnerUnroll"]
|
|
if doReadA:
|
|
localReads.addText(self.comment("local read a"))
|
|
bufferIdx = plrIdx*self.numIterPerCoalescedReadA
|
|
if self.packDTVA:
|
|
# DTV + pack case, offset bufferIdx for local read packing instructions
|
|
bufferIdx = plrIdxDTV*self.numIterPerCoalescedReadA + vregSetIdxLR * kernel["LoopIters"]
|
|
localReadCodeA, packCodeA = self.localReadDo(kernel, bufferIdx, iui*self.numReadsIterCoalescedA, 0, tensorParametersA)
|
|
localReads.addCode(localReadCodeA)
|
|
localReadsA.addCode(localReadCodeA)
|
|
pack[plrIdx*self.numIterPerCoalescedReadA].addCode(packCodeA)
|
|
if doReadB:
|
|
localReads.addText(self.comment("local read b"))
|
|
bufferIdx = plrIdx*self.numIterPerCoalescedReadB
|
|
if self.packDTVB:
|
|
# DTV + pack case, offset bufferIdx for local read packing instructions
|
|
bufferIdx = plrIdxDTV*self.numIterPerCoalescedReadB + vregSetIdxLR * kernel["LoopIters"]
|
|
localReadCodeB, packCodeB = self.localReadDo(kernel, bufferIdx, iui*self.numReadsIterCoalescedB, 0, tensorParametersB)
|
|
localReads.addCode(localReadCodeB)
|
|
localReadsB.addCode(localReadCodeB)
|
|
pack[plrIdx*self.numIterPerCoalescedReadB].addCode(packCodeB)
|
|
# Don't increment the LRO if we are going to reset them below:
|
|
if not isResetLroIter or iui != kernel["InnerUnroll"]-1:
|
|
if doReadA:
|
|
localReads.addText(self.comment("local read increment a"))
|
|
localReads.addText(self.localReadInc(kernel, iui, tensorParametersA))
|
|
if doReadB:
|
|
localReads.addText(self.comment("local read increment b"))
|
|
localReads.addText(self.localReadInc(kernel, iui, tensorParametersB))
|
|
|
|
if kernel["PrefetchGlobalRead"]:
|
|
# wait code for DirectToVgpr
|
|
if kernel["DirectToVgprA"] or kernel["DirectToVgprB"]:
|
|
if self.enable["Wait"]:
|
|
# not generate wait here
|
|
# 1) for the first unroll with self.canOptimizePreLoopLWVmcnt = True
|
|
# 2) local write code in previous u (u-1) has waitcnt vmcnt
|
|
prevVmcnt = False
|
|
prevLocalWrite = ""
|
|
if (u > 0 and kernel["ScheduleIterAlg"] == 3):
|
|
for up in range(u):
|
|
prevLocalWrite += ' '.join([str(x) for x in self.perIterLocalWriteCode[up].flatitems()])
|
|
prevVmcnt = "vmcnt" in prevLocalWrite
|
|
if not (firstIter and u == 0 and self.canOptimizePreLoopLWVmcnt) and not prevVmcnt:
|
|
retStr = self.getWaitcntCodeForDirectToVgpr(kernel, localWriteEndIter, u, firstIterForWait)
|
|
kl.append(retStr)
|
|
# put barrier at localWriteEndIter+1
|
|
if u == localWriteEndIter+1 or (u == (localWriteEndIter+1)%kernel["LoopIters"] and kernel["ScheduleIterAlg"] == 2):
|
|
if self.enable["Wait"]:
|
|
if kernel["DirectToLdsA"] or kernel["DirectToLdsB"]:
|
|
# skip generating wait for global read again here in DirectToVgpr case or no DirectToVgpr + PGR=2
|
|
# no DTV and PGR=2 case, wait is generated at sync (barrier), which is before next local read
|
|
if not(kernel["DirectToVgprA"] or kernel["DirectToVgprB"]) and not kernel["PrefetchGlobalRead"]==2:
|
|
kl.append(self.wait(kernel, tensorParametersA, tensorParametersB, 0, -1, -1, "12wait for global read"))
|
|
else:
|
|
# DirectToVgpr + DirectToLds case, add waitcnt vmcnt before thread sync
|
|
# Except for PGR=2 and Load C case. In that case, Load C is executed after necessary Load A and B.
|
|
# Wait for Load C is already done here in PGR=2 case.
|
|
needLoadC = kernel["StoreCInUnroll"] and (not kernel["AtomicAddC"]) and kernel["ProblemType"]["UseBeta"]
|
|
if not (kernel["PrefetchGlobalRead"]==2 and needLoadC):
|
|
retStr = self.getWaitcntCodeForDirectToVgpr(kernel, localWriteEndIter, u, firstIterForWait, beforeBarrier=True)
|
|
waitLWCode.addCode(retStr)
|
|
# skip local write wait if DirectToVgpr + DirectToLds is enabled
|
|
# (no local write code. Global read wait for DirectToLds is already done)
|
|
if not kernel["NoLdsWriteCode"]:
|
|
waitLWCode.addCode(self.wait(kernel, tensorParametersA, tensorParametersB, -1, 0, -1, "3wait for local write"))
|
|
if self.enable["Sync"]:
|
|
if kernel["DirectToVgprA"] or kernel["DirectToVgprB"]:
|
|
if not (kernel["DirectToVgprA"] and kernel["DirectToVgprB"]):
|
|
# put only barrier for DirectToVgpr (to avoid generating waitcnt for global read)
|
|
# barrier is not necessary if both DirectToVgprA and B are enabled
|
|
syncCode.addCode(self.syncStr + self.endLine)
|
|
else:
|
|
syncCode.addCode(self.syncThreads(kernel))
|
|
|
|
if isSwapAndResetLwoIter: # ResetLroIter
|
|
if self.enable["LocalWrite"]:
|
|
# local write for next iter, used to have local writes here
|
|
pointerLWCode.addText(self.comment("local write swap offsets a"))
|
|
pointerLWCode.addText(self.localWriteSwapOffsets(kernel, expand, tensorParametersA))
|
|
pointerLWCode.addText(self.comment("local write swap offsets b"))
|
|
pointerLWCode.addText(self.localWriteSwapOffsets(kernel, expand, tensorParametersB))
|
|
pointerLWCode.addText(self.localWriteInitPointers(kernel, tensorParametersA))
|
|
pointerLWCode.addText(self.localWriteInitPointers(kernel, tensorParametersB))
|
|
|
|
if isSwapLroIter: # ResetLroIter
|
|
if self.enable["LocalRead"]:
|
|
# Swap, reset, or increment the LRO:
|
|
pointerLRCode.addText(self.comment("local read swap offsets a"))
|
|
pointerLRCode.addText(self.localReadSwapOffsets(kernel, expand, tensorParametersA))
|
|
pointerLRCode.addText(self.comment("local read swap offsets b"))
|
|
pointerLRCode.addText(self.localReadSwapOffsets(kernel, expand, tensorParametersB))
|
|
|
|
if isResetLroIter: # ResetLroIter
|
|
if self.enable["LocalRead"]:
|
|
pointerLRCode.addText(self.comment("local read init pointers a"))
|
|
pointerLRCode.addText(self.localReadInitPointers(kernel, tensorParametersA))
|
|
pointerLRCode.addText(self.comment("local read init pointers b"))
|
|
pointerLRCode.addText(self.localReadInitPointers(kernel, tensorParametersB))
|
|
|
|
# we initiate lgkmcnt to 0, then assigning it correct value in makeSubIterSchedule()
|
|
if self.enable["Wait"]:
|
|
if self.getConditionToSkipLocalReadWriteWait(kernel, True, u, kernel["LoopIters"] - 1):
|
|
waitCode = self.wait(kernel, tensorParametersA, tensorParametersB, \
|
|
-1, 0, 0, \
|
|
"wait for prior local read local write")
|
|
|
|
luIdx = (u) % (self.numVgprBuffer+1) # local to use for MACs
|
|
if self.enable["MAC"]:
|
|
if kernel["EnableMatrixInstruction"]:
|
|
macIterCode.addCode(self.mfmaIter(kernel, u, kernel["InnerUnroll"], vregSetIdxMFMA, firstIter=firstIter and u == 0))
|
|
else:
|
|
macIterCode.addCode(self.macIter(kernel, luIdx, kernel["InnerUnroll"], True ))
|
|
|
|
###### unroll loop efficiency implementation######################################
|
|
# unroll loop efficiency implementation
|
|
## split A&B fetch&MAC code into multiple groups
|
|
## splitting strategy based on TT size
|
|
## 6x4 -> split MAC blob(s) into group of 8(s) and 16 FMA instructions.
|
|
## LDS fetch(es) into group of A{1-2)B(0) , A(3),B(1) (not implemented yet)
|
|
## 4x6 -> split MAC blob(s) into group of 8(s) and 16 FMA instructions.
|
|
## LDS fetch(es) into group of B{1-2)A(0) , B(3),A(1)
|
|
## 4x4 -> split into group of 8 and 8 MAC(s)
|
|
## 6x6 -> split into group of 12 MAC(s)
|
|
## 8x4/4x8 -> split into group of 16 and 16 MAC(s)
|
|
## 8x8 -> split into group of 16 MAC(s)
|
|
## supports only PLR=0
|
|
###############################################################################
|
|
if self.numItersPLR or (not globalParameters["UnrollLoopEfficiencyEnable"]):
|
|
subIterCode = self.makeSubIterSchedule(kernel, localReads, \
|
|
u, pointerLWCode, pointerLRCode, waitCode, macIterCode, waitLWCode, syncCode, pack[luIdx])
|
|
kl.append(subIterCode) # add scheduled "other", local reads, local writes
|
|
for item in list(pack[luIdx].items()):
|
|
if item.tempVgpr != None:
|
|
self.vgprPool.checkIn(item.tempVgpr)
|
|
item.tempVgpr = None
|
|
pack[luIdx] = Code.Module()
|
|
else:
|
|
macIterCode = Code.Module()
|
|
MacitemsReorder = []
|
|
if self.enable["MAC"]:
|
|
luIdx = (u) % (self.numVgprBuffer+1) # local to use for MACs
|
|
macIterCode.addCode(self.macCode(kernel, luIdx, kernel["InnerUnroll"] ))
|
|
MacIteritems = macIterCode.flatitems()
|
|
#remove last and second entry from list if AggressiveMode is set
|
|
# re-insert them back later
|
|
if (kernel["AggressivePerfMode"]):
|
|
MacIteritems = MacIteritems[:-1]
|
|
MacIteritems.pop(1)
|
|
#print("number MacItems\n",len(MacIteritems))
|
|
blockWidth = tensorParametersA["localReadInstruction"].blockWidth
|
|
numVectorsPerTileA = (kernel["ThreadTile%u"%tensorParametersA["tensorIdx"]]/kernel["VectorWidth"])
|
|
numReadsPerVectorA = (kernel["VectorWidth"] * tensorParametersA["bpe"] ) / (blockWidth*4)
|
|
numVectorsPerTileB = (kernel["ThreadTile%u"%tensorParametersB["tensorIdx"]]/kernel["VectorWidth"])
|
|
TotalnumLdsFetches = numVectorsPerTileA*numReadsPerVectorA + numVectorsPerTileB*numReadsPerVectorA
|
|
## Rules for applying kernel["UnrollLoopEfficiencyEnable"]
|
|
## if A+B fetches <= 3 no split approach
|
|
if not TotalnumLdsFetches > 3:
|
|
subIterCode = self.makeSubIterSchedule(kernel, localReads, \
|
|
u, pointerLWCode, pointerLRCode, waitCode, macIterCode)
|
|
kl.append(subIterCode) # add scheduled "other", local reads, local writes
|
|
else:
|
|
if ((kernel["ThreadTile0"] == 6 and kernel["ThreadTile1"] == 4) or
|
|
(kernel["ThreadTile0"] == 4 and kernel["ThreadTile1"] == 6)):
|
|
numGroups = 2 #group0 = 8 MAC(s) #group1 = 16 MAC(s) (6x4 - 4x2)
|
|
# ldsItems for splitting lds(s)
|
|
ldsItems = ([[4,2],[2,2]]) if kernel["ThreadTile0"] == 6 else ([[2,4],[2,2]])
|
|
macItems = [8,16]
|
|
waitCntItems = [0,0]
|
|
elif (kernel["ThreadTile0"] == 4 and kernel["ThreadTile1"] == 4):
|
|
numGroups = 2 #group0 = 8 MAC(s) #group1 = 8 MAC(s) 2)
|
|
ldsItems = ([[4,2],[0,2]])
|
|
macItems = [8,8]
|
|
waitCntItems = [0,0]
|
|
elif (kernel["ThreadTile0"] == 6 and kernel["ThreadTile1"] == 6):
|
|
numGroups = 2 #group0 = 8 MAC(s) #group1 = 16 MAC(s) 2)
|
|
ldsItems = ([[4,4],[2,2]])
|
|
macItems = [16,20]
|
|
waitCntItems = [0,0]
|
|
elif ((kernel["ThreadTile0"] == 8 and kernel["ThreadTile1"] == 4) or
|
|
(kernel["ThreadTile0"] == 4 and kernel["ThreadTile1"] == 8)):
|
|
numGroups = 2 #group0 = 16 MAC(s) #group1 = 16 MAC(s) 2)
|
|
ldsItems = ([[4,4],[4,0]]) if kernel["ThreadTile0"] == 8 else ([[4,4],[0,4]])
|
|
macItems = [16,16]
|
|
waitCntItems = [0,0]
|
|
elif (kernel["ThreadTile0"] == 8 and kernel["ThreadTile1"] == 8):
|
|
numGroups = 2 #group0 = 8 MAC(s) #group1 = 8 MAC(s) 2)
|
|
#ldsItems = ([[4,4],[4,4]])
|
|
macItems = [16,48]
|
|
waitCntItems = [0,0]
|
|
AitemsToReorder = localReadsA.flatitems()
|
|
BitemsToReorder = localReadsB.flatitems()
|
|
##reorder code?? based on LDS fetch
|
|
## works for 2 groups.. needs fix for more than 2 groups
|
|
for iter in range(0,numGroups):
|
|
endIdx = ldsItems[iter][0] if iter == 0 else kernel["ThreadTile%u"%tensorParametersA["tensorIdx"]]
|
|
startIdx = 0 if iter == 0 else ldsItems[iter-1][1]
|
|
for Bitems in range(startIdx, startIdx+ldsItems[iter][1]):
|
|
for Aitems in range(0, endIdx):
|
|
idx = Aitems+(kernel["ThreadTile%u"%tensorParametersA["tensorIdx"]]*Bitems)
|
|
MacitemsReorder.append(MacIteritems[idx])
|
|
if (iter != 0):
|
|
for Bitems in range(0, ldsItems[iter-1][1]):
|
|
for Aitems in range(ldsItems[iter-1][0], kernel["ThreadTile%u"%tensorParametersA["tensorIdx"]]):
|
|
MacitemsReorder.append(MacIteritems[Aitems+((kernel["ThreadTile%u"%tensorParametersA["tensorIdx"]])*Bitems)])
|
|
#print("Total number mac items A(%u)\n",len(MacitemsReorder))
|
|
#print("Total number ds items A(%u)\n"%(TotalnumLdsFetches))
|
|
#print("number ds items A_B(%u .. %u)\n"%(len(AitemsToReorder),len(BitemsToReorder)))
|
|
#reorder LDS fetches so order in which A+B fetches matches MAC blob
|
|
#e.g 8x4 original order in DGEMM case A[0-1]A[2-3]A[4-5]A[6-7]B[0-1][2-3]
|
|
#we want to re-order them into A[0-1][2-3]B[0-1]B[2-3]; In all other except
|
|
#DGEMM type, number of LDS fetches <=4 so no need for LDS re-order
|
|
if self.enable["LocalRead"] and TotalnumLdsFetches > 4:
|
|
localReads = Code.Module()
|
|
for iter in range(0,numGroups):
|
|
if len(AitemsToReorder):
|
|
localReads.addText(self.comment("local read a"))
|
|
numLocalReads = roundUp((ldsItems[iter][0])/kernel["VectorWidth"])
|
|
##print("number ds items A(%u..%u)\n"%(iter,numLocalReads))
|
|
for idx in range(0,numLocalReads):
|
|
localReads.addCode(AitemsToReorder[0])
|
|
AitemsToReorder = AitemsToReorder[1:]
|
|
if len(BitemsToReorder):
|
|
numLocalReads = roundUp(ldsItems[iter][1]/kernel["VectorWidth"])
|
|
##print("number ds items B(%u..%u)\n"%(iter,numLocalReads))
|
|
localReads.addText(self.comment("local read b"))
|
|
for items in range(0,numLocalReads):
|
|
localReads.addCode(BitemsToReorder[0])
|
|
BitemsToReorder = BitemsToReorder[1:]
|
|
if iter == 0:
|
|
waitCntItems[iter] = TotalnumLdsFetches - ((ldsItems[iter][0])/kernel["VectorWidth"] + (ldsItems[iter][1])/kernel["VectorWidth"])
|
|
elif iter+1 != numGroups:
|
|
waitCntItems[iter] = TotalnumLdsFetches - ((ldsItems[iter][0])/kernel["VectorWidth"] + (ldsItems[iter][1])/kernel["VectorWidth"] + waitCntItems[iter-1])
|
|
else:
|
|
waitCntItems[iter] = 0
|
|
#print("Waitcnt(%u..%u)\n"%(iter,waitCntItems[iter]))
|
|
for iter in range(0,numGroups):
|
|
#Mac Code
|
|
subIterCode = Code.Module()
|
|
waitCode = Code.Module()
|
|
macIterCodeGrp = Code.Module()
|
|
doOnce = False
|
|
if self.enable["MAC"]:
|
|
numMacItems = macItems[iter]
|
|
for Items in range(0,numMacItems):
|
|
macItem = MacitemsReorder.pop(0)
|
|
macIterCodeGrp.addCode(macItem)
|
|
## add s_setprio 1 when AggressivePerfMode ==1 as second instruction for second-last blob macCode
|
|
if (kernel["AggressivePerfMode"] and not doOnce):
|
|
macIterCodeGrp.addInst("s_setprio ","1","Raise priority while processing macs")
|
|
doOnce = True
|
|
## add s_setprio 0 when AggressivePerfMode ==1 as last instruction
|
|
if (kernel["AggressivePerfMode"]):
|
|
macIterCodeGrp.addInst("s_setprio ","0","Reset priority after macs")
|
|
#print("ReadWaitcnt(%u..%u)\n"%(iter,waitCntItems[iter]))
|
|
#print("WriteCodeCount(%d..%u)\n",u,self.perIterLocalWriteCode[u].count())
|
|
if (iter == 0):
|
|
if self.enable["Wait"]:
|
|
#calculate lgkmcnt value including read+write for first iteration
|
|
waitCntVal = waitCntItems[iter] + 1 if (self.perIterLocalWriteCode[u].count()>0) else waitCntItems[iter]
|
|
# read + write instructions lgkmcnt (1=> for write)
|
|
# build waitCnt using new lgkmcnt
|
|
waitCode = Code.WaitCnt(self.version, waitCntVal,-1,"wait for prior local read")
|
|
subIterCode = self.makeSubIterSchedule(kernel, localReads, \
|
|
u, pointerLWCode, pointerLRCode, waitCode, macIterCodeGrp)
|
|
else:
|
|
#last group only pointer + localWrite Code
|
|
if self.enable["Wait"]:
|
|
waitCode = Code.WaitCnt(self.version, waitCntItems[iter],-1,"wait for prior local read & local writes")
|
|
subIterCode.addCode(waitCode)
|
|
subIterCode.addCode(macIterCodeGrp)
|
|
kl.append(subIterCode) # add scheduled "other", local reads, local writes
|
|
|
|
# close unrolled loop
|
|
if expand:
|
|
if not finalLoop:
|
|
kl.append(self.comment3("Unrolled Loop - End %u/%u"%(lc+1, loopCopies)))
|
|
else:
|
|
kl.append(self.comment3("Unrolled Loop - End %u/%u (final)"%(lc+1, loopCopies)))
|
|
|
|
# add wait for global read here canOptimizePreLoopLWVmcnt is true and DirectToVgpr is true
|
|
# StoreCInUnroll does not require this wait because wait code is generated at the top of inner loop
|
|
if kernel["PrefetchGlobalRead"] and self.canOptimizePreLoopLWVmcnt and (kernel["DirectToVgprA"] or kernel["DirectToVgprB"]) \
|
|
and (not kernel["StoreCInUnroll"]):
|
|
retStr = self.getWaitcntCodeForDirectToVgpr(kernel, localWriteEndIter, u=0, firstIter=False)
|
|
kl.append(retStr)
|
|
|
|
else:
|
|
kl.append(self.comment3("Unrolled Loop - End"))
|
|
|
|
oddLabel = lc == 0
|
|
kl.append(self.closeLoop(kernel, self.unrollIdx, finalLoop, loopCopies, oddLabel=oddLabel))
|
|
|
|
##############################################################################
|
|
# Kernel Body
|
|
##############################################################################
|
|
def kernelBody( self, kernel, tensorParametersA, tensorParametersB ):
|
|
expand = kernel["ExpandPointerSwap"]
|
|
|
|
####################################
|
|
# Begin String
|
|
kl = []
|
|
|
|
####################################
|
|
# Function Prefix
|
|
kl.append(self.comment3("Function Prefix"))
|
|
kl.append(self.functionPrefix(kernel))
|
|
|
|
####################################
|
|
# Function Signature
|
|
####################################
|
|
kl.append(self.comment3("Begin Kernel"))
|
|
kl.append(self.functionSignaturePrefix(kernel))
|
|
|
|
beforeFunctionSignature = '\n'.join([str(x) for x in kl])
|
|
kl = []
|
|
|
|
kl.append(self.functionSignatureSuffix(kernel))
|
|
kl.append(self.functionBegin(kernel))
|
|
|
|
# init code optimization: generate local read address code before wait for kernel arg load (in allocateResources())
|
|
klLR = []
|
|
self.isLocalReadAddressCodeGenerated = False
|
|
|
|
if self.enable["PreLoop"]:
|
|
if self.isInitCodeOptLR:
|
|
####################################
|
|
# Local Read Addresses
|
|
####################################
|
|
klLR += self.generateLocalReadAddressCode(kernel, tensorParametersA, tensorParametersB)
|
|
|
|
# init code optimization : allocate resource
|
|
self.lwaInitOptAllocate()
|
|
|
|
lraCode=None
|
|
placeholderInitCodeOpt=None
|
|
if self.isInitCodeOptLW:
|
|
lraCode = ""
|
|
placeholderInitCodeOpt = "__placeholderInitCodeOpt__" # placeholder for local write code (for future replacement)
|
|
if self.isInitCodeOptLR:
|
|
# string for local read code
|
|
lraCode = '\n'.join([str(x) for x in klLR])
|
|
# local write code is generated later. Here, just add placeholder to replace with local write code
|
|
if self.isInitCodeOptLW:
|
|
lraCode += '\n' + placeholderInitCodeOpt + '\n'
|
|
|
|
kl.append(self.comment3("Allocate Resources"))
|
|
kl.append(self.allocateResources(kernel, lraCode))
|
|
|
|
if (not kernel["PreloadKernelArguments"]):
|
|
# not PreloadKernelArguments case (local read code is delayed in PreloadKernelArguments case)
|
|
# add local read address code to kl after allocateResources()
|
|
# generate Local Read Addresses for not isInitCodeOptLR case
|
|
kl += self.generateLocalReadAddressCode(kernel, tensorParametersA, tensorParametersB)
|
|
|
|
# doShadowInit performs initialization in the 'shadow' of the global mem prefetch
|
|
self.doShadowInit = 0
|
|
if kernel["PrefetchGlobalRead"]:
|
|
if self.actualSummationLoops == 1:
|
|
self.doShadowInit = 2 # 2 is both store setup and initC
|
|
else:
|
|
# can't do shadow initC with multiple summation since this resets the ValuC counters
|
|
# on each unroll iteration.
|
|
self.doShadowInit = 1 # 1 is just store setup
|
|
|
|
if self.prefetchAcrossPersistent:
|
|
# SrdC/D init before persistent loop
|
|
kl.append(self.globalWriteWorkGroupInitBeforePersistentLoop(kernel))
|
|
|
|
# init code for StoreCInUnroll (only once before persistent kernel loop)
|
|
# SrdC/D init has to be done beforehand
|
|
if self.storeCInUnroll:
|
|
kl.append(self.initStoreCInUnroll(kernel))
|
|
|
|
# first prefetch is outside persistent loop, subsequent prefetch will
|
|
# be integrated into no-load-loop
|
|
kl_NT, kl_LW = self.setupNewTile(kernel, tensorParametersA, tensorParametersB, isPap=False, isOptNLL=False)
|
|
kl += kl_NT
|
|
kl.append(self.openPersistentLoop(kernel))
|
|
else:
|
|
# prefetch is inside persistent loop
|
|
kl.append(self.openPersistentLoop(kernel))
|
|
kl_NT, kl_LW = self.setupNewTile(kernel, tensorParametersA, tensorParametersB, isPap=False, isOptNLL=False)
|
|
kl += kl_NT
|
|
|
|
# init code optimization : release resource
|
|
self.lwaInitOptRelease()
|
|
|
|
pack = [ Code.Module() for i in range (self.numVgprBuffer+1) ]
|
|
self.preLoopLocalWriteCode = None
|
|
|
|
# add local read address code if it is not generated (PreloadKernelArguments case)
|
|
kl += self.generateLocalReadAddressCode(kernel, tensorParametersA, tensorParametersB)
|
|
|
|
if kernel["PrefetchGlobalRead"]:
|
|
if self.doShadowInit:
|
|
kl.append(self.openShadowInit(kernel))
|
|
# init code for StoreCInUnroll per each persistent kernel loop iteration
|
|
# before generate new srdC/D (in globalWriteWorkGroupInit())
|
|
if self.storeCInUnroll:
|
|
kl.append(self.initStoreCInUnrollPerPersistentLoop(kernel))
|
|
kl.append(self.globalWriteWorkGroupInit(kernel))
|
|
# after genarating new SrdC,D, swap with backup values so that previous srdC,D is used in unroll loop for StoreCInUnroll
|
|
if self.storeCInUnroll:
|
|
kl.append(self.swapSrdCDandBackup(kernel))
|
|
if self.doShadowInit == 2:
|
|
kl.append(self.initC(kernel)) # initC while waiting for global reads
|
|
kl.append(self.closeShadowInit(kernel))
|
|
|
|
if self.enable["Wait"] and not self.canOptimizePreLoopLWVmcnt:
|
|
kl.append(self.getWaitcntCodeForPGR(kernel))
|
|
# These cases loop back and run the prefetch loop again
|
|
# we need an extra barrier to ensure that the ds_reads (either for SR or MFMA) from previous iteration
|
|
# have finished before we generate the prefetch for the next summation index.
|
|
if kernel["PersistentKernel"] or kernel["StreamK"] > 0 or self.actualSummationLoops>1:
|
|
kl.append( self.indent + self.syncStr + "// for PersistentKernel / StreamK " + self.endLine )
|
|
|
|
if self.enable["LocalWrite"]:
|
|
# local write
|
|
self.preLoopLocalWriteCode = self.preLoopLocalWriteDo(kernel, tensorParametersA, tensorParametersB)
|
|
kl.append(self.preLoopLocalWriteCode)
|
|
# swap local ptrs
|
|
kl.append(self.comment("local write swap a"))
|
|
kl.append(self.localWriteSwapOffsets(kernel, expand, tensorParametersA))
|
|
kl.append(self.comment("local write swap b"))
|
|
kl.append(self.localWriteSwapOffsets(kernel, expand, tensorParametersB))
|
|
kl.append(self.localWriteInitPointers(kernel, tensorParametersA))
|
|
kl.append(self.localWriteInitPointers(kernel, tensorParametersB))
|
|
|
|
if kernel["PrefetchGlobalRead"] == 2:
|
|
kl.append(self.openPrefetchGlobalRead2(kernel))
|
|
if self.enable["GlobalRead"]:
|
|
# if DirectToVgpr is enabled and swapGlobalRoad is true, swap the order of global read (B->A)
|
|
tensorParameters1st = tensorParametersA
|
|
tensorParameters2nd = tensorParametersB
|
|
if self.isSwapGlobalReadOrderForDtvOrDtl(kernel):
|
|
tensorParameters1st, tensorParameters2nd = tensorParameters2nd, tensorParameters1st
|
|
kl.append(str(self.directToLdsM0Update(kernel, 1, tensorParameters1st)))
|
|
kl.append(str(self.globalReadDo(kernel, 0, tensorParameters1st, 1)))
|
|
kl.append(str(self.directToLdsM0Update(kernel, 1, tensorParameters2nd)))
|
|
kl.append(str(self.globalReadDo(kernel, 0, tensorParameters2nd, 1)))
|
|
|
|
# swap local ptrs again if DirectToLds is enabled
|
|
if kernel["DirectToLdsA"]:
|
|
kl.append(self.comment("local write swap a"))
|
|
kl.append(self.localWriteSwapOffsets(kernel, expand, tensorParametersA))
|
|
kl.append(self.localWriteInitPointers(kernel, tensorParametersA))
|
|
if kernel["DirectToLdsB"]:
|
|
kl.append(self.comment("local write swap b"))
|
|
kl.append(self.localWriteSwapOffsets(kernel, expand, tensorParametersB))
|
|
kl.append(self.localWriteInitPointers(kernel, tensorParametersB))
|
|
|
|
kl.append(self.closePrefetchGlobalRead2(kernel))
|
|
|
|
# prefetch-local
|
|
if self.numItersPLR:
|
|
# not generate wait for local write if LDS write code is not generated
|
|
if self.enable["Wait"] and not kernel["NoLdsWriteCode"]:
|
|
# TODO: need to check if we correctly checked-in the temp VGPR used for Int8 LocalWrite (uDu, PGR=2)
|
|
kl.append(self.wait(kernel, tensorParametersA, tensorParametersB, -1, 0, -1, "0prefetch wait for local write"))
|
|
if self.enable["Sync"]:
|
|
kl.append(self.syncThreads(kernel))
|
|
|
|
# in some cases need an extra copy of the LDS read with appropriate double buffer offsets
|
|
if self.enable["LocalRead"]:
|
|
for plrIdx in range(0, self.numItersPLR):
|
|
pack[plrIdx] = Code.Module()
|
|
# no matter EPS or PAP, only prefect local once per plrIdx
|
|
# for espi in range(0, (self.prefetchAcrossPersistent and kernel["ExpandPointerSwap"])+1):
|
|
for espi in range(0, 1):
|
|
for iui in range(0,kernel["InnerUnroll"]):
|
|
if iui*self.numReadsIterCoalescedA < kernel["InnerUnroll"]:
|
|
kl.append(self.comment("local read prefetch a"))
|
|
localReadCodeA, packCodeA = self.localReadDo(kernel, plrIdx*self.numIterPerCoalescedReadA, iui*self.numReadsIterCoalescedA, espi, tensorParametersA)
|
|
kl.append(localReadCodeA)
|
|
pack[plrIdx].addCode(packCodeA)
|
|
if iui*self.numReadsIterCoalescedB < kernel["InnerUnroll"]:
|
|
kl.append(self.comment("local read prefetch b"))
|
|
localReadCodeB, packCodeB = self.localReadDo(kernel, plrIdx*self.numIterPerCoalescedReadB, iui*self.numReadsIterCoalescedB, espi, tensorParametersB)
|
|
kl.append(localReadCodeB)
|
|
pack[plrIdx].addCode(packCodeB)
|
|
if iui*self.numReadsIterCoalescedA < kernel["InnerUnroll"]:
|
|
kl.append(self.comment("local read inc a"))
|
|
kl.append(self.localReadInc(kernel, iui, tensorParametersA))
|
|
if iui*self.numReadsIterCoalescedB < kernel["InnerUnroll"]:
|
|
kl.append(self.comment("local read inc b"))
|
|
kl.append(self.localReadInc(kernel, iui, tensorParametersB))
|
|
kl.append(self.closeSumAtLeastUnroll(kernel, prefetch=True, isOptNLL=False, isPap=False, isNGLL=False))
|
|
|
|
loopCopies = 2 if expand else 1
|
|
|
|
if self.useInitAccVgprOpt:
|
|
# generate first iteration code for init accvgpr opt
|
|
kl.append(self.comment3("First Unrolled Iter for InitAccVgprOpt - Begin"))
|
|
# open loop without Label
|
|
kl.append(self.openLoop(kernel, self.unrollIdx, noLabelGen=True))
|
|
self.loopBody( kernel, tensorParametersA, tensorParametersB, kl, pack, 0, loopCopies, False, firstIter=True )
|
|
|
|
# open unrolled summation loop
|
|
kl.append(self.comment3("Unrolled Loop(s) - Begin"))
|
|
# In StoreCInUnroll case, LoopCounter check code is already generated. We need only LoopBeginLabel
|
|
beginLabelOnly = kernel["StoreCInUnroll"]
|
|
kl.append(self.openLoop(kernel, self.unrollIdx, beginLabelOnly=beginLabelOnly))
|
|
|
|
lcStart = 0
|
|
if self.useInitAccVgprOpt:
|
|
lcStart = 1 if loopCopies == 2 else 0
|
|
for lc in range(0, loopCopies):
|
|
loopIndex = lcStart + lc
|
|
if loopIndex >= loopCopies:
|
|
loopIndex -= loopCopies
|
|
# loop body code generation
|
|
finalLoop = lc == loopCopies - 1
|
|
self.loopBody( kernel, tensorParametersA, tensorParametersB, kl, pack, loopIndex, loopCopies, finalLoop )
|
|
|
|
kl.append(self.comment("Before NLL: Check VGPR.checkin for INT8 LW"))
|
|
|
|
# swap local write, read again before noLoadLoop if PrefetchGlobalRead and DirectToLds is enabled
|
|
# In DirectToLds enabled case, local write address is necessary for prefetch global read (for m0).
|
|
# However, even exit with DirectToLds will not pass with this code (limitation).
|
|
# So far, this code is to make odd exit case (i.e. k is multiple of 2*depthU) pass for DirectToVgpr
|
|
if not self.useInitAccVgprOpt and kernel["PrefetchGlobalRead"] and self.enable["LocalWrite"] and kernel["ExpandPointerSwap"]:
|
|
# local write for next iter, used to have local writes here
|
|
if(kernel["DirectToLdsA"]):
|
|
kl.append(self.comment("local write swap offsets a"))
|
|
kl.append(self.localWriteSwapOffsets(kernel, expand, tensorParametersA))
|
|
if(kernel["DirectToLdsB"]):
|
|
kl.append(self.comment("local write swap offsets b"))
|
|
kl.append(self.localWriteSwapOffsets(kernel, expand, tensorParametersB))
|
|
# swap local read point for self.useInitAccVgprOpt
|
|
if self.useInitAccVgprOpt and kernel["ExpandPointerSwap"]:
|
|
if self.enable["LocalRead"]:
|
|
kl.append(self.comment("local read swap offsets a"))
|
|
kl.append(self.localReadSwapOffsets(kernel, expand, tensorParametersA))
|
|
kl.append(self.comment("local read swap offsets b"))
|
|
kl.append(self.localReadSwapOffsets(kernel, expand, tensorParametersB))
|
|
|
|
if kernel["PrefetchGlobalRead"] == 2:
|
|
# re-generate store code for StoreCInUnroll (odd=0,isLast=False))
|
|
self.generateStoreCCodeInUnrollLoop(kernel, 0, isLast=False)
|
|
isOptNLL=False
|
|
isPap=False
|
|
if self.prefetchAcrossPersistent:
|
|
isOptNLL = True
|
|
isPap = True
|
|
kl += self.noLoadLoop(kernel, tensorParametersA, tensorParametersB, isOptNLL=isOptNLL, isPap=isPap, isNGLL=True, pack=pack)
|
|
|
|
# re-generate store code for StoreCInUnroll (no increment code (isLast=True))
|
|
# this should be after NGLL code for PGR=2
|
|
odd = 1
|
|
self.generateStoreCCodeInUnrollLoop(kernel, odd, isLast=True)
|
|
|
|
# This "NoLoad" loop is a copy of the unroll loop but with global loads + LDS writes removed
|
|
# doShadowInit is required since this pushes up the store SRD initialization before the NLL
|
|
# OptNLL only allowed for single summation index - for multiple summation we (currently)
|
|
# execute the NLL inside each unroll iteration not just once at the end.
|
|
if kernel["PrefetchGlobalRead"]:
|
|
if not kernel["SuppressNoLoadLoop"]:
|
|
|
|
firstNLLgenerated = False
|
|
if kernel["KernelLanguage"] == "Assembly" and kernel["OptNoLoadLoop"] and \
|
|
kernel["BufferLoad"] and kernel["BufferStore"] and self.doShadowInit and \
|
|
kernel["LocalSplitU"]==1 and kernel["GlobalSplitU"] == 1 and \
|
|
self.actualSummationLoops==1:
|
|
|
|
firstNLLgenerated = True
|
|
|
|
# two different noLoadLoops:
|
|
# 1. OptNLL & PAP global-read interleaved (only for PAP=ON)
|
|
# (2. OptNLL : No PAP global-read (For PAP=OFF, or PAP=ON but the last tile))
|
|
# -> this is unified with 1. global-read is invalidated at the last tile.
|
|
# 3. OrdinaryNLL (Not Opt.)
|
|
self.saveLocalPointers(kernel)
|
|
# deepCopy packCode for OptNLL noLoadLoop
|
|
deepCopyPack = copy.deepcopy(pack)
|
|
# keep StoreCInUnroll related code for the next noLoadLoop
|
|
if kernel["StoreCInUnroll"]:
|
|
self.backupStoreCInUnrollRelatedCode()
|
|
isPap = self.prefetchAcrossPersistent
|
|
kl += self.noLoadLoop(kernel, tensorParametersA, tensorParametersB, isOptNLL=True, isPap=isPap, isNGLL=False, pack=deepCopyPack)
|
|
self.restoreLocalPointers(kernel)
|
|
# restore StoreCInUnroll related code
|
|
if kernel["StoreCInUnroll"]:
|
|
self.restoreStoreCInUnrollRelatedCode()
|
|
|
|
# skip second NLL code if enableSingleNLLOpt
|
|
if not (self.enableSingleNLLOpt and firstNLLgenerated):
|
|
papMode = self.prefetchAcrossPersistent and kernel["PrefetchAcrossPersistentMode"] == 1
|
|
kl += self.noLoadLoop(kernel, tensorParametersA, tensorParametersB, isOptNLL=False, isPap=papMode, isNGLL=False, pack=pack)
|
|
|
|
else:
|
|
# generate PrefetchGlobalLastIterEnd label
|
|
kl.append(self.closeSumAtLeastUnroll(kernel, prefetch=False, isOptNLL=False, isPap=False, isNGLL=False))
|
|
|
|
if kernel["StoreCInUnroll"]:
|
|
# end process for StoreCInUnroll per PersistentLoop (NoOptNLL)
|
|
kl.append(self.endProcessPersistentLoopforStoreCInUnrollNoOptNLL(kernel))
|
|
|
|
# if PGR, last few iterations will have PLR,
|
|
# and those PLR will not be used(register not checkIn) if without NoLoadLoop
|
|
else:
|
|
for i in range(self.numVgprBuffer):
|
|
for item in list(pack[i].items()):
|
|
if item.tempVgpr != None:
|
|
self.vgprPool.checkIn(item.tempVgpr)
|
|
item.tempVgpr = None
|
|
|
|
if self.staggerU and self.actualSummationLoops>1:
|
|
kl.append(self.comment("remove stagger offsets"))
|
|
kl.append(self.removeStagger(kernel, tensorParametersA))
|
|
kl.append(self.removeStagger(kernel, tensorParametersB))
|
|
|
|
if not self.noTailLoop:
|
|
########################################
|
|
# Tail Loop
|
|
# PackSummationDims=1 requires that the tile slice does not cross DepthU
|
|
# which means tail loop not needed.
|
|
########################################
|
|
self.inTailLoop = True
|
|
if kernel["LoopTail"] and not kernel["PackSummationDims"]:
|
|
kl.append(self.comment3("Tail Loop"))
|
|
|
|
# Update local write pointers in case the upcoming global reads are writing directly to LDS:
|
|
if self.enable["LocalWrite"]:
|
|
if kernel["PrefetchGlobalRead"]:
|
|
kl.append(self.comment("local write reset offsets a"))
|
|
kl.append(self.localWriteResetOffsets(kernel, kernel["ExpandPointerSwap"], tensorParametersA))
|
|
if kernel["ExpandPointerSwap"]:
|
|
# reset local write offset in asm code as well
|
|
kl.append(self.localWriteResetOffsets(kernel, False, tensorParametersA))
|
|
kl.append(self.comment("local write reset offsets b"))
|
|
kl.append(self.localWriteResetOffsets(kernel, kernel["ExpandPointerSwap"], tensorParametersB))
|
|
if kernel["ExpandPointerSwap"]:
|
|
# reset local write offset in asm code as well
|
|
kl.append(self.localWriteResetOffsets(kernel, False, tensorParametersB))
|
|
|
|
if self.enable["GlobalRead"]:
|
|
# tail: global read
|
|
kl.append(self.calculateLoopNumIter(kernel, -1, False))
|
|
if self.staggerU and self.actualSummationLoops==1:
|
|
kl.append(self.comment("remove stagger offsets for tail loop"))
|
|
kl.append(self.removeStagger(kernel, tensorParametersA))
|
|
kl.append(self.removeStagger(kernel, tensorParametersB))
|
|
|
|
# if DirectToVgpr is enabled and swapGlobalRoad is true, swap the order of global read (B->A)
|
|
tensorParameters1st = tensorParametersA
|
|
tensorParameters2nd = tensorParametersB
|
|
tc1 = 'a'
|
|
tc2 = 'b'
|
|
if self.isSwapGlobalReadOrderForDtvOrDtl(kernel):
|
|
tensorParameters1st, tensorParameters2nd = tensorParameters2nd, tensorParameters1st
|
|
tc1, tc2 = tc2, tc1
|
|
kl.append(self.comment("Update M0 for DTLDS"))
|
|
tmpStr = str(self.directToLdsM0Update(kernel, 1, tensorParameters1st))
|
|
tmpStr = tmpStr.replace("__placeholder__", str(0))
|
|
kl.append(tmpStr)
|
|
kl.append(self.comment("global read %s"%tc1))
|
|
vregSetIdx = 0
|
|
kl.append(str(self.globalReadDo(kernel, 2, tensorParameters1st, vregSetIdx)))
|
|
kl.append(self.comment("Update M0 for DTLDS"))
|
|
tmpStr = str(self.directToLdsM0Update(kernel, 1, tensorParameters2nd))
|
|
tmpStr = tmpStr.replace("__placeholder__", str(0))
|
|
kl.append(tmpStr)
|
|
kl.append(self.comment("global read %s"%tc2))
|
|
vregSetIdx = 0
|
|
kl.append(str(self.globalReadDo(kernel, 2, tensorParameters2nd, vregSetIdx)))
|
|
if self.enable["Wait"]:
|
|
kl.append(self.wait(kernel, tensorParametersA, tensorParametersB, 0, -1, -1, "2wait for global read"))
|
|
if self.enable["Sync"]:
|
|
kl.append(self.syncThreads(kernel))
|
|
|
|
kl.append(self.doneGlobalABReads(kernel))
|
|
|
|
# the following read/write addresses could be modified in recalcLocal(Read|Write)Addresses due to policy change
|
|
self.oriLraA = None # back up original local read address vgpr
|
|
self.oriLraB = None
|
|
self.oriLwaA = None # back up original local write address vgpr
|
|
self.oriLwaB = None
|
|
for uDu in range(0, kernel["DepthULdsDivisor"]):
|
|
# change local write policy from interleave-K to fractional as tail loop
|
|
# iterate LDS read address one unit of K at a time
|
|
# skip recalcLocalWriteAddresses if DirectToVgpr is enabled
|
|
if kernel.enabledSplitLDS and not (kernel["DirectToVgprA"]):
|
|
kl.append(self.comment("Recalc local write offset A"))
|
|
kl.append(self.recalcLocalWriteAddresses(kernel, tensorParametersA, uDu))
|
|
if kernel.enabledSplitLDS and not (kernel["DirectToVgprB"]):
|
|
kl.append(self.comment("Recalc local write offset B"))
|
|
kl.append(self.recalcLocalWriteAddresses(kernel, tensorParametersB, uDu))
|
|
if self.enable["Sync"]:
|
|
if uDu > 0:
|
|
kl.append(self.comment("sync before local write"))
|
|
kl.append(self.syncThreads(kernel))
|
|
if self.enable["LocalWrite"] and not kernel["NoLdsWriteCode"]:
|
|
# tail: local write
|
|
kl.append(self.localWriteInitPointers(kernel, tensorParametersA))
|
|
kl.append(self.localWriteInitPointers(kernel, tensorParametersB))
|
|
kl.append(self.comment("local write a"))
|
|
tempLWCodeModA = self.localWriteDo(kernel, tensorParametersA, None)
|
|
kl.append(tempLWCodeModA)
|
|
kl.append(self.comment("local write b"))
|
|
tempLWCodeModB = self.localWriteDo(kernel, tensorParametersB, None)
|
|
kl.append(tempLWCodeModB)
|
|
# change local read policy from wider local read to one unit of K at a time
|
|
# DirectToVgpr case, use original wider local read instead of recalculating local read address
|
|
if not (kernel["DirectToVgprA"] or kernel["DirectToVgprB"]):
|
|
kl.append(self.comment("Recalc local read offsets"))
|
|
kl.append(self.recalcLocalReadAddressesAB(kernel))
|
|
if self.enable["Wait"]:
|
|
# TODO: need to check if we correctly checked-in the temp VGPR used for Int8 LocalWrite (uDu, PGR=2)
|
|
kl.append(self.wait(kernel, tensorParametersA, tensorParametersB, -1, 0, -1, "5wait for local write"))
|
|
if self.enable["Sync"]:
|
|
kl.append(self.syncThreads(kernel))
|
|
#kl.append(self.dumpLds(kernel, 0, 8))
|
|
|
|
# tail: re-init local read addresses
|
|
if kernel["PrefetchGlobalRead"]:
|
|
kl.append(self.comment("local read reset offsets a"))
|
|
kl.append(self.localReadResetOffsets(kernel, tensorParametersA))
|
|
kl.append(self.comment("local read reset offsets b"))
|
|
kl.append(self.localReadResetOffsets(kernel, tensorParametersB))
|
|
kl.append(self.comment("local read init pointers a"))
|
|
kl.append(self.localReadInitPointers(kernel, tensorParametersA))
|
|
kl.append(self.comment("local read init pointers b"))
|
|
kl.append(self.localReadInitPointers(kernel, tensorParametersB))
|
|
# tail: macs
|
|
kl.append(self.comment("tail loop: macs"))
|
|
kl.append(self.openLoop(kernel, -1, uDu if kernel.enabledSplitLDS else None))
|
|
|
|
# Try to use InnerUnroll in the tail loop if allowed:
|
|
KinInnerUnroll = kernel["InnerUnroll"]
|
|
if kernel["EnableMatrixInstruction"]:
|
|
KinInnerUnroll *= kernel["MatrixInstK"]
|
|
|
|
tailLoopInnerUnroll = 1
|
|
asem = kernel["AssertSummationElementMultiple"]
|
|
gsu = kernel["GlobalSplitU"]
|
|
if ((asem%gsu == 0) and (asem//gsu) % KinInnerUnroll == 0):
|
|
tailLoopInnerUnroll = kernel["InnerUnroll"]
|
|
elif (kernel["LocalDotLayout"] > 1) and (kernel["InnerUnroll"] == kernel["LocalDotLayout"]):
|
|
tailLoopInnerUnroll = kernel["InnerUnroll"]
|
|
# need to unroll tail loop for the following cases
|
|
mEnd = 1
|
|
if (kernel["DirectToVgprA"] or kernel["DirectToVgprB"] or kernel["DirectToLdsA"] or kernel["DirectToLdsB"]) \
|
|
and kernel["EnableMatrixInstruction"]:
|
|
mEnd = kernel["_DepthULds"]//(kernel["MatrixInstK"]*kernel["LocalSplitU"])
|
|
elif kernel["EnableMatrixInstruction"] and \
|
|
((kernel["LdsPadA"] and kernel["LdsBlockSizePerPadA"]) or (kernel["LdsPadB"] and kernel["LdsBlockSizePerPadB"])):
|
|
# LdsPad + LBSPP case, address increment is not distributed uniformly. So, we need to unroll tail loop
|
|
mEnd = kernel["_DepthULds"]//(kernel["MatrixInstK"]*kernel["LocalSplitU"])
|
|
|
|
for mValue in range(mEnd):
|
|
if mEnd > 1:
|
|
# print tail loop counter if mEnd>1 (means do tail loop unroll)
|
|
kl.append(self.comment("tail loop unroll iter %u"%(mValue)))
|
|
pack[0] = Code.Module()
|
|
# always use vregSetIdx=0 for DirectToVgpr + tail loop
|
|
vregSetIdxMFMA = 0
|
|
for iui in range(0, tailLoopInnerUnroll):
|
|
if self.enable["LocalRead"]:
|
|
# local read buffer id. No prefetch in tail loop case.
|
|
bufIdx = (mValue % (self.numVgprBuffer+1)) % kernel["LoopIters"]
|
|
# DTVpack case, use different bufIdx for all loop iterations
|
|
bufIdxDTV = mValue % kernel["LoopIters"]
|
|
bufIdxA = bufIdxDTV if self.packDTVA else bufIdx
|
|
bufIdxB = bufIdxDTV if self.packDTVB else bufIdx
|
|
if mValue*self.numReadsIterCoalescedA < mEnd:
|
|
# Reading 16-bit data from LDS requires packing when ECC enabled
|
|
kl.append(self.comment("local read a"))
|
|
localReadCodeA, packCodeA = self.localReadDo(kernel, bufIdxA*self.numIterPerCoalescedReadA, iui*self.numIterPerCoalescedReadA, 0, tensorParametersA)
|
|
kl.append(localReadCodeA)
|
|
pack[0].addCode(packCodeA)
|
|
if mValue*self.numReadsIterCoalescedB < mEnd:
|
|
kl.append(self.comment("local read b"))
|
|
localReadCodeB, packCodeB = self.localReadDo(kernel, bufIdxB*self.numIterPerCoalescedReadB, iui*self.numIterPerCoalescedReadB, 0, tensorParametersB)
|
|
kl.append(localReadCodeB)
|
|
pack[0].addCode(packCodeB)
|
|
# adjustment for DirectToLds case
|
|
iuiParam = iui + tailLoopInnerUnroll * mValue
|
|
kl.append(self.comment("local read inc a"))
|
|
kl.append(self.localReadInc(kernel, iuiParam, tensorParametersA))
|
|
kl.append(self.comment("local read inc b"))
|
|
kl.append(self.localReadInc(kernel, iuiParam, tensorParametersB))
|
|
if self.enable["Wait"]:
|
|
kl.append(self.wait(kernel, tensorParametersA, tensorParametersB, -1, -1, 0, "4wait for local read"))
|
|
|
|
if kernel["EnableMatrixInstruction"]:
|
|
kl.append(pack[0])
|
|
# vgpr.checkin for all the checked-out vgpr in LocalRead
|
|
for item in list(pack[0].items()):
|
|
if item.tempVgpr != None:
|
|
self.vgprPool.checkIn(item.tempVgpr)
|
|
item.tempVgpr = None
|
|
pack[0] = Code.Module()
|
|
|
|
if self.enable["MAC"]:
|
|
if kernel["EnableMatrixInstruction"]:
|
|
# always use vregSetIdx=0 for DirectToVgpr + tail loop
|
|
vregSetIdxMFMA = 0
|
|
kl.append(self.mfmaIter(kernel, mValue, tailLoopInnerUnroll, vregSetIdxMFMA, False, True))
|
|
else:
|
|
kl.append(self.macIter(kernel, mValue, tailLoopInnerUnroll, True, True))
|
|
|
|
finalLoop = mValue == mEnd - 1
|
|
kl.append(self.closeLoop(kernel, -1, finalLoop, loopCopies, uDu if kernel.enabledSplitLDS else None, skipCondJumpCounter=mValue))
|
|
# always emit the skip-tail-loop label
|
|
kl.append(self.closeLoop(kernel, -1, None, loopCopies, emitEndLabelOnly=True))
|
|
# tail: close
|
|
self.inTailLoop = False
|
|
|
|
# extra summation loops: global increment and close
|
|
for i in reversed(range(self.otherSummationLoops)):
|
|
kl.append(self.comment("global read inc AB"))
|
|
kl.append(self.globalReadIncrementAB(kernel, i, 0))
|
|
kl.append(self.closeLoop(kernel, i, True, loopCopies))
|
|
|
|
if self.prefetchAcrossPersistent and kernel["PrefetchAcrossPersistentMode"] != 1:
|
|
kl.append(str(self.openPrefetchAcrossPersistent(kernel, isOptNLL=False)))
|
|
kl_NT, _ = self.setupNewTile(kernel, self.tPA, self.tPB, isPap=True, isOptNLL=False)
|
|
kl += kl_NT
|
|
kl.append(str(self.closePrefetchAcrossPersistent(kernel, isOptNLL=False)))
|
|
|
|
kl.append(self.endSummation(kernel))
|
|
if self.enable["PostLoop"]:
|
|
if not self.doShadowInit:
|
|
kl.append(self.globalWriteWorkGroupInit(kernel))
|
|
|
|
####################################
|
|
# Shift Vector Components
|
|
####################################
|
|
if kernel["EdgeType"] == "ShiftPtr":
|
|
# GuaranteeNoPartial means each component in the vector loads is always valid. In this case we
|
|
# don't need the unshift code
|
|
|
|
# shift vector components d0
|
|
if not kernel["GuaranteeNoPartialA"] and self.readTileDimVectorA:
|
|
kl.append(self.comment("shift vector components d0"))
|
|
kl.append(self.shiftVectorComponents(kernel, tensorParametersA))
|
|
|
|
# shift vector components d1, for MFMA version, B never entered this
|
|
if not kernel["GuaranteeNoPartialB"] and self.readTileDimVectorB:
|
|
kl.append(self.comment("shift vector components d1"))
|
|
kl.append(self.shiftVectorComponents(kernel, tensorParametersB))
|
|
|
|
# complex declare tmp registers
|
|
kl.append(self.complexDeclareTmpRegisters(kernel))
|
|
|
|
####################################
|
|
# LocalSplitU reduction
|
|
####################################
|
|
#if kernel["NumThreads"]%kernel["MacroTile0"] == 0:
|
|
if kernel["LocalSplitU"] > 1:
|
|
kl.append(self.comment3("LocalSplitU Reduction"))
|
|
if self.enable["Sync"]:
|
|
# not generate sync code when both DirectToVgprA and B are enabled
|
|
# in this case, LDS is not used and no need to sync to use LDS for LocalSplitU
|
|
if not (kernel["DirectToVgprA"] and kernel["DirectToVgprB"]):
|
|
kl.append(self.syncThreads(kernel))
|
|
|
|
# LocalSplitU: local write
|
|
kl.append(self.comment("LocalSplitU: local write"))
|
|
kl.append(self.localSplitULocalWrite(kernel))
|
|
|
|
# LocalSplitU: local read
|
|
kl.append(self.comment("LocalSplitU: local read"))
|
|
kl.append(self.localSplitULocalRead(kernel))
|
|
|
|
# LocalSplitU: local read
|
|
kl.append(self.comment("LocalSplitU: reduction"))
|
|
kl.append(self.localSplitUReduction(kernel))
|
|
|
|
# LocalSplitU: global write indices
|
|
kl.append(self.comment("LocalSplitU: global write indices"))
|
|
kl.append(self.localSplitUGlobalWriteIndices(kernel))
|
|
|
|
# LocalSplitU: global write
|
|
kl.append(self.comment("LocalSplitU: global write"))
|
|
kl.append(self.localSplitUGlobalWrite(kernel))
|
|
|
|
else:
|
|
####################################
|
|
# NOT LocalSplitU
|
|
####################################
|
|
|
|
# global write indices
|
|
kl.append(self.comment("not-LocalSplitU: global write indices"))
|
|
kl.append(self.notLocalSplitUGlobalWriteIndices(kernel))
|
|
|
|
# global write
|
|
kl.append(self.comment("not-LocalSplitU: global write"))
|
|
kl.append(self.notLocalSplitUGlobalWrite(kernel))
|
|
|
|
# After we know the #-of-globalwrite instructions, we can go back to replace the pre-loop LW vmcnt
|
|
# Note that currently, this code-replacement occurs only when PrefetchAcrossPersistent=True,
|
|
# otherwise, nothing is changed
|
|
if self.preLoopLocalWriteCode != None:
|
|
self.replacePreLoopLWVmcnt(kernel)
|
|
|
|
# function suffix
|
|
kl.append(self.functionEnd(kernel, True))
|
|
kl.append(self.functionSuffix(kernel))
|
|
|
|
kStr = '\n'.join([str(x) for x in kl])
|
|
# init code opt
|
|
if placeholderInitCodeOpt != None:
|
|
# replace placeholder with localWriteCode
|
|
kStrLW = '\n'.join([str(x) for x in kl_LW])
|
|
kStr = kStr.replace(placeholderInitCodeOpt, kStrLW)
|
|
afterFunctionSignature = kStr
|
|
|
|
error = self.overflowedResources
|
|
|
|
# function signature last since it needs to know how many gprs were actually used
|
|
kStr = beforeFunctionSignature + self.functionSignature(kernel) + afterFunctionSignature
|
|
return (error,kStr)
|
|
|
|
##############################################################################
|
|
# Kernel Body
|
|
##############################################################################
|
|
def generateLocalReadAddressCode( self, kernel, tensorParametersA, tensorParametersB ):
|
|
kl = []
|
|
# if code is already generated, just return
|
|
if self.isLocalReadAddressCodeGenerated:
|
|
return kl
|
|
####################################
|
|
# Local Read Addresses
|
|
####################################
|
|
kl.append(self.comment3("Local Read Addresses"))
|
|
|
|
# tile assignments
|
|
kl.append(self.comment("local read addresses: tile assignments a/b"))
|
|
kl.append(self.lraTileAssignment(kernel, tensorParametersA, tensorParametersB))
|
|
|
|
# final offsets
|
|
kl.append(self.comment("local read addresses: final offsets a"))
|
|
kl.append(self.lraFinalOffset(kernel, tensorParametersA))
|
|
kl.append(self.comment("local read addresses: final offsets b"))
|
|
kl.append(self.lraFinalOffset(kernel, tensorParametersB))
|
|
|
|
# declare addresses
|
|
kl.append(self.comment("local read addresses: declare addresses a"))
|
|
kl.append(self.lraDeclareAddresses(kernel, tensorParametersA))
|
|
kl.append(self.comment("local read addresses: declare addresses b"))
|
|
kl.append(self.lraDeclareAddresses(kernel, tensorParametersB))
|
|
|
|
# set flag
|
|
self.isLocalReadAddressCodeGenerated = True
|
|
|
|
return kl
|
|
|
|
##############################################################################
|
|
#
|
|
# Functions to Write Kernel Segments
|
|
#
|
|
##############################################################################
|
|
|
|
def comment1(self, text):
|
|
"""
|
|
single line comment
|
|
"""
|
|
|
|
s = ""
|
|
s += self.indent
|
|
s += self.commentPrefix
|
|
s += " %s " % text
|
|
s += self.commentSuffix
|
|
s += self.endLine
|
|
return s
|
|
|
|
def comment(self, text):
|
|
"""
|
|
comment with prior newline
|
|
"""
|
|
|
|
s = ""
|
|
s += self.endLine
|
|
s += self.comment1(text)
|
|
return s
|
|
|
|
def comment3(self, text):
|
|
"""
|
|
3-line comment
|
|
"""
|
|
|
|
s = ""
|
|
s += self.endLine
|
|
s += self.indent
|
|
s += self.commentPrefix
|
|
s += self.commentHR
|
|
s += self.commentSuffix
|
|
s += self.endLine
|
|
|
|
for line in text.split("\n"):
|
|
s += self.indent
|
|
s += self.commentPrefix
|
|
s += " %-38s " % line
|
|
s += self.commentSuffix
|
|
s += self.endLine
|
|
|
|
s += self.indent
|
|
s += self.commentPrefix
|
|
s += self.commentHR
|
|
s += self.commentSuffix
|
|
s += self.endLine
|
|
return s
|
|
|
|
##############################################################################
|
|
# Init Kernel
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def initKernel(self, kernel, tensorParametersA, tensorParametersB ):
|
|
|
|
if "ISA" not in kernel:
|
|
raise ValueError(f"ISA not found in kernel {kernel}. This is required for assembly kernels.")
|
|
|
|
self.staggerU = kernel["StaggerU"]
|
|
if self.staggerU:
|
|
assert (kernel["KernelLanguage"]=="Source" or kernel["BufferLoad"])
|
|
self.tPA = tensorParametersA
|
|
self.tPB = tensorParametersB
|
|
|
|
# Only assembly supports scheduling
|
|
self.canSchedule = (kernel["KernelLanguage"] == "Assembly")
|
|
|
|
if self.canSchedule:
|
|
self.scheduleGlobalRead = kernel["ScheduleGlobalRead"] \
|
|
and kernel["PrefetchGlobalRead"]
|
|
else:
|
|
self.scheduleGlobalRead = 0
|
|
|
|
if self.canSchedule:
|
|
self.scheduleLocalWrite = kernel["ScheduleLocalWrite"] \
|
|
and kernel["PrefetchGlobalRead"]
|
|
else:
|
|
self.scheduleLocalWrite = 0
|
|
|
|
if self.canSchedule:
|
|
self.scheduleIterAlg = kernel["ScheduleIterAlg"]
|
|
else:
|
|
self.scheduleIterAlg = 0
|
|
|
|
self.prefetchAcrossPersistent = kernel["PrefetchAcrossPersistent"]
|
|
|
|
self.storeCInUnroll = kernel["StoreCInUnroll"]
|
|
|
|
# NoTailLoop optimization
|
|
# Case 1 (NoTailLoop = 1): just remove TailLoop
|
|
# - ASEM%GSU=0 and ASEM/GSU is multiple of DepthU. TailLoop code will not be used in this case.
|
|
# Case 2(NoTailLoop = 2): generate TailLoop code in NoLoadLoop (last loop code) and remove TailLoop
|
|
# all of the following conditions should be true
|
|
# - BufferLoad = True
|
|
# - SuppressNoLoadLoop = False
|
|
# - MatrixInstruction + MatrixInstK > 1
|
|
# - global read width for TailLoop decided by assert is multiple of GlobalLoadVectorWidthA/B
|
|
# (this is necessary to use prefetch global read fot tail loop without out of range access at the edge)
|
|
# - GlobalSplitU = 1
|
|
# GSU>1 case, remaining K is distributed unevenly and does not work with tailLoop in noLoadLoop
|
|
# - PersistentKernel = 0
|
|
# - StreamK = 0
|
|
# - DepthULdsDivisor = 1
|
|
# - StaggerU = 0
|
|
# StaggerU=0 case, we can exit NoLoadLoop earlier when whole K range is processed
|
|
# - InnerUnroll = 1
|
|
# K mask part does not work properly with InnerUnroll>1
|
|
# Case 3 (NoTailLoop = 3): Case 2 + StaggerU != 0 + (NT + BufferLoad (except for DirectToLds)
|
|
# - StaggerU>0 and NT(+BufferLoad)
|
|
# if StaggerU>0, the partial K part can be in unroll and K mask cannot be handled in NoLoadLoop
|
|
# If NT and BufferLoad, global load for out of range K is always 0 because out of range K address
|
|
# (This is not true in DirectToLds case)
|
|
# is always out of array load (means load 0)
|
|
# If StaggerU is enabled, cannot exit unless whole code in NoLoadLoop is done
|
|
#
|
|
# Reject the following cases if noTailLoop is not enabled
|
|
# - PrefetchAcrossPersistent and PrefetchAcrossPersistentMode
|
|
# PrefetchAcrossPersistentMode does not support TailLoop (TLU is necessary for NoTailLoop)
|
|
# - DirectToLds + TLU + NumLoadsCoalesced > 1 (special local read offset conversion is not implemented in tail loop code)
|
|
# - DirectToLds + LRVW > 1
|
|
|
|
# global load width for tail loop (based on AssertFree0, 1 or AssertSummationElementMultiple)
|
|
asem = kernel["AssertSummationElementMultiple"]
|
|
# need to adjust asem for GSU
|
|
gsu = kernel["GlobalSplitU"]
|
|
asemDivGSU = 1 if asem%gsu !=0 else asem//gsu
|
|
# A
|
|
tluA = kernel["ProblemType"]["TLUA"]
|
|
glvwA = kernel["GlobalLoadVectorWidthA"]
|
|
afem = kernel["AssertFree0ElementMultiple"]
|
|
tailLoopLoadWidthA = afem if tluA else asem
|
|
# B
|
|
tluB = kernel["ProblemType"]["TLUB"]
|
|
glvwB = kernel["GlobalLoadVectorWidthB"]
|
|
afem = kernel["AssertFree1ElementMultiple"]
|
|
tailLoopLoadWidthB = afem if tluB else asem
|
|
# if glvw is not power of 2, use 1
|
|
if glvwA <= 1 or (glvwA & (glvwA - 1)):
|
|
tailLoopLoadWidthA = 1
|
|
if glvwB <= 1 or (glvwB & (glvwB - 1)):
|
|
tailLoopLoadWidthB = 1
|
|
|
|
noTailLoop = 0
|
|
if (asemDivGSU % kernel["DepthU"] == 0):
|
|
noTailLoop = 1
|
|
elif kernel["BufferLoad"] and (not kernel["SuppressNoLoadLoop"]) and \
|
|
kernel["EnableMatrixInstruction"] and kernel["MatrixInstK"] > 1 and \
|
|
(glvwA <= 1 or (tailLoopLoadWidthA % glvwA == 0)) and (glvwB <= 1 or (tailLoopLoadWidthB % glvwB == 0)) and \
|
|
gsu == 1 and kernel["PersistentKernel"] == 0 and kernel["StreamK"] == 0 and kernel["DepthULdsDivisor"] == 1 and \
|
|
kernel["InnerUnroll"] == 1:
|
|
if kernel["StaggerU"] == 0:
|
|
noTailLoop = 2
|
|
elif (tluA and (not kernel["DirectToLdsA"]) and tluB and (not kernel["DirectToLdsB"])):
|
|
noTailLoop = 3
|
|
|
|
# no tail loop optimization setting
|
|
# noTailLoop=1: remove TailLoop
|
|
# noTailLoop=2: remove TailLoop and generate TailLoop in NoLoadLoop with early exit
|
|
# noTailLoop=3: remove TailLoop and generate TailLoop in NoLoadLoop without early exit
|
|
self.noTailLoop = noTailLoop > 0
|
|
self.tailLoopInNLL = noTailLoop >= 2
|
|
self.noEarlyExitForTailLoopInNLL = noTailLoop == 3
|
|
|
|
# DTV odd check
|
|
# need DTV odd code if (asem//GSU) is not multiple of DepthU*2
|
|
self.generateDTVodd = False
|
|
if ( kernel["DirectToVgprA"] or kernel["DirectToVgprB"]) and (asemDivGSU % (kernel["DepthU"] * 2) != 0):
|
|
self.generateDTVodd = True
|
|
|
|
self.actualSummationLoops = 1 if kernel["PackSummationDims"] else kernel["ProblemType"]["NumIndicesSummation"]
|
|
self.otherSummationLoops = self.actualSummationLoops-1
|
|
self.otherSummations = kernel["ProblemType"]["NumIndicesSummation"]-1 # not loops but summations vars
|
|
|
|
# If 0, unroll loop is decremented by 1 each iteration and scaled by DEPTHU when number of summation elements
|
|
# is required.
|
|
# if 1, unroll loop starts at 0 and increments by DEPTHU. No scaling is required. This mode is required
|
|
# for pack summation dims, but can also be used independently and this is useful for isolation and testing.
|
|
self.unrollIncIsDepthU = kernel["UnrollIncIsDepthU"] or kernel["PackSummationDims"] \
|
|
or bool(kernel["ProblemType"]["ZeroPadA"]) or bool(kernel["ProblemType"]["ZeroPadB"])
|
|
|
|
# turn on parts of prefetchAcrossPersistent code for testing
|
|
self.prefetchAcrossPersistent0 = 0 or self.prefetchAcrossPersistent
|
|
self.canOptimizePreLoopLWVmcnt = kernel["OptPreLoopVmcnt"]
|
|
|
|
self.enable = {}
|
|
dkp = kernel["DisableKernelPieces"]
|
|
# Can locally overrid these by changing True to False or
|
|
# use the DisableKernelPieces for a quick search (see Common.py)
|
|
self.enable["PreLoop"] = True and not (dkp>0 and dkp >= 7) and not dkp == -7
|
|
self.enable["GlobalRead"] = True and not (dkp>0 and dkp >= 2) and not dkp == -2
|
|
self.enable["GlobalReadInc"] = True and not (dkp>0 and dkp >= 7) and not dkp == -7
|
|
self.enable["LocalWrite"] = True and not (dkp>0 and dkp >= 3) and not dkp == -3
|
|
self.enable["LocalRead"] = True and not (dkp>0 and dkp >= 4) and not dkp == -4
|
|
self.enable["Wait"] = True and not (dkp>0 and dkp >= 5) and not dkp == -5
|
|
self.enable["Sync"] = True and not (dkp>0 and dkp >= 5) and not dkp == -5
|
|
self.enable["MAC"] = True and not (dkp>0 and dkp >= 6) and not dkp == -6
|
|
self.enable["PostLoop"] = True and not (dkp>0 and dkp >= 1) and not dkp == -1
|
|
self.enable["InvalidLocalReadA"] = dkp == -10 or dkp == -12
|
|
self.enable["InvalidLocalReadB"] = dkp == -11 or dkp == -12
|
|
self.enable["InvalidLocalWriteA"] = dkp == -13 or dkp == -15
|
|
self.enable["InvalidLocalWriteB"] = dkp == -14 or dkp == -15
|
|
self.enable["InvalidGlobalReadA"] = (dkp == -16 or dkp == -18) and kernel["BufferLoad"]
|
|
self.enable["InvalidGlobalReadB"] = (dkp == -17 or dkp == -18) and kernel["BufferLoad"]
|
|
|
|
#if dkp:
|
|
# print "\nKernelWriter enable:", self.enable
|
|
|
|
if kernel["KernelLanguage"] == "Source":
|
|
self.language = globalParameters["RuntimeLanguage"]
|
|
else:
|
|
self.language = "ASM"
|
|
self.indexChars = []
|
|
for i in range(0, len(globalParameters["IndexChars"])):
|
|
self.indexChars.append(globalParameters["IndexChars"][i])
|
|
self.indexChars[kernel["ProblemType"]["Index0"]] \
|
|
= "0" + self.indexChars[kernel["ProblemType"]["Index0"]]
|
|
self.indexChars[kernel["ProblemType"]["Index1"]] \
|
|
= "1" + self.indexChars[kernel["ProblemType"]["Index1"]]
|
|
self.unrollIdx = kernel["ProblemType"]["NumIndicesSummation"]-1
|
|
self.unrollChar = \
|
|
self.indexChars[kernel["ProblemType"]["IndicesSummation"][\
|
|
self.unrollIdx]]
|
|
self.tileChar0 = self.indexChars[kernel["ProblemType"]["Index0"]]
|
|
self.tileChar1 = self.indexChars[kernel["ProblemType"]["Index1"]]
|
|
self.tileCharA = self.tileChar0 if (kernel["ProblemType"]["Tensor0"]==0) \
|
|
else self.tileChar1
|
|
self.tileCharB = self.tileChar0 if (kernel["ProblemType"]["Tensor0"]==1) \
|
|
else self.tileChar1
|
|
|
|
"""
|
|
if kernel["ProblemType"]["Tensor0"]==0:
|
|
kernel["ThreadTileA"] = kernel["ThreadTile0"]
|
|
kernel["ThreadTileB"] = kernel["ThreadTile1"]
|
|
kernel["SubGroupA"] = kernel["SubGroup0"]
|
|
kernel["SubGroupB"] = kernel["SubGroup1"]
|
|
kernel["MacroTileA"] = kernel["MacroTile0"]
|
|
kernel["MacroTileB"] = kernel["MacroTile1"]
|
|
else:
|
|
kernel["ThreadTileB"] = kernel["ThreadTile0"]
|
|
kernel["ThreadTileA"] = kernel["ThreadTile1"]
|
|
kernel["SubGroupB"] = kernel["SubGroup0"]
|
|
kernel["SubGroupA"] = kernel["SubGroup1"]
|
|
kernel["MacroTileB"] = kernel["MacroTile0"]
|
|
kernel["MacroTileA"] = kernel["MacroTile1"]
|
|
"""
|
|
|
|
########################################
|
|
# derrive global-read-coalesce-group from local in config
|
|
"""
|
|
if kernel["ProblemType"]["TLUA"]:
|
|
self.globalReadCoalesceGroupA = kernel["LocalWriteCoalesceGroupA"]
|
|
else:
|
|
self.globalReadCoalesceGroupA = not kernel["LocalWriteCoalesceGroupA"]
|
|
if kernel["ProblemType"]["TLUB"]:
|
|
self.globalReadCoalesceGroupB = kernel["LocalWriteCoalesceGroupB"]
|
|
else:
|
|
self.globalReadCoalesceGroupB = not kernel["LocalWriteCoalesceGroupB"]
|
|
"""
|
|
self.globalReadCoalesceGroupA = kernel["GlobalReadCoalesceGroupA"]
|
|
self.globalReadCoalesceGroupB = kernel["GlobalReadCoalesceGroupB"]
|
|
"""
|
|
# original parameters
|
|
NumLoadsCoalesced -> NumLoadsPerpendicular
|
|
# new intermediate parameters
|
|
numReadsTile # nrt
|
|
numReadsUnroll # nru
|
|
numReadsTileVecComp # nrvt
|
|
numReadsUnrollVecComp # nrvu
|
|
numWritesCoal # nwc
|
|
numWritesPerp # nwp
|
|
numWritesCoalVecComp # nwvc
|
|
numWritesPerpVecComp # nwvp
|
|
readTileComponents (based on grcv)
|
|
readTileVector
|
|
"""
|
|
|
|
# TODO load sub-vector
|
|
vwa = kernel["GlobalLoadVectorWidthA"]
|
|
vwb = kernel["GlobalLoadVectorWidthB"]
|
|
|
|
# lrvwTileA,B
|
|
# lrvwTileA,B > 1 is to use wider local read + v_perm
|
|
# MIInputPerThread > 1 case, we need MIInputPerThread continuous K elements
|
|
# lrvwTileA,B should not exceed MIInputPerThread
|
|
# = 2 means, load 2 continuous M or N and do swap
|
|
# (M,K) = (0,0) (1,0)
|
|
# (0,1) (1,1)
|
|
# to
|
|
# (M,K) = (0,0) (0,1)
|
|
# (1,0) (1,1)
|
|
# = 4 means, load 4 continuous M or N and do swap
|
|
# (M,K) = (0,0) (1,0) (2,0) (3,0)
|
|
# (0,1) (1,1) (2,1) (3,1)
|
|
# (0,2) (1,2) (2,2) (3,2)
|
|
# (0,3) (1,3) (2,3) (3,3)
|
|
# to
|
|
# (M,K) = (0,0) (0,1) (0,2) (0,3)
|
|
# (1,0) (1,1) (1,2) (1,3)
|
|
# (2,0) (2,1) (2,2) (2,3)
|
|
# (3,0) (3,1) (3,2) (3,3)
|
|
# it requires
|
|
# EnableMatrixInstruction + MIInputPerThread > 1
|
|
# SourceSwap only (TODO: non SourceSwap)
|
|
# VgprForLocalReadPacking (need dedicated vpgr for packing)
|
|
# ClusterLocalRead
|
|
# not UnrollMajorLDS
|
|
# VectorWidthA,B > 1
|
|
self.lrvwTileA = 1
|
|
self.lrvwTileB = 1
|
|
self.useWiderLocalReadB = False
|
|
if kernel["EnableMatrixInstruction"] and kernel["MIInputPerThread"] > 1 and \
|
|
kernel["VgprForLocalReadPacking"] and kernel["ClusterLocalRead"]:
|
|
if (not kernel["UnrollMajorLDSA"]):
|
|
self.lrvwTileA = min(kernel["MIInputPerThread"], kernel["VectorWidthA"]) # should not exceed MIInputPerThread
|
|
if (not kernel["UnrollMajorLDSB"]):
|
|
self.lrvwTileB = min(kernel["MIInputPerThread"], kernel["VectorWidthB"]) # should not exceed MIInputPerThread
|
|
# DirectToVgpr + pack (v_perm)
|
|
self.packDTVA = kernel["DirectToVgprA"] and self.lrvwTileA > 1
|
|
self.packDTVB = kernel["DirectToVgprB"] and self.lrvwTileB > 1
|
|
|
|
self.numItersPLR = kernel["PrefetchLocalRead"]%kernel["LoopIters"]
|
|
self.numVgprBuffer = kernel["LoopIters"] if kernel["PrefetchLocalRead"] > kernel["LoopIters"] else kernel["PrefetchLocalRead"]
|
|
if kernel["UnrollMajorLDSA"]:
|
|
self.lrvwA = kernel["LocalReadVectorWidth"]
|
|
else:
|
|
if kernel["EnableMatrixInstruction"]:
|
|
# MI + UMLDS, we need minimum of MIInputPerThread for lrvw
|
|
self.lrvwA = kernel["MIInputPerThread"]
|
|
if kernel["DirectToVgprA"]:
|
|
# DirectToVgprA case, ignore LocalReadVectorWidth and use GlobalLoadVectorWidth instead.
|
|
self.lrvwA = vwa
|
|
else:
|
|
self.lrvwA = 1
|
|
if kernel["UnrollMajorLDSB"]:
|
|
self.lrvwB = kernel["LocalReadVectorWidth"]
|
|
else:
|
|
if kernel["EnableMatrixInstruction"]:
|
|
# MI + UMLDS, we need minimum of MIInputPerThread for lrvw
|
|
self.lrvwB = kernel["MIInputPerThread"]
|
|
if kernel["DirectToVgprB"]:
|
|
# DirectToVgprB case, ignore LocalReadVectorWidth and use GlobalLoadVectorWidth instead.
|
|
self.lrvwB = vwb
|
|
elif kernel["DirectToVgprA"] and kernel["ProblemType"]["TLUA"]:
|
|
# MI + UMLDS, we need minimum of MIInputPerThread for lrvw
|
|
# DirectToVgprA + TLUA + UnrollMajorLDSB=False case, allow wider LocalReadVectorWidth
|
|
self.lrvwB = kernel["LocalReadVectorWidth"]
|
|
self.useWiderLocalReadB = self.lrvwB > kernel["MIInputPerThread"]
|
|
else:
|
|
self.lrvwB = 1
|
|
|
|
# Wider LocalRead
|
|
if kernel["EnableMatrixInstruction"]:
|
|
self.numReadsIterCoalescedA = ceil(self.lrvwA / kernel["MIInputPerThread"]) if kernel["UnrollMajorLDSA"] else 1
|
|
self.numReadsIterCoalescedB = ceil(self.lrvwB / kernel["MIInputPerThread"]) if kernel["UnrollMajorLDSB"] else 1
|
|
else:
|
|
self.numReadsIterCoalescedA = 1
|
|
self.numReadsIterCoalescedB = 1
|
|
self.numIterPerCoalescedReadA = max(1,self.numReadsIterCoalescedA//kernel["InnerUnroll"])
|
|
self.numIterPerCoalescedReadB = max(1,self.numReadsIterCoalescedB//kernel["InnerUnroll"])
|
|
|
|
if kernel["ScheduleIterAlg"] == 3 or kernel["ScheduleIterAlg"] == 2:
|
|
self.numMfmaPerIter = kernel["MIWaveTile"][0] * kernel["MIWaveTile"][1] * kernel["InnerUnroll"]
|
|
if kernel["ProblemType"]["DataType"].isComplex(): self.numMfmaPerIter *= 4
|
|
|
|
########################################
|
|
# read vectors or vector components
|
|
########################################
|
|
if kernel["ProblemType"]["TLUA"]: # NT no transpose
|
|
self.numReadsTileA = kernel["NumLoadsCoalescedA"]
|
|
self.numReadsUnrollA = kernel["NumLoadsPerpendicularA"]
|
|
if kernel["GlobalReadCoalesceVectorA"]: # read vectors
|
|
self.readTileDimComponentsA = False # Vector
|
|
self.readTileDimVectorA = True # Vector
|
|
self.readUnrollDimComponentsA = False # Scalar
|
|
self.readUnrollDimVectorA = False # Scalar
|
|
self.numReadsTileVecCompA = vwa
|
|
self.numReadsUnrollVecCompA = 1
|
|
else: # read components, write components
|
|
self.readTileDimComponentsA = False # Scalar
|
|
self.readTileDimVectorA = False # Scalar
|
|
self.readUnrollDimComponentsA = kernel["VectorWidthA"] > 1 # Components
|
|
self.readUnrollDimVectorA = False # Components
|
|
self.numReadsTileVecCompA = 1
|
|
self.numReadsUnrollVecCompA = vwa
|
|
else: # TN yes transpose
|
|
self.numReadsTileA = kernel["NumLoadsPerpendicularA"]
|
|
self.numReadsUnrollA = kernel["NumLoadsCoalescedA"]
|
|
if kernel["GlobalReadCoalesceVectorA"]: # read vector
|
|
self.readTileDimComponentsA = False # Scalar
|
|
self.readTileDimVectorA = False # Scalar
|
|
self.readUnrollDimComponentsA = False # Vector
|
|
self.readUnrollDimVectorA = True # Vector
|
|
self.numReadsUnrollVecCompA = vwa
|
|
self.numReadsTileVecCompA = 1
|
|
else: # read components, write vectors
|
|
self.readTileDimComponentsA = kernel["VectorWidthA"] > 1 # Components
|
|
self.readTileDimVectorA = False # Components
|
|
self.readUnrollDimComponentsA = False # Scalar
|
|
self.readUnrollDimVectorA = False # Scalar
|
|
# NEW
|
|
self.numReadsUnrollVecCompA = 1
|
|
self.numReadsTileVecCompA = vwa
|
|
|
|
########################################
|
|
# write vectors or vector components
|
|
########################################
|
|
if kernel["ProblemType"]["TLUA"] != kernel["UnrollMajorLDSA"]: # NT no transpose
|
|
self.numWritesCoalA = kernel["NumLoadsCoalescedA"]
|
|
if kernel["GlobalReadCoalesceVectorA"]: # read vectors, write vectors
|
|
self.writeUnrollDimComponentsA = False # Scalar
|
|
if kernel["LocalDotLayout"]>1:
|
|
self.writeTileDimComponentsA = kernel["GlobalLoadVectorWidthA"] > 1 # Components
|
|
writeCoal = False
|
|
else:
|
|
self.writeTileDimComponentsA = False # Vector
|
|
writeCoal = True
|
|
else: # read components, write components
|
|
self.writeTileDimComponentsA = False # Scalar
|
|
self.writeUnrollDimComponentsA = kernel["GlobalLoadVectorWidthA"] > 1 # Components
|
|
writeCoal = False
|
|
else: # TN yes transpose
|
|
self.numWritesCoalA = kernel["NumLoadsPerpendicularA"]
|
|
if kernel["GlobalReadCoalesceVectorA"]: # read vector, write components
|
|
self.writeUnrollDimComponentsA = False # Scalar
|
|
if kernel["LocalDotLayout"]>1:
|
|
self.writeTileDimComponentsA = kernel["GlobalLoadVectorWidthA"] > 1 # Components
|
|
# LDS writes with LDL>1 will never be coalesced
|
|
writeCoal = False
|
|
else:
|
|
self.writeTileDimComponentsA = kernel["GlobalLoadVectorWidthA"] > 1 # Components
|
|
writeCoal = False
|
|
else: # read components, write vectors
|
|
self.writeTileDimComponentsA = False # Vector
|
|
self.writeUnrollDimComponentsA = False # Scalar
|
|
writeCoal = True
|
|
|
|
# writeCoal indicates writes should be done in the coal dim
|
|
# else in perp
|
|
if writeCoal:
|
|
self.numWritesCoalVecCompA = vwa / kernel["DepthULdsDivisor"] if vwa < 1 else vwa // kernel["DepthULdsDivisor"]
|
|
self.numWritesPerpVecCompA = 1
|
|
else:
|
|
self.numWritesCoalVecCompA = 1
|
|
self.numWritesPerpVecCompA = vwa
|
|
del writeCoal
|
|
|
|
self.numReadVectorComponentsA = kernel["GlobalLoadVectorWidthA"] \
|
|
if (self.readTileDimComponentsA \
|
|
or self.readUnrollDimComponentsA) else 1
|
|
# self.numWriteVectorComponentsA = kernel["GlobalLoadVectorWidthA"] \
|
|
# if (self.writeTileDimComponentsA \
|
|
# or self.writeUnrollDimComponentsA) else 1
|
|
# self.numReadTileVectorComponentsA = kernel["GlobalLoadVectorWidthA"] \
|
|
# if self.readTileDimComponentsA else 1 # for branches
|
|
# convert tile/unroll to para/perp
|
|
if kernel["ProblemType"]["TLUA"]:
|
|
self.numReadsCoalVecCompA = self.numReadsTileVecCompA
|
|
self.numReadsPerpVecCompA = self.numReadsUnrollVecCompA
|
|
# for asm
|
|
self.readCoalescedComponentsA = self.readTileDimComponentsA
|
|
# self.readCoalescedVectorA = self.readTileDimVectorA # Not Used
|
|
self.readPerpendicularComponentsA = self.readUnrollDimComponentsA
|
|
# self.readPerpendicularVectorA = self.readUnrollDimVectorA # Not Used
|
|
else:
|
|
self.numReadsCoalVecCompA = self.numReadsUnrollVecCompA
|
|
self.numReadsPerpVecCompA = self.numReadsTileVecCompA
|
|
# for asm
|
|
self.readCoalescedComponentsA = self.readUnrollDimComponentsA
|
|
# self.readCoalescedVectorA = self.readUnrollDimVectorA # Not Used
|
|
self.readPerpendicularComponentsA = self.readTileDimComponentsA
|
|
# self.readPerpendicularVectorA = self.readTileDimVectorA # Not Used
|
|
|
|
####################################
|
|
# read vectors or vector components b
|
|
####################################
|
|
if kernel["ProblemType"]["TLUB"]: # NT no transpose
|
|
self.numReadsTileB = kernel["NumLoadsCoalescedB"]
|
|
self.numReadsUnrollB = kernel["NumLoadsPerpendicularB"]
|
|
if kernel["GlobalReadCoalesceVectorB"]:
|
|
self.readTileDimComponentsB = False # Vector
|
|
self.readTileDimVectorB = True # Vector
|
|
self.readUnrollDimComponentsB = False # Scalar
|
|
self.readUnrollDimVectorB = False # Scalar
|
|
self.numReadsTileVecCompB = vwb
|
|
self.numReadsUnrollVecCompB = 1
|
|
else:
|
|
self.readTileDimComponentsB = False # Scalar
|
|
self.readTileDimVectorB = False # Scalar
|
|
self.readUnrollDimComponentsB = kernel["VectorWidthB"] > 1 # Components
|
|
self.readUnrollDimVectorB = False # Components
|
|
# NEW
|
|
self.numReadsTileVecCompB = 1
|
|
self.numReadsUnrollVecCompB = vwb
|
|
else: # TN yes transpose
|
|
self.numReadsTileB = kernel["NumLoadsPerpendicularB"]
|
|
self.numReadsUnrollB = kernel["NumLoadsCoalescedB"]
|
|
if kernel["GlobalReadCoalesceVectorB"]:
|
|
self.readTileDimComponentsB = False # Scalar
|
|
self.readTileDimVectorB = False # Scalar
|
|
self.readUnrollDimComponentsB = False # Vector
|
|
self.readUnrollDimVectorB = True # Vector
|
|
self.numReadsUnrollVecCompB = vwb
|
|
self.numReadsTileVecCompB = 1
|
|
else:
|
|
self.readTileDimComponentsB = kernel["VectorWidthB"] > 1 # Components
|
|
self.readTileDimVectorB = False # Components
|
|
self.readUnrollDimComponentsB = False # Scalar
|
|
self.readUnrollDimVectorB = False # Scalar
|
|
# NEW
|
|
self.numReadsUnrollVecCompB = 1
|
|
self.numReadsTileVecCompB = vwb
|
|
|
|
####################################
|
|
# write vectors or vector components b
|
|
####################################
|
|
if kernel["ProblemType"]["TLUB"] != kernel["UnrollMajorLDSB"]: # NT no transpose
|
|
self.numWritesCoalB = kernel["NumLoadsCoalescedB"]
|
|
if kernel["GlobalReadCoalesceVectorB"]:
|
|
self.writeUnrollDimComponentsB = False # Vector
|
|
if kernel["LocalDotLayout"]>1:
|
|
self.writeTileDimComponentsB = kernel["GlobalLoadVectorWidthB"] > 1 # Components
|
|
writeCoal = False
|
|
else:
|
|
self.writeTileDimComponentsB = False # Vector
|
|
writeCoal = True
|
|
else:
|
|
self.writeTileDimComponentsB = False # Scalar
|
|
self.writeUnrollDimComponentsB = kernel["GlobalLoadVectorWidthB"] > 1 # Components
|
|
# NEW
|
|
self.numWritesCoalVecCompB = 1
|
|
self.numWritesPerpVecCompB = vwb
|
|
else: # TN yes transpose
|
|
self.numWritesCoalB = kernel["NumLoadsPerpendicularB"]
|
|
if kernel["GlobalReadCoalesceVectorB"]:
|
|
self.writeUnrollDimComponentsB = False
|
|
if kernel["LocalDotLayout"]>1:
|
|
self.writeTileDimComponentsB = kernel["GlobalLoadVectorWidthB"] > 1 # Components
|
|
# LDS writes with LDL>1 will never be coalesced
|
|
writeCoal = False
|
|
else:
|
|
self.writeTileDimComponentsB = kernel["GlobalLoadVectorWidthB"] > 1 # Components
|
|
writeCoal = False
|
|
else:
|
|
self.writeTileDimComponentsB = False # Vector
|
|
self.writeUnrollDimComponentsB = False # Scalar
|
|
# NEW
|
|
self.numWritesCoalVecCompB = vwb
|
|
self.numWritesPerpVecCompB = 1
|
|
|
|
# writeCoal indicates writes should be done in the coal dim
|
|
# else in perp
|
|
if writeCoal:
|
|
self.numWritesCoalVecCompB = vwb / kernel["DepthULdsDivisor"] if vwb < 1 else vwb // kernel["DepthULdsDivisor"]
|
|
self.numWritesPerpVecCompB = 1
|
|
else:
|
|
self.numWritesCoalVecCompB = 1
|
|
self.numWritesPerpVecCompB = vwb
|
|
del writeCoal
|
|
|
|
# numReadVectorComponentsB is refers to global reads
|
|
self.numReadVectorComponentsB = kernel["GlobalLoadVectorWidthB"] \
|
|
if (self.readTileDimComponentsB \
|
|
or self.readUnrollDimComponentsB) else 1
|
|
# self.numWriteVectorComponentsB = kernel["GlobalLoadVectorWidthB"] \
|
|
# if (self.writeTileDimComponentsB \
|
|
# or self.writeUnrollDimComponentsB) else 1
|
|
# self.numReadTileVectorComponentsB = kernel["GlobalLoadVectorWidthB"] \
|
|
# if self.readTileDimComponentsB else 1 # for branches
|
|
# convert tile/unroll to para/perp
|
|
if kernel["ProblemType"]["TLUB"]:
|
|
self.numReadsCoalVecCompB = self.numReadsTileVecCompB
|
|
self.numReadsPerpVecCompB = self.numReadsUnrollVecCompB
|
|
# for asm
|
|
self.readCoalescedComponentsB = self.readTileDimComponentsB
|
|
# self.readCoalescedVectorB = self.readTileDimVectorB # Not Used
|
|
self.readPerpendicularComponentsB = self.readUnrollDimComponentsB
|
|
# self.readPerpendicularVectorB = self.readUnrollDimVectorB # Not Used
|
|
else:
|
|
self.numReadsCoalVecCompB = self.numReadsUnrollVecCompB
|
|
self.numReadsPerpVecCompB = self.numReadsTileVecCompB
|
|
# for asm
|
|
self.readCoalescedComponentsB = self.readUnrollDimComponentsB
|
|
# self.readCoalescedVectorB = self.readUnrollDimVectorB # Not Used
|
|
self.readPerpendicularComponentsB = self.readTileDimComponentsB
|
|
# self.readPerpendicularVectorB = self.readTileDimVectorB # Not Used
|
|
|
|
####################################
|
|
# load sizes
|
|
"""
|
|
if kernel["ProblemType"]["TLUA"]:
|
|
kernel["LSCA"] = kernel["MacroTileA"] \
|
|
/ kernel["NumLoadsCoalescedA"]
|
|
kernel["LSPA"] = kernel["DepthU"] / kernel["NumLoadsPerpendicularA"]
|
|
else:
|
|
kernel["LSCA"] = kernel["DepthU"] / kernel["NumLoadsCoalescedA"]
|
|
kernel["LSPA"] = kernel["MacroTileA"] \
|
|
/ kernel["NumLoadsPerpendicularA"]
|
|
if kernel["ProblemType"]["TLUB"]:
|
|
kernel["LSCB"] = kernel["MacroTileB"] \
|
|
/ kernel["NumLoadsCoalescedB"]
|
|
kernel["LSPB"] = kernel["DepthU"] / kernel["NumLoadsPerpendicularB"]
|
|
else:
|
|
kernel["LSCB"] = kernel["DepthU"] / kernel["NumLoadsCoalescedB"]
|
|
kernel["LSPB"] = kernel["MacroTileB"] \
|
|
/ kernel["NumLoadsPerpendicularB"]
|
|
kernel["LVCA"] = kernel["LSCA"] / kernel["GlobalLoadVectorWidthA"]
|
|
kernel["LVCB"] = kernel["LSCB"] / kernel["GlobalLoadVectorWidthB"]
|
|
kernel["LVPA"] = kernel["LSPA"] / kernel["GlobalLoadVectorWidthA"]
|
|
kernel["LVPB"] = kernel["LSPB"] / kernel["GlobalLoadVectorWidthB"]
|
|
"""
|
|
|
|
self.getTensorParameters(tensorParametersA, kernel, True)
|
|
self.getTensorParameters(tensorParametersB, kernel, False)
|
|
|
|
tensorParametersA["PackBatchDims"] = kernel["PackBatchDims"] if kernel["PackBatchDims"] & 0x1 else 0
|
|
tensorParametersB["PackBatchDims"] = kernel["PackBatchDims"] if kernel["PackBatchDims"] & 0x2 else 0
|
|
tensorParametersA["PackedIndices"] = kernel["PackedC%uIndicesX"%self.tPA["tile01Idx"]]
|
|
tensorParametersB["PackedIndices"] = kernel["PackedC%uIndicesX"%self.tPB["tile01Idx"]]
|
|
|
|
# condition(s) to enable init accvgpr opt (use const "0" as an operand instead of initializing whole accvgpr)
|
|
self.useInitAccVgprOpt = False
|
|
# enable for the following conditions
|
|
if kernel["EnableMatrixInstruction"] and (kernel["PrefetchGlobalRead"] == 1 or kernel["PrefetchGlobalRead"] == 2) \
|
|
and globalParameters["AsmCaps"][kernel["ISA"]]["HasMFMA_constSrc"] \
|
|
and kernel["StreamK"] == 0:
|
|
self.useInitAccVgprOpt = True
|
|
# force to disable for the following conditions
|
|
if self.useInitAccVgprOpt:
|
|
asgt3 = 0 if not (3 in kernel["AssertSizeGreaterThan"].keys()) else kernel["AssertSizeGreaterThan"][3]
|
|
gsu = kernel["GlobalSplitU"]
|
|
# PGR=1 case, K > DepthU * 1 is necessary ( if not noTailLoop, need > DepthU * 2)
|
|
# (kernel["AssertSizeGreaterThan"][3] > DepthU * GSU * 1 (or 2)
|
|
# PGR=2 case, K > DepthU * 2 is necessary ( if not noTailLoop, need > DepthU * 3)
|
|
# (kernel["AssertSizeGreaterThan"][3] > DepthU * GSU * 2 (or 3)
|
|
minDUnum = kernel["PrefetchGlobalRead"]
|
|
if not self.noTailLoop:
|
|
minDUnum += 1
|
|
if not (asgt3 >= kernel["DepthU"] * gsu * minDUnum):
|
|
tPrint(3, "InitAccVgprOpt is disabled because AssertSizeGreaterThan for K is not greater than DepthU * GSU * %u"%minDUnum)
|
|
self.useInitAccVgprOpt = False
|
|
|
|
# condition(s) to enable singleNLL opt
|
|
self.enableSingleNLLOpt = False
|
|
if self.noTailLoop and not (self.prefetchAcrossPersistent and kernel["PrefetchAcrossPersistentMode"] == 0):
|
|
if kernel["StoreCInUnroll"]:
|
|
self.enableSingleNLLOpt = True
|
|
# so far, not enabled for DirectToVgpr
|
|
# Performance is better with Tensile, but does not perform better with HPL
|
|
#if kernel["DirectToVgprA"] or kernel["DirectToVgprB"]:
|
|
# self.enableSingleNLLOpt = True
|
|
|
|
# condition(s) for swapMfmaInnerLoop
|
|
# swap inner and outer loop for mfmaIter
|
|
self.swapMfmaInnerLoop = False
|
|
if kernel["EnableMatrixInstruction"]:
|
|
if kernel["ProblemType"]["DataType"].isComplex() and tensorParametersB["tile01Idx"]:
|
|
# complex case, swap inner loop and outer loop so that idxA comes outer
|
|
# this is to re-use same tmp vgpr to nagate ai or ar
|
|
self.swapMfmaInnerLoop = True
|
|
if (not kernel["ProblemType"]["DataType"].isComplex()) and kernel["DirectToVgprA"] and kernel["PrefetchGlobalRead"] == 2:
|
|
# not Complex and DTVA + PGR2 case, use B for inner loop to schedule more mfma between DTVA global read instructions
|
|
# exception: both A and B pack code case
|
|
needPackAB = kernel["EnableMatrixInstruction"] and kernel["MIInputPerThread"] > 1 and \
|
|
((not kernel["UnrollMajorLDSA"]) and (not kernel["UnrollMajorLDSB"]))
|
|
if not needPackAB:
|
|
self.swapMfmaInnerLoop = True
|
|
|
|
# init code optimization
|
|
# generate local read/write address code and global read tile offset code before wait for kernel arg load (if applicable)
|
|
# set False here for source kernel (enable only for Asm)
|
|
self.isInitCodeOptLR = False
|
|
self.isInitCodeOptLW = False
|
|
|
|
@staticmethod
|
|
def zpForSumIdx(sumIdx, zeroPad):
|
|
""" Returns zero-pad for specified sumIdx if it matches or None if not """
|
|
return next((zpi for zpi in zeroPad if zpi[1] == sumIdx), None)
|
|
|
|
|
|
##############################################################################
|
|
# Function Prefix
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def functionPrefix(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Function Signature Prefix
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def functionSignaturePrefix(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Function Signature
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def functionSignature(self, kernel ):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Function Signature Suffix
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def functionSignatureSuffix(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Function Begin
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def functionBegin(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Allocate Resources
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def allocateResources(self, kernel, lraCode=None):
|
|
return ""
|
|
|
|
|
|
##############################################################################
|
|
# Open Persistent Loop
|
|
# init iteration counter, define loop target
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def openPersistentLoop(self, kernel):
|
|
return ""
|
|
|
|
|
|
##############################################################################
|
|
# Global Read Addresses: Work-Group
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def graWorkGroup(self, kernel, isPap):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Get Params For Tensor A/B
|
|
##############################################################################
|
|
def getTensorParameters(self, tP, kernel, tA):
|
|
tP["mirror"] = bool(kernel["ProblemType"]["MirrorDims%s" % ("A" if tA else "B")])
|
|
if tA: # A
|
|
tP["isA"] = True # is this tensor A
|
|
tP["isB"] = False # is this tensor B
|
|
tP["bpe"] = int(4*kernel["ProblemType"]["DataType"].numRegisters())
|
|
tP["tensorChar"] = "A" # tensor character A/B
|
|
tP["tensorIdx"] = 0 # tensor index A=0, B=1
|
|
tP["tileChar"] = self.tileCharA # tile char I0 or J1
|
|
tP["tileIdx"] = kernel["ProblemType"]["Index01A"] # is the tile dimension of A the 0th or 1th index, i.e. Aki, tileIdx=0
|
|
tP["tile01Idx"] = 1 if tP["tileIdx"] else 0
|
|
tP["lsc"] = "LSCA" # load size coalesced A, number of elements that get loaded along coalesced dimension with each load
|
|
tP["lsp"] = "LSPA" # load size perpendicular A, number of elements that get loaded along non-coalesced dimension with each load
|
|
tP["lvc"] = "LVCA" # "load size" in terms of number of short-vectors and not elements
|
|
tP["lvp"] = "LVPA" # "load size" in terms of number of short-vectors and not elements
|
|
tP["rtv"] = self.readTileDimVectorA # bool in the tile dimension, reads will read vectors
|
|
tP["rtc"] = self.readTileDimComponentsA # bool in the tile dimension, reads will read vector components
|
|
#tP["ruv"] = self.readUnrollDimVectorA
|
|
#tP["nlvc"] = self.numReadVectorComponentsA
|
|
#tP["nwvc"] = self.numWriteVectorComponentsA
|
|
tP["wg"] = "WorkGroup%u" % (tP["tile01Idx"])# these are storing the actual strong to lookup the number from kernel dictionary
|
|
tP["prevWg"] = "PrevWorkGroup0" # used for prefetch-across-persistent #NHWC TO-do
|
|
tP["sg"] = "SubGroup%u" % (tP["tile01Idx"])
|
|
tP["tt"] = "ThreadTile%u" % (tP["tile01Idx"])
|
|
tP["mt"] = "MacroTile%u" % (tP["tile01Idx"])
|
|
tP["grcg"] = self.globalReadCoalesceGroupA # global reads are coalesced along threads
|
|
tP["grcv"] = kernel["GlobalReadCoalesceVectorA"] # global reads are vector reads, and lds writes will be components if transposing
|
|
tP["tlu"] = kernel["ProblemType"]["TLUA"] # thread stride is less than unroll stride, i.e., not transposing matrix
|
|
tP["ia"] = kernel["ProblemType"]["IndexAssignmentsA"] # array of index assignments
|
|
#tP["nlc"] = kernel["NumLoadsCoalescedA"]
|
|
#tP["nlp"] = kernel["NumLoadsPerpendicularA"]
|
|
#tP["nlcv"] = self.numReadsCoalVecCompA
|
|
tP["nlpv"] = self.numReadsPerpVecCompA # num vector components perpendicular to coalesced; =1 or VW
|
|
# NEW
|
|
tP["nrt"] = self.numReadsTileA # number of reads along tile dimension
|
|
tP["nrtv"] = self.numReadsTileVecCompA # number of vector components along tile dimension; =1 or VW
|
|
tP["nru"] = self.numReadsUnrollA # number of reads along unroll dimension
|
|
tP["nruv"] = self.numReadsUnrollVecCompA # number of vector components along unroll dimension; =1 or VW
|
|
tP["nrc"] = kernel["NumLoadsCoalescedA"] # number of reads along coalesced dimension
|
|
tP["nrcv"] = self.numReadsCoalVecCompA # number of vector components along coalesced dimension
|
|
tP["nrp"] = kernel["NumLoadsPerpendicularA"] # number of reads along perpendicular dimension
|
|
tP["nrpv"] = self.numReadsPerpVecCompA # number of vector components along perpendicular dimension
|
|
tP["nwcv"] = self.numWritesCoalVecCompA # number of vector component writes along coalesced dimension
|
|
tP["nwpv"] = self.numWritesPerpVecCompA # number of vector component writes along perpendicular dimension
|
|
tP["glvw"] = kernel["GlobalLoadVectorWidthA"]
|
|
# asm
|
|
tP["rcc"] = self.readCoalescedComponentsA # read vector components along coalesced dimensions
|
|
# tP["rcv"] = self.readCoalescedVectorA # read vector along coalesced dimension
|
|
tP["rpc"] = self.readPerpendicularComponentsA # read vector components along perpendicular dimension
|
|
# tP["rpv"] = self.readPerpendicularVectorA # read vector along perpendicular dimension
|
|
tP["ruc"] = self.readUnrollDimComponentsA # read vector components along unroll dimension
|
|
tP["wtc"] = self.writeTileDimComponentsA # write vector components along tile dimension
|
|
tP["wuc"] = self.writeUnrollDimComponentsA # write vector components along unroll dimension
|
|
tP["idx"] = kernel["ProblemType"]["Index0"] # index 0 is tile dimension belonging to A. Note 'idx' may not be in tP['ia'].
|
|
tP["rc"] = kernel["ProblemType"]["IndexAssignmentsA"][0] \
|
|
in [kernel["ProblemType"]["Index01A"], \
|
|
kernel["ProblemType"]["IndexUnroll"]] # can read coalesced
|
|
tP["NonTemporal"] = kernel["NonTemporalA"] # non-temporal read type
|
|
else: # B
|
|
tP["isA"] = False
|
|
tP["isB"] = True
|
|
tP["bpe"] = int(4*kernel["ProblemType"]["DataType"].numRegisters())
|
|
tP["tensorChar"] = "B"
|
|
tP["tensorIdx"] = 1
|
|
tP["tileChar"] = self.tileCharB
|
|
tP["tileIdx"] = kernel["ProblemType"]["Index01B"]
|
|
tP["tile01Idx"] = 1 if tP["tileIdx"] else 0
|
|
tP["lsc"] = "LSCB"
|
|
tP["lsp"] = "LSPB"
|
|
tP["lvc"] = "LVCB"
|
|
tP["lvp"] = "LVPB"
|
|
tP["rtv"] = self.readTileDimVectorB
|
|
tP["rtc"] = self.readTileDimComponentsB
|
|
#tP["ruv"] = self.readUnrollDimVectorB
|
|
#tP["nlvc"] = self.numReadVectorComponentsB
|
|
#tP["nwvc"] = self.numWriteVectorComponentsB
|
|
tP["wg"] = "WorkGroup%u" % (tP["tile01Idx"])
|
|
tP["prevWg"] = "PrevWorkGroup1"
|
|
tP["sg"] = "SubGroup%u" % (tP["tile01Idx"])
|
|
tP["tt"] = "ThreadTile%u" % (tP["tile01Idx"])
|
|
tP["mt"] = "MacroTile%u" % (tP["tile01Idx"])
|
|
tP["grcg"] = self.globalReadCoalesceGroupB
|
|
tP["grcv"] = kernel["GlobalReadCoalesceVectorB"]
|
|
tP["tlu"] = kernel["ProblemType"]["TLUB"]
|
|
tP["ia"] = kernel["ProblemType"]["IndexAssignmentsB"]
|
|
# NEW
|
|
tP["nrt"] = self.numReadsTileB
|
|
tP["nrtv"] = self.numReadsTileVecCompB
|
|
tP["nru"] = self.numReadsUnrollB
|
|
tP["nruv"] = self.numReadsUnrollVecCompB
|
|
tP["nrc"] = kernel["NumLoadsCoalescedB"]
|
|
tP["nrcv"] = self.numReadsCoalVecCompB
|
|
tP["nrp"] = kernel["NumLoadsPerpendicularB"]
|
|
tP["nrpv"] = self.numReadsPerpVecCompB
|
|
tP["nwcv"] = self.numWritesCoalVecCompB
|
|
tP["nwpv"] = self.numWritesPerpVecCompB
|
|
tP["glvw"] = kernel["GlobalLoadVectorWidthB"]
|
|
# asm
|
|
tP["rcc"] = self.readCoalescedComponentsB
|
|
# tP["rcv"] = self.readCoalescedVectorB
|
|
tP["rpc"] = self.readPerpendicularComponentsB
|
|
# tP["rpv"] = self.readPerpendicularVectorB
|
|
tP["ruc"] = self.readUnrollDimComponentsB
|
|
tP["wtc"] = self.writeTileDimComponentsB
|
|
tP["wuc"] = self.writeUnrollDimComponentsB
|
|
tP["idx"] = kernel["ProblemType"]["Index1"]
|
|
tP["rc"] = kernel["ProblemType"]["IndexAssignmentsB"][0] \
|
|
in [kernel["ProblemType"]["Index01B"], \
|
|
kernel["ProblemType"]["IndexUnroll"]] # can read coalesced
|
|
tP["NonTemporal"] = kernel["NonTemporalB"]
|
|
|
|
##############################################################################
|
|
# Global Read Addresses: Tile Assignment A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def graTileAssignment(self, kernel, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Global Read Addresses: Unroll Assignment A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def graUnrollAssignment(self, kernel, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Global Read Addresses: Other Free Assignments
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def graOtherFreeAssignments(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Global Read Addresses: Other Summation Assignments
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def graOtherSummationAssignments(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Global Read Addresses: Tile Offsets A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def graTileOffsets(self, kernel, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Global Read Addresses: Unroll Offsets A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def graUnrollOffsets(self, kernel, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Global Read Addresses: Branch A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def graBranch(self, kernel, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Global Read Addresses: Shift A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def graShift(self, kernel, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Global Read Addresses: Final Offsets A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def graFinalOffsets(self, kernel, tP, releaseResource=False):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Global Read Addresses: Addresses A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def graAddresses(self, kernel, tP, isPap=False):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Global Read Addresses: Increments A/B
|
|
# This function declares the increments
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def graIncrements(self, kernel, loopIdx, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Write Addresses: Tile Assignment A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def lwaTileAssignment(self, kernel, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Write Addresses: Unroll Assignment A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def lwaUnrollAssignment(self, kernel, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Write Addresses: Release tile related vgpr
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def lwaReleaseTileVgpr(self, kernel, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Write Addresses: First Offset A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def lwaFirstOffset(self, kernel, tP, uDu=0):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Write Addresses: Final Offsets A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def lwaFinalOffsets(self, kernel, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Write Addresses: Declare Addresses A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def lwaDeclareAddresses(self, kernel, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Write Addresses: Allocate tmpSgpr for initOpt
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def lwaInitOptAllocate(self):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Write Addresses: Release tmpSgpr for initOpt
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def lwaInitOptRelease(self):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Read Addresses: Tile Assignment
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def lraTileAssignment(self, kernel, tPA, tPB):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Read Addresses: Final Offset A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def lraFinalOffset(self, kernel, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Read Addresses offset conversion for DTL + NLC > 1
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def lraOffsetConversionForDTLandNLC(self, kernel, tP, offset_val, generateAsm=False, \
|
|
finalVgpr=None, tmp1=None, tmp2=None):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Read Addresses: Declare Addresses A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def lraDeclareAddresses(self, kernel, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Recalculate local read addresses A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def recalcLocalReadAddressesAB(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Recalculate local write addresses A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def recalcLocalWriteAddresses(self, kernel, tP, uDu):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Declare Loop Num Iterations
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def declareLoopNumIter(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Define stagger parms that will be used in calculateStagger
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def declareStaggerParms(self, kernel):
|
|
return ""
|
|
|
|
|
|
##############################################################################
|
|
# Calculate and apply stagger offsets and edge
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def calculateStagger(self, kernel, loopIdx):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Remove stagger offset (before tail loop)
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def removeStagger(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Calculate Loop Num Iter
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def calculateLoopNumIter(self, kernel, loopIdx, isPap):
|
|
return ""
|
|
|
|
|
|
##############################################################################
|
|
# openShadowInit:
|
|
# Top of shadow init code
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def openShadowInit(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# closeShadowInit:
|
|
# Top of shadow init code
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def closeShadowInit(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Initialize C
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def initC(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Open Loop
|
|
# loopIdx<0 : tail loop
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def openLoop(self, kernel, loopIdx, uDu, noLabelGen, beginLabelOnly):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Close Loop
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def closeLoop(self, kernel, loopIdx, finalLoop, loopCopies, uDu=None, emitEndLabelOnly=False, oddLabel=False, \
|
|
skipCondJumpCounter=-1, isOptNLL=False, skipJump=False):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Open Loop Copy
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def openLoopCopy(self, kernel, lc):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# End Summation
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def endSummation(self, kernel, label = None, isOptNLL = False):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# MAC Iteration
|
|
# useMacro : if true, call the MAC* macro. If False, inline the MACs
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def macIter(self, kernel, bufferIdx, iuiCount, useMacro, isTail=False):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# At Least 1 Unroll
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def openSumAtLeastUnroll(self, kernel, prefetch, isOptNLL, isPap):
|
|
return ""
|
|
|
|
@abc.abstractmethod
|
|
def closeSumAtLeastUnroll(self, kernel, prefetch, isOptNLL, isPap, isNGLL):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Global Read: Increment A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def globalReadIncrementAB(self, kernel, loopIdx, prefetchIndex, incs=1):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Global Read: Do It A/B
|
|
# mode: 0=prefetch, 1=unroll loop, 2=guardK
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def globalReadDo(self, kernel, mode, tP, vregSetIdx=0):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Global Read A/B completed
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def doneGlobalABReads(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# directToLds m0 update: Do It A/B
|
|
# mode: 0=prefetch, 1=unroll loop, 2=guardK
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def directToLdsM0Update(self, kernel, mode, tP, usePlaceHolder=False):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Write: Swap Offsets A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def localWriteSwapOffsets(self, kernel, internalPointerSwap, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Write: Reset Offsets A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def localWriteResetOffsets(self, kernel, internalPointerSwap, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Write: Init Pointers A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def localWriteInitPointers(self, kernel, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Write in Prefetch Pass (PreLoop): Do It A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def preLoopLocalWriteDo(self, kernel, tPA, tPB):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Replace the determined vmcnt in PreLoop LocalWrite
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def replacePreLoopLWVmcnt(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Write: Do It A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def localWriteDo(self, kernel, tP, uDu):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Read: Swap Offsets A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def localReadSwapOffsets(self, kernel, internalPointerSwap, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Read: Reset Offsets A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def localReadResetOffsets(self, kernel, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Read: Init Pointers A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def localReadInitPointers(self, kernel, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Read: Increment A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def localReadInc(self, kernel, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Local Read: Do It A/B
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def localReadDo(self, kernel, bufferIdx, innerUnrollIndex, epsi, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Shift Vector Components d0/1
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def shiftVectorComponents(self, kernel, tP):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Complex Declare Tmp Registers
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def complexDeclareTmpRegisters(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# LocalSplitU: Local Write
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def localSplitULocalWrite(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# LocalSplitU: Local Read
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def localSplitULocalRead(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# LocalSplitU: Reduction
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def localSplitUReduction(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# globalWriteWorkGroupInitBeforePersistentLoop:
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def globalWriteWorkGroupInitBeforePersistentLoop(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# globalWriteWorkGroupInit:
|
|
# Perform work-group granularity init
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def globalWriteWorkGroupInit(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# LocalSplitU: Global Write Indices
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def localSplitUGlobalWriteIndices(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# LocalSplitU: Global Write
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def localSplitUGlobalWrite(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Not LocalSplitU: Global Write Indices
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def notLocalSplitUGlobalWriteIndices(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Not LocalSplitU: Global Write
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def notLocalSplitUGlobalWrite(self, kernel):
|
|
return ""
|
|
|
|
@abc.abstractmethod
|
|
def openPrefetchAcrossPersistent(self, kernel, isOptNLL=False, useBufferOOB=False):
|
|
return ""
|
|
|
|
@abc.abstractmethod
|
|
def closePrefetchAcrossPersistent(self, kernel, isOptNLL=False, useBufferOOB=False):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# init for StoreCInUnroll
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def initStoreCInUnroll(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# init for StoreCInUnroll per Persistent Loop
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def initStoreCInUnrollPerPersistentLoop(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# init for StoreCInUnroll per Unroll Loop
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def initStoreCInUnrollPerUnrollLoop(self, kernel, needInit):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# swap SrdC and SrdCbackup, SrdD and SrdDbackup
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def swapSrdCDandBackup(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# C/D address increment value for StoreCInUnroll
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def generateCorDaddrIncrementForStoreCInUnroll(self, kernel, CorD, odd, tmpSgprWork):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# get address/gpr index increment frequency for StoreCInUnroll
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def getAddrGprIdxIncrementFrequencyForStoreCInUnroll(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# generate post process for StoreCInUnroll loop
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def generatePostProcessForStoreCInUnrollLoop(self, kernel, needPost):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# restore SrdCbackup and SrdDbackup
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def restoreSrdCandDBackup(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# reset storeC sync objects
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def resetStoreCsyncObject(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# set storeC sync objects
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def setStoreCsyncObject(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# end process for StoreCInUnroll per PersistentLoop (OptNLL)
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def endProcessPersistentLoopforStoreCInUnrollOptNLL(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# end process for StoreCInUnroll per PersistentLoop (NoOptNLL)
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def endProcessPersistentLoopforStoreCInUnrollNoOptNLL(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# number of storeC code in template for StoreCInUnroll
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def getNumberOfStoreCInTemplate(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# number of LoadC code in template for StoreCInUnroll
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def getNumberOfLoadCInForLoadC(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# generate storeCInUnroll post loop code
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def generateStoreInUnrollPostLoop(self, kernel, isOptNLL, isDTVodd):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# openOddNoLoadLoopForDTV
|
|
# generate open code for DirectToVgpr + odd exit case in noLoadLoop code
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def openOddNoLoadLoopForDTV(self, kernel, isNGLL, name):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# closeOddNoLoadLoopForDTV
|
|
# generate close code for DirectToVgpr + odd exit case in noLoadLoop code
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def closeOddNoLoadLoopForDTV(self, kernel, isNGLL, name):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# generateEvenEndLabeNoLoadLoopForDTV
|
|
# generate even end label for DirectToVgpr
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def generateEvenEndLabeNoLoadLoopForDTV(self, kernel, isNGLL, name):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# generateOddEndVgprCopyForDTV
|
|
# generate odd end vgpr copy for DirectToVgpr
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def generateOddEndVgprCopyForDTV(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# isSwapGlobalReadOrderForDtvOrDtl
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def isSwapGlobalReadOrderForDtvOrDtl(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# PrefetchGlobalRead2
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def openPrefetchGlobalRead2(self, kernel):
|
|
return ""
|
|
|
|
@abc.abstractmethod
|
|
def closePrefetchGlobalRead2(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Function End
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def functionEnd(self, kernel, addLabel=True):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Function Suffix
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def functionSuffix(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# Kernel Body Prefix
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def kernelBodyPrefix(self, kernel, tPA, tPB ):
|
|
return ""
|
|
|
|
|
|
##############################################################################
|
|
# Kernel Body Suffix
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def kernelBodySuffix(self, kernel, tPA, tPB ):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# WaitCnt
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def wait(self, kernel, tPA, tPB, globalRead, localWrite, localRead, comment):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# waitcnt code for DirectToVgpr
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def getWaitcntCodeForDirectToVgpr(self, kernel, localWriteEndIter, u, firstIter, isPap=True, beforeBarrier=False, NLLlast=False, oddLast=False):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# waitcnt code for PrefetchGlobalRead
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def getWaitcntCodeForPGR(self, kernel):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# SyncThreads
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def syncThreads(self, kernel):
|
|
return self.indent + self.syncStr + self.endLine
|
|
|
|
##############################################################################
|
|
# MapAcctoArch
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def MapAcctoArchRegs(self, kernel, option):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# openmovaccVgpr
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def openmovaccVgpr(self, kernel, backupSgpr):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# getAccVgprCode
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def getAccVgprCode(self,kernel,odd):
|
|
return ""
|
|
|
|
##############################################################################
|
|
# closemovaccVgpr
|
|
##############################################################################
|
|
@abc.abstractmethod
|
|
def closemovaccVgpr(self, kernel, backupSgpr):
|
|
return ""
|
|
|
|
|
|
##############################################################################
|
|
#
|
|
# Entry Functions
|
|
#
|
|
##############################################################################
|
|
|
|
|
|
##############################################################################
|
|
# get kernel name
|
|
##############################################################################
|
|
def getKernelFileBase(self, kernel):
|
|
if isCustomKernelConfig(kernel):
|
|
fileBase = kernel["CustomKernelName"]
|
|
elif globalParameters["ShortNames"]:
|
|
fileBase = Solution.getNameSerial(kernel, self.kernelSerialNaming)
|
|
else:
|
|
fileBase = self.shortenFileBase(kernel)
|
|
return fileBase
|
|
|
|
def getKernelName(self, kernel):
|
|
kernelName = Solution.getNameMin(kernel, self.kernelMinNaming)
|
|
return kernelName
|
|
|
|
def getKernelSource(self, kernel):
|
|
"""
|
|
Returns the source of the kernel, either C++ or assembly.
|
|
"""
|
|
|
|
|
|
fileString = ""
|
|
self.tPA = tensorParametersA = {}
|
|
self.tPB = tensorParametersB = {}
|
|
self.initKernel(kernel, tensorParametersA, tensorParametersB )
|
|
fileString += self.kernelBodyPrefix( kernel, tensorParametersA, \
|
|
tensorParametersB )
|
|
self.stringIdx = 0
|
|
(error, kb) = self.kernelBody( kernel, tensorParametersA, tensorParametersB)
|
|
|
|
fileString += kb
|
|
fileString += self.kernelBodySuffix( kernel, tensorParametersA, \
|
|
tensorParametersB )
|
|
|
|
if error != 0:
|
|
if globalParameters["ForceGenerateKernel"]:
|
|
print ("warning: Generating kernel source resulted in error {}, but ForceGenerateKernel=1 so saving source".format(error))
|
|
else:
|
|
raise RuntimeError("Generating kernel source resulted in error {}".format(error))
|
|
|
|
return fileString
|
|
|
|
def getAssemblyDirectory(self):
|
|
return Common.ensurePath(os.path.join(globalParameters["WorkingPath"], "assembly"))
|
|
|
|
def byteArrayScriptSource(self):
|
|
return """
|
|
#!/usr/bin/env python
|
|
|
|
fileString = ""
|
|
fileString += "/*******************************************************************************\\n"
|
|
fileString += "* Copyright (C) 2016 Advanced Micro Devices, Inc. All rights reserved.\\n"
|
|
fileString += "*\\n"
|
|
fileString += "* Permission is hereby granted, free of charge, to any person obtaining a copy\\n"
|
|
fileString += '* of this software and associated documentation files (the \"Software\"), to deal\\n'
|
|
fileString += "* in the Software without restriction, including without limitation the rights\\n"
|
|
fileString += "* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cop-\\n"
|
|
fileString += "* ies of the Software, and to permit persons to whom the Software is furnished\\n"
|
|
fileString += "* to do so, subject to the following conditions:\\n"
|
|
fileString += "*\\n"
|
|
fileString += "* The above copyright notice and this permission notice shall be included in all\\n"
|
|
fileString += "* copies or substantial portions of the Software.\\n"
|
|
fileString += "*\\n"
|
|
fileString += '* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM-\\n'
|
|
fileString += "* PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\\n"
|
|
fileString += "* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\\n"
|
|
fileString += "* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\\n"
|
|
fileString += "* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNE-\\n"
|
|
fileString += "* CTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\\n"
|
|
fileString += "*******************************************************************************/\\n\\n"
|
|
fileString += "/**************************************************\\n"
|
|
fileString += "* This file was generated by Tensile: *\\n"
|
|
fileString += "* https://github.com/ROCm/Tensile *\\n"
|
|
fileString += "**************************************************/\\n\\n\\n"
|
|
import os.path
|
|
fileString += '#include "Kernels.h"\\n\\n'
|
|
fileString += "/* code object byte array */\\n\\n"
|
|
codeObjectFileNames = [f for f in os.listdir(".") if (os.path.isfile(f) and f.endswith(".co"))]
|
|
for codeObjectFileName in codeObjectFileNames:
|
|
print codeObjectFileName
|
|
print "\\n"
|
|
kernelName=os.path.splitext(codeObjectFileName)[0]
|
|
codeObjectFile = open(codeObjectFileName, "r")
|
|
codeObjectByteArray = bytearray(codeObjectFile.read())
|
|
codeObjectFile.close()
|
|
# write code object byte array for asm
|
|
fileString += "const unsigned char %s_coba[%u] = {\\n" % (kernelName, len(codeObjectByteArray))
|
|
for byteIdx in range(0, len(codeObjectByteArray)):
|
|
byte = codeObjectByteArray[byteIdx]
|
|
fileString += "0x%02x" % byte
|
|
if byteIdx < len(codeObjectByteArray)-1:
|
|
fileString += ","
|
|
else:
|
|
fileString += "};\\n"
|
|
if byteIdx % 16 == 15:
|
|
fileString += "\\n"
|
|
text_file = open("Kernels.cpp", "w")
|
|
text_file.write("%s" % fileString)
|
|
text_file.close()
|
|
"""
|
|
|
|
def writeByteArrayScript(self):
|
|
asmPath = self.getAssemblyDirectory()
|
|
|
|
bytearrayFileName = os.path.join(asmPath,"insert_byte_array.py")
|
|
if not os.path.isfile(bytearrayFileName):
|
|
with open(bytearrayFileName, 'w') as bytearrayFile:
|
|
bytearrayFile.write(self.byteArrayScriptSource())
|
|
os.chmod(bytearrayFileName, 0o777)
|
|
return bytearrayFileName
|
|
|
|
def getReplacementKernelPath(self, kernel):
|
|
if not kernel["ReplacementKernel"] and not isCustomKernelConfig(kernel): #kernel["CustomKernelName"]:
|
|
return None
|
|
|
|
kernelName = self.getKernelName(kernel)
|
|
|
|
if isCustomKernelConfig(kernel):
|
|
return os.path.join(globalParameters["CustomKernelDirectory"], (kernelName + ".s"))
|
|
else: # Replacement kernel
|
|
return ReplacementKernels.Get(kernelName)
|
|
|
|
def shortenFileBase(self, kernel):
|
|
base = self.getKernelName(kernel)
|
|
if len(base) <= globalParameters["MaxFileName"]:
|
|
return base
|
|
|
|
import hashlib
|
|
import base64
|
|
|
|
pivot = globalParameters["MaxFileName"] * 3 // 4
|
|
firstPart = base[:pivot]
|
|
secondPart = base[pivot:]
|
|
|
|
secondHash = hashlib.sha256(secondPart.encode()).digest()
|
|
secondPart = base64.b64encode(secondHash, b'_-').decode()
|
|
|
|
return firstPart + secondPart
|
|
|
|
def getKernelObjectAssemblyFile(self, kernel):
|
|
asmPath = self.getAssemblyDirectory()
|
|
# write assembly file to assembly directory
|
|
kernelName = self.getKernelFileBase(kernel)
|
|
fileBase = os.path.join(asmPath, kernelName )
|
|
assemblyFileName = "%s.s" % fileBase
|
|
|
|
replacementKernel = self.getReplacementKernelPath(kernel)
|
|
|
|
if replacementKernel is not None:
|
|
self.tPA = tensorParametersA = {}
|
|
self.tPB = tensorParametersB = {}
|
|
if isCustomKernelConfig(kernel):
|
|
kernelFoundMessage = "Custom kernel filename "
|
|
# ISA version, such as 803
|
|
self.kernel = kernel
|
|
self.language = "ASM"
|
|
self.version = tuple(kernel["ISA"])
|
|
if not globalParameters["AsmCaps"][self.version]["SupportedISA"]:
|
|
defaultIsa = (9,0,0)
|
|
print("warning: ISA:", self.version, " is not supported; overriding with ", defaultIsa)
|
|
self.version = defaultIsa
|
|
else:
|
|
kernelFoundMessage = "replacement_assemblyFilename "
|
|
self.initKernel(kernel, tensorParametersA, tensorParametersB )
|
|
|
|
shutil.copyfile(replacementKernel, assemblyFileName)
|
|
if globalParameters["PrintLevel"] >= 1:
|
|
print(kernelFoundMessage + assemblyFileName)
|
|
else:
|
|
kernelSource = self.getKernelSource(kernel)
|
|
|
|
if globalParameters["PrintLevel"] >= 3:
|
|
print("write_assemblyFilename %s" % assemblyFileName)
|
|
|
|
with open(assemblyFileName, 'w') as assemblyFile:
|
|
assemblyFile.write(kernelSource)
|
|
|
|
return assemblyFileName
|
|
|
|
def getAssembledKernelObjectFile(self, kernel):
|
|
assemblyFileName = self.getKernelObjectAssemblyFile(kernel)
|
|
|
|
base, ext = os.path.splitext(assemblyFileName)
|
|
objectFileName = base + '.o'
|
|
|
|
args = self.getCompileArgs(assemblyFileName, objectFileName)
|
|
tPrint(2, "Assemble command: " + " ".join(args) + " && ")
|
|
|
|
# change to use check_output to force windows cmd block util command finish
|
|
try:
|
|
out = subprocess.check_output(args, stderr=subprocess.STDOUT, cwd=self.getAssemblyDirectory())
|
|
tPrint(3, out)
|
|
except subprocess.CalledProcessError as err:
|
|
print(err.output)
|
|
raise
|
|
if self.removeTemporaries:
|
|
os.remove(assemblyFileName)
|
|
|
|
return objectFileName
|
|
|
|
def getSingleCodeObjectFile(self, kernel):
|
|
objectFileName = self.getAssembledKernelObjectFile(kernel)
|
|
|
|
base, ext = os.path.splitext(objectFileName)
|
|
coFileName = base + '.co'
|
|
|
|
args = self.getLinkCodeObjectArgs([objectFileName], coFileName)
|
|
|
|
tPrint(2, 'Single Code Object File: ' + ' '.join(args))
|
|
|
|
# change to use check_output to force windows cmd block util command finish
|
|
try:
|
|
out = subprocess.check_output(args, stderr=subprocess.STDOUT, cwd=self.getAssemblyDirectory())
|
|
tPrint(3, out)
|
|
except subprocess.CalledProcessError as err:
|
|
print(err.output)
|
|
raise
|
|
|
|
return coFileName
|
|
|
|
def getByteArrayCobaDefinition(self, varName, byteArray):
|
|
s = self.comment("code object byte array")
|
|
s += "const unsigned char %s_coba[%u] = {\n" % (varName, len(byteArray))
|
|
|
|
if len(byteArray) != 0:
|
|
s += "0x%02x" % byteArray[0]
|
|
for byteIdx, byte in enumerate(byteArray[1:]):
|
|
if byteIdx % 16 == 15:
|
|
s += ",\n0x%02x" % byte
|
|
else:
|
|
s += ",0x%02x" % byte
|
|
|
|
s += '};\n'
|
|
return s
|
|
|
|
def getFileCobaDefinition(self, varName, fileName):
|
|
with open(fileName, 'rb') as f:
|
|
byteArray = bytearray(f.read())
|
|
return self.getByteArrayCobaDefinition(varName, byteArray)
|
|
|
|
##############################################################################
|
|
def getSourceFileString(self, kernel):
|
|
"""
|
|
Returns a string suitable for placing in Kernels.cpp. This means the actual kernel source in the case
|
|
of a source kernel, or an assembled code object byte array definition in the case of an assembly kernel,
|
|
or an empty string in the case that CodeFromFiles is true.
|
|
|
|
In the case of an assembly kernel, this function has the side effect of creating the following files:
|
|
* An assembly source file
|
|
* An object file
|
|
* A code object file
|
|
* A Python script which can create byte array variable definitions.
|
|
"""
|
|
|
|
try:
|
|
if kernel["KernelLanguage"] == "Assembly":
|
|
# asmPath = self.getAssemblyDirectory()
|
|
# kernelName = self.getKernelName(kernel)
|
|
|
|
# Skip if .o files will have already been built for this file
|
|
# @TODO remove need for this with better code organization
|
|
if kernel.duplicate:
|
|
self.language = "ASM"
|
|
return (0, "")
|
|
if globalParameters["GenerateSourcesAndExit"]:
|
|
# only create the assembly file.
|
|
self.getKernelObjectAssemblyFile(kernel)
|
|
return (0, "")
|
|
else:
|
|
self.writeByteArrayScript()
|
|
self.getSingleCodeObjectFile(kernel)
|
|
|
|
# I guess in this case we are making sure that the code object file exists by executing the code
|
|
# above but we aren't placing it into the source.
|
|
return (0, "")
|
|
|
|
# Old client debug option
|
|
# return (0, self.getFileCobaDefinition(kernelName, os.path.join(asmPath, coFile)))
|
|
|
|
else:
|
|
return (0, self.getKernelSource(kernel))
|
|
|
|
except subprocess.CalledProcessError as exc:
|
|
if isinstance(exc.cmd, Sequence):
|
|
print("Command: ")
|
|
print(' '.join(exc.cmd))
|
|
print("returned non-zero exit status ", exc.returncode)
|
|
else:
|
|
print(exc)
|
|
return (-1, "")
|
|
except RuntimeError as exc:
|
|
if globalParameters["PrintSolutionRejectionReason"]:
|
|
print(exc)
|
|
return (-2, "")
|
|
|
|
##############################################################################
|
|
# header file string
|
|
##############################################################################
|
|
def getHeaderFileString(self, kernel):
|
|
kernelName = self.getKernelName(kernel)
|
|
fileString = "" # CHeader
|
|
if self.language == "HIP":
|
|
if not globalParameters["MergeFiles"]:
|
|
fileString += CHeader
|
|
fileString += "#pragma once\n\n"
|
|
fileString += "#include <hip/hip_runtime.h>\n"
|
|
fileString += "#include <hip/hip_fp16.h>\n"
|
|
fileString += "#include <KernelHeader.h>\n"
|
|
fileString += "\n"
|
|
fileString += self.functionSignature(kernel)
|
|
fileString += ";\n"
|
|
else:
|
|
if not globalParameters["MergeFiles"] or globalParameters["NumMergedFiles"] > 1:
|
|
fileString += "#pragma once\n\n"
|
|
if not globalParameters["CodeFromFiles"]:
|
|
fileString += "extern const unsigned char %s_coba[]; // code object byte array\n" % kernelName
|
|
|
|
return fileString
|
|
|
|
##############################################################################
|
|
# flip Vreg set for DirectToVgpr in global read
|
|
##############################################################################
|
|
def flipVregSetForDirectToVgprInGlobalRead(self, kernel, itemStr):
|
|
# need to swap VGPR register set for odd code
|
|
baseName = "G2LA" if kernel["DirectToVgprA"] else "G2LB" # only one of them is enabled
|
|
set0 = baseName + "0"
|
|
set1 = baseName + "1"
|
|
if set0 in itemStr:
|
|
# replace set0 with set1
|
|
itemStr = itemStr.replace(set0, set1)
|
|
elif set1 in itemStr:
|
|
# replace set1 with set0
|
|
itemStr = itemStr.replace(set1, set0)
|
|
return itemStr
|
|
|
|
##############################################################################
|
|
# Backup StoreCInUnroll related code
|
|
##############################################################################
|
|
def backupStoreCInUnrollRelatedCode(self):
|
|
# keep StoreCInUnrollPreCode, StoreCUnrollPostCode for the next noLoadLoop
|
|
self.StoreCUnrollPreCodeBackup = copy.deepcopy(self.StoreCUnrollPreCode)
|
|
self.StoreCUnrollPostCodeBackup = copy.deepcopy(self.StoreCUnrollPostCode)
|
|
|
|
##############################################################################
|
|
# Restore StoreCInUnroll related code
|
|
##############################################################################
|
|
def restoreStoreCInUnrollRelatedCode(self):
|
|
self.StoreCUnrollPreCode = self.StoreCUnrollPreCodeBackup
|
|
self.StoreCUnrollPostCode = self.StoreCUnrollPostCodeBackup
|
|
self.StoreCUnrollLoopCodeStarted = 0
|
|
|
|
##############################################################################
|
|
# generate storeC code in UnrollLoop
|
|
##############################################################################
|
|
def generateStoreCCodeInUnrollLoop(self, kernel, odd, isLast=False):
|
|
self.LoadCUnrollCode = Code.Module()
|
|
self.StoreCUnrollCode = Code.Module()
|
|
self.StoreCUnrollPreCode = Code.Module()
|
|
self.StoreCUnrollPostCode = Code.Module()
|
|
self.numItemsBeforeStoreC = 0
|
|
self.StoreCUnrollStartComment ="Start of StoreCInUnroll code"
|
|
self.StoreCUnrollStoreStartComment ="Start of StoreCInUnroll Store code"
|
|
self.StoreCUnrollLoopCodeStarted = 0 # 0:not StoreC code started, 1: started
|
|
if kernel["StoreCInUnroll"]:
|
|
needInit = not odd
|
|
needPost = odd
|
|
needInc = (not isLast) or kernel["StoreCInUnrollPostLoop"]
|
|
backupSgpr = self.getTmpSgpr(2).idx() # allocate all tmp register here
|
|
tmpSgprWork = backupSgpr + 1
|
|
needAddrC = (not kernel["AssertCEqualsD"]) and kernel["ProblemType"]["UseBeta"]
|
|
|
|
# init/inc code is necessary if inc frequency is 1
|
|
needInit = needInit or (self.getAddrGprIdxIncrementFrequencyForStoreCInUnroll(kernel) == 1)
|
|
needPost = needPost or (self.getAddrGprIdxIncrementFrequencyForStoreCInUnroll(kernel) == 1)
|
|
|
|
# generate init code for StoreCInUnroll per Unroll Loop
|
|
initPerUnrollCode = self.initStoreCInUnrollPerUnrollLoop(kernel, needInit)
|
|
|
|
# loadC
|
|
for x in self.LoadCTemplate.items():
|
|
# Load C case, insert Init per unroll code before Load C (setup vgpr offset for loadC and StoreC)
|
|
s = initPerUnrollCode + str(x)
|
|
initPerUnrollCode = "" # reset initPerUnrollCode so that it is not inserted again
|
|
self.LoadCUnrollCode.addText(s)
|
|
# Addr C increment code (no increment for isLast (and not PostLoop))
|
|
if needInc and needAddrC:
|
|
oddParam = needPost
|
|
kStr = self.generateCorDaddrIncrementForStoreCInUnroll(kernel, "C", oddParam, tmpSgprWork)
|
|
self.LoadCUnrollCode.addText(kStr)
|
|
|
|
if initPerUnrollCode != "":
|
|
# If init code is not inserted (no Load C case), insert it to the top of StoreC list (setup vgpr offset for StoreC)
|
|
self.StoreCUnrollPreCode.addText(initPerUnrollCode)
|
|
initPerUnrollCode = "" # reset initPerUnrollCode so that it is not inserted again
|
|
|
|
# these 3 items need to be in the same set
|
|
# open gpr indexing
|
|
# accVgpr (need gpr indexing)
|
|
# close gpr indexing
|
|
kStr = self.openmovaccVgpr(kernel, backupSgpr)
|
|
# odd case, use + (1 iteration) for gpr index, but not necessary if index frequency is 1
|
|
oddGprIndex = odd and (self.getAddrGprIdxIncrementFrequencyForStoreCInUnroll(kernel) > 1)
|
|
kStr += self.getAccVgprCode(kernel, oddGprIndex)
|
|
first, second = self.closemovaccVgpr(kernel, backupSgpr)
|
|
kStr += first
|
|
self.StoreCUnrollPreCode.addText(kStr)
|
|
# put second part of close gpr indexing separately (for better scheduling)
|
|
self.StoreCUnrollPreCode.addText(second)
|
|
# Alpha
|
|
kStr = ""
|
|
for x in self.AlphaOpTemplate.items():
|
|
kStr += str(x)
|
|
|
|
if kStr != "":
|
|
self.StoreCUnrollPreCode.addText(kStr)
|
|
|
|
# count the number of items before StoreC (before beta)
|
|
self.numItemsBeforeStoreC = len(list(self.StoreCUnrollPreCode.items()))
|
|
|
|
# StoreC
|
|
|
|
# put marker comment to recognize start point of StoreC code
|
|
# this must be the first item in self.StoreCUnrollCode.
|
|
self.StoreCUnrollCode.addComment0(self.StoreCUnrollStartComment)
|
|
# add necessary dummy based on number of mfma instructions between local write items
|
|
# put enough interval (=3) for LocalWritePerMfma == -1 case
|
|
numMfma = 3 if kernel["LocalWritePerMfma"] == -1 else roundUp(1/kernel["LocalWritePerMfma"])
|
|
n = self.numItemsBeforeStoreC - numMfma # first numMfma items are inserted at the start comment and following mfmas
|
|
while n >= numMfma:
|
|
self.StoreCUnrollCode.addText("")
|
|
n -= numMfma
|
|
|
|
# insert items in postProcessList between StoreC/AtomicAdd (StoreVectorWidth=1 only)
|
|
imod = Code.Module()
|
|
imod.addComment0(self.StoreCUnrollStoreStartComment)
|
|
StartComment = str(imod)
|
|
|
|
# Beta
|
|
kStrBeta = ""
|
|
for x in self.BetaOpTemplate.items():
|
|
kStrBeta += str(x)
|
|
# double complex case or num of store == 1 case, put beta instruction separately
|
|
if kStrBeta != "" and (kernel["ProblemType"]["DestDataType"].isDoubleComplex() or self.getNumberOfStoreCInTemplate(kernel) == 1):
|
|
# combine beta code with first StoreC comment to avoid generating beta before alpha
|
|
self.StoreCUnrollCode.addText(kStrBeta + StartComment)
|
|
kStrBeta = ""
|
|
StartComment = ""
|
|
|
|
# number of instructions(items) of increment code between MFMAs
|
|
putCount = 1
|
|
postProcessListIndex = 0
|
|
# generate post process for StoreCInUnroll loop
|
|
# 1) increment gpr indexing (new value in tmp). Put this as separate item in StoreCUnrollCode
|
|
# 2-1) increment StoreC address (new value in tmp)
|
|
# 2-2) check enable count and apply new values when necessary
|
|
postProcessList = []
|
|
finalAddrIncList = []
|
|
if needInc:
|
|
postProcessList, finalAddrIncList = self.generatePostProcessForStoreCInUnrollLoop(kernel, needPost)
|
|
|
|
for x in self.StoreCTemplate.items():
|
|
kStr = ""
|
|
if x == self.StoreCTemplate.items()[0]:
|
|
kStr += kStrBeta + StartComment # combine beta code with first StoreC. first item case, add marker comment
|
|
StartComment = ""
|
|
strX = str(x)
|
|
kStr += strX
|
|
if x != self.StoreCTemplate.items()[-1]:
|
|
# not the last StoreC
|
|
# add postprocess code or empty between StoreC
|
|
self.StoreCUnrollCode.addCode(kStr)
|
|
end = kernel["StoreCInUnrollInterval"] - 1
|
|
for i in range(end):
|
|
if postProcessListIndex < len(postProcessList):
|
|
self.StoreCUnrollCode.addText(postProcessList[postProcessListIndex])
|
|
postProcessListIndex += 1
|
|
else:
|
|
self.StoreCUnrollCode.addText("") # add empty str to add interval between Store codes
|
|
else:
|
|
# last StoreC
|
|
if not (kernel["StoreCInUnrollPostLoop"] and isLast):
|
|
# last element and not StoreCInUnrollPostLoop+isLast case
|
|
self.StoreCUnrollCode.addCode(kStr)
|
|
# add remaining postprocess, finalAddrInc code in StoreCUnrollPostCode
|
|
count = 0
|
|
kStr = ""
|
|
for i in range(postProcessListIndex, len(postProcessList)):
|
|
kStr += postProcessList[i]
|
|
count+=1
|
|
if count == putCount:
|
|
self.StoreCUnrollPostCode.addText(kStr)
|
|
count = 0
|
|
kStr = ""
|
|
for i in range(len(finalAddrIncList)):
|
|
kStr += finalAddrIncList[i]
|
|
count+=1
|
|
if count == putCount:
|
|
self.StoreCUnrollPostCode.addText(kStr)
|
|
count = 0
|
|
kStr = ""
|
|
if count > 0:
|
|
self.StoreCUnrollPostCode.addText(kStr)
|
|
else:
|
|
# not last element or StoreCInUnrollPostLoop+isLast
|
|
# add all remaining items in postProcessList and finalAddrInc code after the last StoreC (in the same item)
|
|
for item in (postProcessList[postProcessListIndex:] + finalAddrIncList):
|
|
kStr += item
|
|
self.StoreCUnrollCode.addCode(kStr)
|