Files
TheRock/rocm-libraries/shared/tensile/Tensile/Code.py
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

902 lines
38 KiB
Python

################################################################################
#
# Copyright (C) 2016-2023 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 __future__ import print_function
from .Common import globalParameters, printExit
import ctypes
# Global to print module names around strings
printModuleNames = 0
class Item:
"""
Base class for Modules, Instructions, etc
Item is a atomic collection of or more instructions and commentsA
"""
pass
def toStr(self):
return str(self)
def countType(self,ttype):
return int(isinstance(self, ttype))
def prettyPrint(self, indent=""):
ostream = ""
ostream += "%s%s "%(indent, type(self).__name__)
ostream += str(self)
return ostream
class Module(Item):
"""
Modules contain lists of text instructions, Inst objects, or additional modules
They can be easily converted to string that represents all items in the list
and can be mixed with standard text.
The intent is to allow the kernel writer to express the structure of the
code (ie which instructions are a related module) so the scheduler can later
make intelligent and legal transformations.
"""
def __init__(self, name=""):
self.name = name
self.itemList = []
self.tempVgpr = None
def findNamedItem(self, targetName):
return next((item for item in self.itemList if item.name==targetName), None)
def __str__(self):
s = ""
if printModuleNames:
s += "// %s { \n" % self.name
s += "".join([str(x) for x in self.itemList])
if printModuleNames:
s += "// } %s\n" % self.name
return s
def addCode(self, item):
"""
Add specified item to the list of items in the module.
Item MUST be a Item (not a string) - can use
addText(...)) to add a string.
All additions to itemList should use this function.
Returns item to facilitate one-line create/add patterns
"""
#assert (isinstance(item, Item)) # for debug
if isinstance(item,Item):
self.itemList.append(item)
elif isinstance(item,str):
self.addCode(TextBlock(item))
else:
assert 0, "unknown item type (%s) for Module.addCode. item=%s"%(type(item), item)
return item
def addComment0(self, comment):
"""
Convenience function to format arg as a comment and add TextBlock item
This comment is a single line /* MYCOMMENT */
"""
self.addCode(TextBlock("/* %s */\n"%comment))
def addComment1(self, comment):
"""
Convenience function to format arg as a comment and add TextBlock item
This comment is a blank line followed by /* MYCOMMENT */
"""
self.addCode(TextBlock("\n/* %s */\n"%comment))
def addInst(self, *args):
"""
Convenience function to construct a single Inst and add to items
"""
self.addCode(Inst(*args))
def addText(self,text):
"""
Convenience function to construct a TextBlock and add to items
"""
self.addCode(TextBlock(text))
def prettyPrint(self,indent=""):
ostream = ""
ostream += '%s%s "%s"\n'%(indent, type(self).__name__, self.name)
for i in self.itemList:
ostream += i.prettyPrint(indent.replace("|--", "| ") + "|--")
return ostream
"""
Test code:
mod1 = Code.Module("TopModule")
mod2 = Code.Module("Module-lvl2")
mod2.addCode(Code.Inst("bogusInst", "comments"))
mod3 = Code.Module("Module-lvl3")
mod3.addCode(Code.TextBlock("bogusTextBlock\nbogusTextBlock2\nbogusTextBlock3"))
mod3.addCode(Code.GlobalReadInst("bogusGlobalReadInst", "comments"))
mod2.addCode(mod3)
mod1.addCode(mod2)
mod1.addCode(Code.Inst("bogusInst", "comments"))
mod1.addCode(mod2)
print(mod1.prettyPrint())
Output:
Module "TopModule"
|--Module "Module-lvl2"
| |--Inst bogusInst // comments
| |--Module "Module-lvl3"
| | |--TextBlock
| | | |--bogusTextBlock
| | | |--bogusTextBlock2
| | | |--bogusTextBlock3
| | |--GlobalReadInst bogusGlobalReadInst // comments
|--Inst bogusInst // comments
|--Module "Module-lvl2"
| |--Inst bogusInst // comments
| |--Module "Module-lvl3"
| | |--TextBlock
| | | |--bogusTextBlock
| | | |--bogusTextBlock2
| | | |--bogusTextBlock3
| | |--GlobalReadInst bogusGlobalReadInst // comments
"""
def countType(self,ttype):
"""
Count number of items with specified type in this Module
Will recursively count occurrences in submodules
(Overrides Item.countType)
"""
count=0
for i in self.itemList:
if isinstance(i, Module):
count += i.countType(ttype)
else:
count += int(isinstance(i, ttype))
return count
def count(self):
count=0
for i in self.itemList:
if isinstance(i, Module):
count += i.count()
else:
count += 1
return count
def items(self):
"""
Return list of items in the Module
Items may be other Modules, TexBlock, or Inst
"""
return self.itemList
def flatitems(self):
"""
Return flattened list of items in the Module
Items in sub-modules will be flattened into single list
Items may be TexBlock or Inst
"""
flatitems = []
for i in self.itemList:
if isinstance(i, Module):
flatitems += i.flatitems()
else:
flatitems.append(i)
return flatitems
def addTempVgpr(self, vgpr):
self.tempVgpr = vgpr
class StructuredModule(Module):
def __init__(self, name=None):
Module.__init__(self,name)
self.header = Module("header")
self.middle = Module("middle")
self.footer = Module("footer")
self.addCode(self.header)
self.addCode(self.middle)
self.addCode(self.footer)
class Label (Item):
"""
Label that can be the target of a jump.
"""
def __init__(self, labelNum, comment):
self.labelNum = labelNum
self.comment = comment
def __str__(self):
t = "label_%04u:" % (self.labelNum)
if self.comment:
t += " /// %s" % self.comment
t += "\n"
return t
class TextBlock(Item):
"""
An unstructured block of text that can contain comments and instructions
"""
def __init__(self,text):
assert(isinstance(text, str))
self.text = text
self.name = text
def __str__(self):
return self.text
def prettyPrint(self, indent=""):
ostream = ""
ostream += "%s%s "%(indent, type(self).__name__)
l = [_i for _i in str(self).split("\n") if len(_i)>0]
l.insert(0, "")
ostream += "%s"%(("\n"+indent.replace("|-", "| |-")).join(l))
ostream += "\n"
return ostream
class Inst(Item):
"""
Inst is a single instruction and is base class for other instructions.
Currently just stores text+comment but over time may grow
"""
def __init__(self, *args):
params = args[0:len(args)-1]
comment = args[len(args)-1]
assert(isinstance(comment, str))
formatting = "%s"
if len(params) > 1:
formatting += " %s"
for i in range(0, len(params)-2):
formatting += ", %s"
instStr = formatting % (params)
self.text = self.formatWithComment(instStr, comment)
def formatWithComment(self, instStr, comment):
return "%-50s // %s\n" % (instStr, comment)
def __str__(self):
return self.text
class WaitCnt (Module):
"""
Construct a waitcnt from specified lgkmcnt and vmcnt:
lgkmcnt, vmcnt:
if -1 then will not be added to the wait term.
If lgkmcnt=vmcnt= -1 then the waitcnt is a nop and
an instruction with a comment is returned.
"""
def __init__(self, version,lgkmcnt=-1,vmcnt=-1,comment=""):
super().__init__("wait")
self.version = version
self.lgkmcnt = lgkmcnt
self.vmcnt = vmcnt
self.comment = "lgkmcnt={} vmcnt={}".format(lgkmcnt, vmcnt) + comment
# let this derived class play nicely with Module.prettyPrint()
self.__dict__.update(self.instructions().__dict__)
def instructions(self):
rv = Module()
main_args = []
wait_store = False
if self.lgkmcnt != -1:
currentIsa = self.version
maxLgkmcnt = globalParameters["AsmCaps"][currentIsa]["MaxLgkmcnt"]
seperateVscnt = globalParameters["ArchCaps"][currentIsa]["SeparateVscnt"]
wait_store = True
main_args += ["lgkmcnt(%u)" % (min(self.lgkmcnt,maxLgkmcnt))]
if self.vmcnt != -1:
main_args += ["vmcnt(%u)" % self.vmcnt]
if len(main_args) > 0:
rv.addInst("s_waitcnt", *main_args, self.comment)
if wait_store and seperateVscnt and self.vmcnt != -1:
rv.addInst("s_waitcnt_vscnt", "null", self.vmcnt, "writes")
else:
rv.addComment0(self.comment)
return rv
def __str__(self):
return str(self.instructions())
# uniq type that can be used in Module.countType
class GlobalReadInst (Inst):
def __init__(self,*args):
Inst.__init__(self,*args)
# uniq type that can be used in Module.countType
class LocalWriteInst (Inst):
def __init__(self,issuelatency,*args):
self.IssueLatency = issuelatency
Inst.__init__(self,*args)
# uniq type that can be used in Module.countType
class LocalReadInst (Inst):
def __init__(self,issuelatency,readToTempVgpr,*args):
self.IssueLatency = issuelatency
self.readToTempVgpr = readToTempVgpr
Inst.__init__(self,*args)
################################################################################
# MFMA Instruction
################################################################################
class MFMAInst (Inst):
"""
Construct a MFMA instruction from specified precision Type, aIndex, bIndex, PLR, innerUnroll:
dataType:
aIndex: index value from range (0, kernel["ThreadTile0"])
bIndex: index value from range (0, kernel["ThreadTile1"])
PLR: valida values 0,1
usage Module.addCode(Code.MFMAInst())
"""
def __init__(self,kernel,aIdx,bIdx,PLRval,innerUnroll):
self.endLine = ""
self.version = kernel["ISA"]
self.kernel = kernel
self.aIdx = aIdx
self.bIdx = bIdx
self.PLR = PLRval
self.innerUnroll = innerUnroll
def __str__(self):
# single precision
kStr = ""
numOfRowsperMfma = 1
numOfRowInsts = self.kernel["ThreadTile0"]/numOfRowsperMfma
#numOfColInsts = kernel["ThreadTile1"]/kernel["MatrixInstN"]
numOfDstRgs = (self.kernel["MatrixInstN"] * self.kernel["MatrixInstM"] * self.kernel["MatrixInstB"] // self.kernel["WavefrontSize"])
if self.kernel["ProblemType"]["DataType"].isSingle():
for iui in range(0, self.innerUnroll):
cStr = "a[(%u+%u*%u)*%u):((((%u+%u*%u)*%u)+%u)-1)]" % (self.aIdx,self.bIdx,numOfRowInsts,numOfDstRgs,self.aIdx,numOfDstRgs,self.bIdx,numOfRowInsts,numOfDstRgs)
aStr = "v[%s+%u]" \
% ("vgprValuA_X%u_I%u"%(self.PLR,iui), self.aIdx)
bStr = "v[%s+%u]" \
% ("vgprValuB_X%u_I%u"%(self.PLR,iui), self.bIdx)
kStr += "v_mfma_f32_%ux%ux%uf32 %s, %s, %s, %s%s" % (self.kernel["MatrixInstM"], self.kernel["MatrixInstN"], self.kernel["MatrixInstK"], cStr, aStr, bStr, cStr, self.endLine)
else:
printExit("Assembly doesn't support %s" % self.kernel["ProblemType"]["DataType"])
return self.formatWithComment(kStr, "")
def getLatency(self):
# return latency in cycles
return (self.kernel["MatrixInstM"] // 4 ) * 8
################################################################################
# Mac Instruction
# can be generic as VALU instruction
# implement later generic
################################################################################
class MacInst (Inst):
"""
Construct a mac instruction from specified dataType, aIndex, bIndex, PLR, innerUnroll:
- dataType:
- aIndex: index value from range (0, kernel["ThreadTile0"])
- bIndex: index value from range (0, kernel["ThreadTile1"])
PLR: valida values 0,1
usage `Module.addCode(Code.MacInst())`
"""
def __init__(self,kernel,aIdx,bIdx,PLRval,innerUnroll):
self.endLine = ""
self.version = kernel["ISA"]
self.kernel = kernel
self.aIdx = aIdx
self.bIdx = bIdx
self.PLR = PLRval
self.innerUnroll = innerUnroll
#def toCodeInst(self,kernel,aIdx,bIdx,PLRval,innerUnroll):
def __str__(self):
# half precision
kStr = ""
if self.kernel["ProblemType"]["DataType"].isHalf():
if self.version == (8,0,3):
for b in range(self.bIdx*2, (self.bIdx+1)*2):
for a in range(self.aIdx*2, (self.aIdx+1)*2):
for iui in range(0, self.innerUnroll):
# v_mac_f16 or v_fma_f16
cStr = "v[%s+%u+%u*%u+0]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"])
aStr = "v[%s+%u]" \
% ("vgprValuA_X%u_I%u"%(self.PLR,iui), self.aIdx)
bStr = "v[%s+%u]" \
% ("vgprValuB_X%u_I%u"%(self.PLR,iui), self.bIdx)
kStr += "v_mac_f16 %s, %s, %s\n" % (cStr, aStr, bStr) # FIXME op_sel
elif self.version == (9,0,0):
if self.kernel["ProblemType"]["HighPrecisionAccumulate"]:
# we treat HighPrecisionAccumulate as expanded packed math
#b = self.bIdx*2
#a = self.aIdx*2
if self.kernel["LocalDotLayout"] > 1 and self.innerUnroll == 2: # Only supports LocalDotLayout == 2 for now
cStr = "v[%s+%u*2+%u*%u*2+0*2+0]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"]) # *2 b/c of fp32
cidx = self.aIdx*2 + self.bIdx*self.kernel["ThreadTile0"]*2 + 0
aStr = "v[%s+%u]" \
% ("vgprValuA_X%u_I0"%self.PLR, self.aIdx)
bStr = "v[%s+%u]" \
% ("vgprValuB_X%u_I0"%self.PLR, self.bIdx)
kStr += "v_mad_mix_f32 %s, %s, %s, %s op_sel:[0,0,0] op_sel_hi:[1,1,0] //ValuC[%u] %s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
kStr += "v_mad_mix_f32 %s, %s, %s, %s op_sel:[1,1,0] op_sel_hi:[1,1,0] //ValuC[%u] %s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
cidx = self.aIdx*2 + self.bIdx*self.kernel["ThreadTile0"]*2 + 1
aStr = "v[%s+%u]" \
% ("vgprValuA_X%u_I1"%self.PLR, self.aIdx)
bStr = "v[%s+%u]" \
% ("vgprValuB_X%u_I0"%self.PLR, self.bIdx)
cStr = "v[%s+%u*2+%u*%u*2+0*2+1]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"]) # *2 b/c of fp32
kStr += "v_mad_mix_f32 %s, %s, %s, %s op_sel:[0,0,0] op_sel_hi:[1,1,0] //ValuC[%u]%s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
kStr += "v_mad_mix_f32 %s, %s, %s, %s op_sel:[1,1,0] op_sel_hi:[1,1,0] //ValuC[%u]%s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
cidx = self.aIdx*2 + self.bIdx*self.kernel["ThreadTile0"]*2 + self.kernel["ThreadTile0"] + 0
aStr = "v[%s+%u]" \
% ("vgprValuA_X%u_I0"%self.PLR, self.aIdx)
bStr = "v[%s+%u]" \
% ("vgprValuB_X%u_I1"%self.PLR, self.bIdx)
cStr = "v[%s+%u*2+%u*%u*2+%u*2+0]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"], self.kernel["ThreadTile0"]/2)
kStr += "v_mad_mix_f32 %s, %s, %s, %s op_sel:[0,0,0] op_sel_hi:[1,1,0] //ValuC[%u]%s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
kStr += "v_mad_mix_f32 %s, %s, %s, %s op_sel:[1,1,0] op_sel_hi:[1,1,0] //ValuC[%u]%s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
cidx = self.aIdx*2 + self.bIdx*self.kernel["ThreadTile0"]*2 + self.kernel["ThreadTile0"] + 1
aStr = "v[%s+%u]" \
% ("vgprValuA_X%u_I1"%self.PLR, self.aIdx)
bStr = "v[%s+%u]" \
% ("vgprValuB_X%u_I1"%self.PLR, self.bIdx)
cStr = "v[%s+%u*2+%u*%u*2+%u*2+1]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"], self.kernel["ThreadTile0"]/2)
kStr += "v_mad_mix_f32 %s, %s, %s, %s op_sel:[0,0,0] op_sel_hi:[1,1,0] //valuC[%u]%s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
kStr += "v_mad_mix_f32 %s, %s, %s, %s op_sel:[1,1,0] op_sel_hi:[1,1,0] //valuC[%u]%s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
#kStr += self.bomb(-13)
"""
ignore this, not quite correct for mixed precision
D.f[31:16] = S0.f[31:16] * S1.f[31:16] + S2.f[31:16]
D.f[15:00] = S0.f[15:00] * S1.f[15:00] + S2.f[15:00]
C[0] = A[0]*B[0]+D[0]
C[1] = A[1]*B[1]+D[1]
"""
else:
for iui in range(0, self.innerUnroll):
cStr = "v[%s+%u*2+%u*%u*2+0*2+0]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"]) # *2 b/c of fp32
cidx = self.aIdx*2 + self.bIdx*self.kernel["ThreadTile0"]*2 + 0
aStr = "v[%s+%u]" \
% ("vgprValuA_X%u_I%u"%(self.PLR,iui), self.aIdx)
bStr = "v[%s+%u]" \
% ("vgprValuB_X%u_I%u"%(self.PLR,iui), self.bIdx)
kStr += "v_mad_mix_f32 %s, %s, %s, %s op_sel:[0,0,0] op_sel_hi:[1,1,0] //ValuC[%u] iui=%u%s" % (cStr, aStr, bStr, cStr, cidx, iui, self.endLine)
cidx = self.aIdx*2 + self.bIdx*self.kernel["ThreadTile0"]*2 + 1
cStr = "v[%s+%u*2+%u*%u*2+0*2+1]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"]) # *2 b/c of fp32
kStr += "v_mad_mix_f32 %s, %s, %s, %s op_sel:[1,0,0] op_sel_hi:[1,1,0] //ValuC[%u]%s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
cidx = self.aIdx*2 + self.bIdx*self.kernel["ThreadTile0"]*2 + self.kernel["ThreadTile0"] + 0
cStr = "v[%s+%u*2+%u*%u*2+%u*2+0]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"], self.kernel["ThreadTile0"]/2)
kStr += "v_mad_mix_f32 %s, %s, %s, %s op_sel:[0,1,0] op_sel_hi:[1,1,0] //ValuC[%u]%s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
cidx = self.aIdx*2 + self.bIdx*self.kernel["ThreadTile0"]*2 + self.kernel["ThreadTile0"] + 1
cStr = "v[%s+%u*2+%u*%u*2+%u*2+1]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"], self.kernel["ThreadTile0"]/2)
kStr += "v_mad_mix_f32 %s, %s, %s, %s op_sel:[1,1,0] op_sel_hi:[1,1,0] //valuC[%u]%s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
"""
ignore this, not quite correct for mixed precision
D.f[31:16] = S0.f[31:16] * S1.f[31:16] + S2.f[31:16]
D.f[15:00] = S0.f[15:00] * S1.f[15:00] + S2.f[15:00]
C[0] = A[0]*B[0]+D[0]
C[1] = A[1]*B[1]+D[1]
"""
else:
#b = self.bIdx*2
#a = self.aIdx*2
for iui in range(0, self.innerUnroll):
cStr = "v[%s+%u+%u*%u+0]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"]) # /2 b/c of 2 f16's per 32-bit vgpr
aStr = "v[%s+%u]" \
% ("vgprValuA_X%u_I%u"%(self.PLR,iui), self.aIdx)
bStr = "v[%s+%u]" \
% ("vgprValuB_X%u_I%u"%(self.PLR,iui), self.bIdx)
kStr += "v_pk_fma_f16 %s, %s, %s, %s op_sel:[0,0,0] op_sel_hi:[1,0,1]%s" % (cStr, aStr, bStr, cStr, self.endLine)
cStr = "v[%s+%u+%u*%u+%u]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"], self.kernel["ThreadTile0"]/2)
kStr += "v_pk_fma_f16 %s, %s, %s, %s op_sel:[0,1,0] op_sel_hi:[1,1,1]%s" % (cStr, aStr, bStr, cStr, self.endLine)
"""
D.f[31:16] = S0.f[31:16] * S1.f[31:16] + S2.f[31:16]
D.f[15:00] = S0.f[15:00] * S1.f[15:00] + S2.f[15:00]
C[0] = A[0]*B[0]+D[0]
C[1] = A[1]*B[1]+D[1]
"""
elif self.version == (9,0,6):
if self.kernel["ProblemType"]["HighPrecisionAccumulate"]:
# we treat HighPrecisionAccumulate as expanded packed math
#b = self.bIdx*2
#a = self.aIdx*2
if self.kernel["LocalDotLayout"] > 1 and self.innerUnroll == 2: # Only supports LocalDotLayout == 2 for now
cStr = "v[%s+%u*2+%u*%u*2+0*2+0]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"]) # *2 b/c of fp32
cidx = self.aIdx*2 + self.bIdx*self.kernel["ThreadTile0"]*2 + 0
aStr = "v[%s+%u]" \
% ("vgprValuA_X%u_I0"%self.PLR, self.aIdx)
bStr = "v[%s+%u]" \
% ("vgprValuB_X%u_I0"%self.PLR, self.bIdx)
kStr += "v_dot2_f32_f16 %s, %s, %s, %s op_sel:[0,0] op_sel_hi:[1,1] //ValuC[%u] %s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
cidx = self.aIdx*2 + self.bIdx*self.kernel["ThreadTile0"]*2 + 1
aStr = "v[%s+%u]" \
% ("vgprValuA_X%u_I1"%self.PLR, self.aIdx)
bStr = "v[%s+%u]" \
% ("vgprValuB_X%u_I0"%self.PLR, self.bIdx)
cStr = "v[%s+%u*2+%u*%u*2+0*2+1]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"]) # *2 b/c of fp32
kStr += "v_dot2_f32_f16 %s, %s, %s, %s op_sel:[0,0] op_sel_hi:[1,1] //ValuC[%u]%s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
cidx = self.aIdx*2 + self.bIdx*self.kernel["ThreadTile0"]*2 + self.kernel["ThreadTile0"] + 0
aStr = "v[%s+%u]" \
% ("vgprValuA_X%u_I0"%self.PLR, self.aIdx)
bStr = "v[%s+%u]" \
% ("vgprValuB_X%u_I1"%self.PLR, self.bIdx)
cStr = "v[%s+%u*2+%u*%u*2+%u*2+0]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"], self.kernel["ThreadTile0"]/2)
kStr += "v_dot2_f32_f16 %s, %s, %s, %s op_sel:[0,0] op_sel_hi:[1,1] //ValuC[%u]%s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
cidx = self.aIdx*2 + self.bIdx*self.kernel["ThreadTile0"]*2 + self.kernel["ThreadTile0"] + 1
aStr = "v[%s+%u]" \
% ("vgprValuA_X%u_I1"%self.PLR, self.aIdx)
bStr = "v[%s+%u]" \
% ("vgprValuB_X%u_I1"%self.PLR, self.bIdx)
cStr = "v[%s+%u*2+%u*%u*2+%u*2+1]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"], self.kernel["ThreadTile0"]/2)
kStr += "v_dot2_f32_f16 %s, %s, %s, %s op_sel:[0,0] op_sel_hi:[1,1] //valuC[%u]%s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
#kStr += self.bomb(-13)
"""
ignore this, not quite correct for mixed precision
D.f[31:16] = S0.f[31:16] * S1.f[31:16] + S2.f[31:16]
D.f[15:00] = S0.f[15:00] * S1.f[15:00] + S2.f[15:00]
C[0] = A[0]*B[0]+D[0]
C[1] = A[1]*B[1]+D[1]
"""
else:
for iui in range(0, self.innerUnroll):
cStr = "v[%s+%u*2+%u*%u*2+0*2+0]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"]) # *2 b/c of fp32
cidx = self.aIdx*2 + self.bIdx*self.kernel["ThreadTile0"]*2 + 0
aStr = "v[%s+%u]" \
% ("vgprValuA_X%u_I%u"%(self.PLR,iui), self.aIdx)
bStr = "v[%s+%u]" \
% ("vgprValuB_X%u_I%u"%(self.PLR,iui), self.bIdx)
kStr += "v_fma_mix_f32 %s, %s, %s, %s op_sel:[0,0,0] op_sel_hi:[1,1,0] //ValuC[%u] iui=%u%s" % (cStr, aStr, bStr, cStr, cidx, iui, self.endLine)
cidx = self.aIdx*2 + self.bIdx*self.kernel["ThreadTile0"]*2 + 1
aStr = "v[%s+%u]" \
% ("vgprValuA_X%u_I%u"%(self.PLR,iui), self.aIdx)
bStr = "v[%s+%u]" \
% ("vgprValuB_X%u_I%u"%(self.PLR,iui), self.bIdx)
kStr += "v_fma_mix_f32 %s, %s, %s, %s op_sel:[0,0,0] op_sel_hi:[1,1,0] //ValuC[%u] iui=%u%s" % (cStr, aStr, bStr, cStr, cidx, iui, self.endLine)
cidx = self.aIdx*2 + self.bIdx*self.kernel["ThreadTile0"]*2 + 1
cStr = "v[%s+%u*2+%u*%u*2+0*2+1]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"]) # *2 b/c of fp32
kStr += "v_fma_mix_f32 %s, %s, %s, %s op_sel:[1,0,0] op_sel_hi:[1,1,0] //ValuC[%u]%s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
cidx = self.aIdx*2 + self.bIdx*self.kernel["ThreadTile0"]*2 + self.kernel["ThreadTile0"] + 0
cStr = "v[%s+%u*2+%u*%u*2+%u*2+0]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"], self.kernel["ThreadTile0"]/2)
kStr += "v_fma_mix_f32 %s, %s, %s, %s op_sel:[0,1,0] op_sel_hi:[1,1,0] //ValuC[%u]%s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
cidx = self.aIdx*2 + self.bIdx*self.kernel["ThreadTile0"]*2 + self.kernel["ThreadTile0"] + 1
cStr = "v[%s+%u*2+%u*%u*2+%u*2+1]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"], self.kernel["ThreadTile0"]/2)
kStr += "v_fma_mix_f32 %s, %s, %s, %s op_sel:[1,1,0] op_sel_hi:[1,1,0] //valuC[%u]%s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
"""
ignore this, not quite correct for mixed precision
D.f[31:16] = S0.f[31:16] * S1.f[31:16] + S2.f[31:16]
D.f[15:00] = S0.f[15:00] * S1.f[15:00] + S2.f[15:00]
C[0] = A[0]*B[0]+D[0]
C[1] = A[1]*B[1]+D[1]
"""
#kStr += self.bomb(-13)
else:
#b = self.bIdx*2
#a = self.aIdx*2
for iui in range(0, self.innerUnroll):
cStr = "v[%s+%u+%u*%u+0]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"]) # /2 b/c of 2 f16's per 32-bit vgpr
aStr = "v[%s+%u]" \
% ("vgprValuA_X%u_I%u"%(self.PLR,iui), self.aIdx)
bStr = "v[%s+%u]" \
% ("vgprValuB_X%u_I%u"%(self.PLR,iui), self.bIdx)
kStr += "v_pk_fma_f16 %s, %s, %s, %s op_sel:[0,0,0] op_sel_hi:[1,0,1]%s" % (cStr, aStr, bStr, cStr, self.endLine)
cStr = "v[%s+%u+%u*%u+%u]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"], self.kernel["ThreadTile0"]/2)
kStr += "v_pk_fma_f16 %s, %s, %s, %s op_sel:[0,1,0] op_sel_hi:[1,1,1]%s" % (cStr, aStr, bStr, cStr, self.endLine)
"""
D.f[31:16] = S0.f[31:16] * S1.f[31:16] + S2.f[31:16]
D.f[15:00] = S0.f[15:00] * S1.f[15:00] + S2.f[15:00]
C[0] = A[0]*B[0]+D[0]
C[1] = A[1]*B[1]+D[1]
"""
else:
raise NotImplementedError("Half-precision not supported for arch=%u" % self.version )
# integer i8x4
elif self.kernel["ProblemType"]["DataType"].isInt8x4():
if self.version == (8,0,3):
kStr += "// int8 not implemented yet for gfx803:"
elif self.version == (9,0,0):
kStr += "// int8 not implemented yet for gfx900:"
elif self.version == (9,0,6):
for iui in range(0, self.innerUnroll):
cidx = self.aIdx + self.bIdx*self.kernel["ThreadTile0"] + 0
cStr = "v[%s+%u+%u*%u]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"])
aStr = "v[%s+%u]" % ("vgprValuA_X%u_I%u"%(self.PLR,iui), self.aIdx)
bStr = "v[%s+%u]" % ("vgprValuB_X%u_I%u"%(self.PLR,iui), self.bIdx)
kStr += "v_dot4_i32_i8 %s, %s, %s, %s //valuC[%u]%s" % (cStr, aStr, bStr, cStr, cidx, self.endLine)
# single precision
elif self.kernel["ProblemType"]["DataType"].isSingle():
for iui in range(0, self.innerUnroll):
cStr = "v[%s+%u+%u*%u]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"])
aStr = "v[%s+%u]" \
% ("vgprValuA_X%u_I%u"%(self.PLR,iui), self.aIdx)
bStr = "v[%s+%u]" \
% ("vgprValuB_X%u_I%u"%(self.PLR,iui), self.bIdx)
#if a==0 and b==0:
# kStr += dump(aStr)
kStr += "_v_mac_f32 %s, %s, %s%s" % (cStr, aStr, bStr, self.endLine)
##if macIdx == self.kernel["PerformanceWaitLocation"]:
## kStr += "s_waitcnt lgkmcnt(%u) // extra wait for performance%s" \
## % (self.kernel["PerformanceWaitCount"], self.endLine)
##if macIdx == self.kernel["PerformanceSyncLocation"]:
## kStr += "s_barrier // extra barrier for performance%s" \
## % (self.endLine)
##macIdx += 1
# double precision
elif self.kernel["ProblemType"]["DataType"].isDouble():
for iui in range(0, self.innerUnroll):
cStr = "v[%s+(%u+%u*%u)*2:(%s+%u+%u*%u)*2+1]" % ("vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"], "vgprValuC", self.aIdx, self.bIdx, self.kernel["ThreadTile0"])
aStr = "v[%s+%u*2:%s+%u*2+1]" \
% ("vgprValuA_X%u_I%u"%(self.PLR,iui) , self.aIdx, "vgprValuA_X%u_I%u"%(self.PLR,iui), self.aIdx)
bStr = "v[%s+%u*2:%s+%u*2+1]" \
% ("vgprValuB_X%u_I%u"%(self.PLR,iui) , self.bIdx, "vgprValuB_X%u_I%u"%(self.PLR,iui), self.bIdx)
kStr += "v_fma_f64 %s, %s, %s, %s%s" % (cStr, aStr, bStr, cStr, self.endLine)
# other precision
else:
printExit("Assembly doesn't support %s" % self.kernel["ProblemType"]["DataType"])
return self.formatWithComment(kStr, "")
class BitfieldStructure(ctypes.Structure):
def field_desc(self, field):
fname = field[0]
bits = " ({}b)".format(field[2]) if len(field) > 2 else ""
value = getattr(self, fname)
return "{0}{1}: {2}".format(fname, bits, value)
def desc(self):
return '\n'.join([self.field_desc(field) for field in self._fields_])
class BitfieldUnion(ctypes.Union):
def __str__(self):
return "0x{0:08x}".format(self.value)
def desc(self):
return "hex: {}\n".format(self) + self.fields.desc()
class SrdUpperFields9XX(BitfieldStructure):
_fields_ = [("dst_sel_x", ctypes.c_uint, 3),
("dst_sel_y", ctypes.c_uint, 3),
("dst_sel_z", ctypes.c_uint, 3),
("dst_sel_w", ctypes.c_uint, 3),
("num_format", ctypes.c_uint, 3),
("data_format", ctypes.c_uint, 4),
("user_vm_enable", ctypes.c_uint, 1),
("user_vm_mode", ctypes.c_uint, 1),
("index_stride", ctypes.c_uint, 2),
("add_tid_enable", ctypes.c_uint, 1),
("_unusedA", ctypes.c_uint, 3),
("nv", ctypes.c_uint, 1),
("_unusedB", ctypes.c_uint, 2),
("type", ctypes.c_uint, 2)]
@classmethod
def default(cls):
return cls(data_format = 4)
class SrdUpperValue9XX(BitfieldUnion):
_fields_ = [("fields", SrdUpperFields9XX), ("value", ctypes.c_uint32)]
@classmethod
def default(cls):
return cls(fields=SrdUpperFields9XX.default())
class SrdUpperFields10XX(BitfieldStructure):
_fields_ = [("dst_sel_x", ctypes.c_uint, 3),
("dst_sel_y", ctypes.c_uint, 3),
("dst_sel_z", ctypes.c_uint, 3),
("dst_sel_w", ctypes.c_uint, 3),
("format", ctypes.c_uint, 7),
("_unusedA", ctypes.c_uint, 2),
("index_stride", ctypes.c_uint, 2),
("add_tid_enable", ctypes.c_uint, 1),
("resource_level", ctypes.c_uint, 1),
("_unusedB", ctypes.c_uint, 1),
("LLC_noalloc", ctypes.c_uint, 2),
("oob_select", ctypes.c_uint, 2),
("type", ctypes.c_uint, 2)]
@classmethod
def default(cls):
return cls(format = 4,
resource_level = 1,
oob_select = 3)
class SrdUpperValue10XX(BitfieldUnion):
_fields_ = [("fields", SrdUpperFields10XX), ("value", ctypes.c_uint32)]
@classmethod
def default(cls):
return cls(fields=SrdUpperFields10XX.default())
class SrdUpperFields11XX(BitfieldStructure):
_fields_ = [("dst_sel_x", ctypes.c_uint, 3),
("dst_sel_y", ctypes.c_uint, 3),
("dst_sel_z", ctypes.c_uint, 3),
("dst_sel_w", ctypes.c_uint, 3),
("format", ctypes.c_uint, 7),
("_unusedA", ctypes.c_uint, 2),
("index_stride", ctypes.c_uint, 2),
("add_tid_enable", ctypes.c_uint, 1),
("resource_level", ctypes.c_uint, 1),
("_unusedB", ctypes.c_uint, 1),
("LLC_noalloc", ctypes.c_uint, 2),
("oob_select", ctypes.c_uint, 2),
("type", ctypes.c_uint, 2)]
@classmethod
def default(cls):
return cls(format = 4,
resource_level = 1,
oob_select = 3)
class SrdUpperValue11XX(BitfieldUnion):
_fields_ = [("fields", SrdUpperFields11XX), ("value", ctypes.c_uint32)]
@classmethod
def default(cls):
return cls(fields=SrdUpperFields11XX.default())
# gfx12 buffer-resource DWORD3 layout.
# Mirrors SrdUpperFields12XX in rocisa (projects/hipblaslt/tensilelite/rocisa/rocisa/
# include/code.hpp) which is the gfx12-aware code generator path. Differences vs.
# gfx11: no LLC_noalloc field, _unusedB widens from 1 to 3 bits, and oob_select moves
# from bits 27-28 to bits 28-29. With the gfx9 fallback (data_format=4) gfx1201 raw
# buffer stores are silently dropped because oob_select reads as 0; we need oob_select=3
# and format=32 for the RAW buffer mode used by all Tensile buffer load/store ops.
class SrdUpperFields12XX(BitfieldStructure):
_fields_ = [("dst_sel_x", ctypes.c_uint, 3),
("dst_sel_y", ctypes.c_uint, 3),
("dst_sel_z", ctypes.c_uint, 3),
("dst_sel_w", ctypes.c_uint, 3),
("format", ctypes.c_uint, 7),
("_unusedA", ctypes.c_uint, 2),
("index_stride", ctypes.c_uint, 2),
("add_tid_enable", ctypes.c_uint, 1),
("resource_level", ctypes.c_uint, 1),
("_unusedB", ctypes.c_uint, 3),
("oob_select", ctypes.c_uint, 2),
("type", ctypes.c_uint, 2)]
@classmethod
def default(cls):
return cls(format = 32,
oob_select = 3)
class SrdUpperValue12XX(BitfieldUnion):
_fields_ = [("fields", SrdUpperFields12XX), ("value", ctypes.c_uint32)]
@classmethod
def default(cls):
return cls(fields=SrdUpperFields12XX.default())
def SrdUpperValue(isa):
if isa[0] == 12:
return SrdUpperValue12XX.default()
elif isa[0] == 11:
return SrdUpperValue11XX.default()
elif isa[0] == 10:
return SrdUpperValue10XX.default()
else:
return SrdUpperValue9XX.default()
class OpTemplate(Module):
"""
Base template for high level operator (mem/alu), template contain data and code
section for implementing high level operator;
resources required for high level operator are already accounted in main parts
kernel section
Usage: high level operator like unary,binary operators opearting on tensor dimension or
memory operator that stores/loads tensor from near/far memory
Initially monolithic code segments doing high level operator vpgr/sgpr (temp ones ) are placeholders
must be replaced when its actually placed in main kernel
"""
## constructor
def __init__(self, name=""):
self.name = name
## list order of operations
## Module key "LaddrCalcA", "lrda", "lwra", "StoreC", "loadC",
## order of code is important
self.itemList = [] ## list of Code Modules
self.tmpSgpr = None
self.tmpVgpr = None
def __str__(self):
s = ""
if printModuleNames:
s += "// %s { \n" % self.name
s += "".join([str(x) for x in self.itemList])
if printModuleNames:
s += "// } %s\n" % self.name
return s
def findNamedCode(self, targetName):
return next((Moditem for Moditem in self.itemList if Moditem.name==targetName), None)
def addModule(self, ModItem):
"""
Add specified Code modules to the list of Modules in the opTemplate
ModItem MUST be a Module, list of instructions not string
returns Module to facilitate one-line create/add patterns
"""
if isinstance(ModItem,Module):
#self.itemList.append(ModItem)
self.itemList.extend(ModItem.itemList)
else:
assert 0, "unknown ModItem type (%s) for OpTemplate.addCode. Moditem=%s"%(type(ModItem), ModItem)
return ModItem
def addTempSgpr(self, sgpr):
self.tmpSgpr = sgpr
def addTempVgpr(self, vgpr):
self.tmpVgpr = vgpr
class MemOpTemplate(OpTemplate):
"""
template for local/global data movement code sections
list should have sequence of code modules supporting data movement,
including offset calculation , offset increment, load/store
current code sections are expected to follow program consistency (no out of order in scheduling them)
This needs further refinement- handling temp registers in code
temporary register(s) used at the time of code generation(S) need to be replaced when its called
temp register in instruction should use _sgpr%len_ or _vgpr%len_ len determines number of temp register
before using code , allocate number of registers required for code
"""
def __init__(self, name=""):
self.name = name
self.itemList = [] ## list of COde Modules
self.tmpSgpr = None
self.tmpVgpr = None