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

1472 lines
81 KiB
Python

################################################################################
#
# Copyright (C) 2022-2026 Advanced Micro Devices, Inc. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cop-
# ies of the Software, and to permit persons to whom the Software is furnished
# to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM-
# PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNE-
# CTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
################################################################################
import ctypes
import math
import struct
from collections import OrderedDict
from copy import deepcopy
from enum import Enum, IntFlag
from typing import List, Union
from rocisa import rocIsa
from rocisa.code import Module, TextBlock
from rocisa.container import VCC, EXEC, vgpr, sgpr, HolderContainer, RegisterContainer, Holder
from rocisa.enum import InstType
from rocisa.instruction import Instruction, SMovB32, SNop, SSetMask, VAddF16, VAddF32, \
VAddPKF16, VAndB32, VCmpGEF16, VCmpGEF32, VCmpGEF64, VCmpGEI32, VCmpGTF16, VCmpGTF32, \
VCmpGTF64, VCmpGTI32, VCmpXClassF32, VCmpXLtF32, VCndMaskB32, VExpF16, VExpF32, VFmaF16, \
VFmaF32, VFmaPKF16, VMaxF32, VMaxF64, VMaxI32, VMaxPKF16, VMed3I32, VMinF16, VMinF32, \
VMinF64, VMinI32, VMovB32, VMulF16, VMulF32, VMulF64, VMulLOU32, VMulPKF16, VRcpF16, \
VRcpF32, VSubF32, VSubI32
from Tensile.Common.DataType import DataType
from Tensile.Common.Utilities import printExit, printWarning
from dataclasses import dataclass
################################################################################
# How to add an activation
# 1. Add a new type in ActivationType
# 2. Create a new getXXXAssembly function in class Activation
# 3. Add if-else condition in generateAssembly in class Activation
# 4. Add if-else condition in generateInlineAssemblyBody in class
# ActivationInline
#
# Helper function(s)
# 1. getRegAndInitAssembly
# ```
# getRegAndInitAssembly(<v for vgpr/ s for sgpr>,
# <False for reg pool/ True for tmp reg pool>,
# <size of reg>,
# <init value>,
# <key>,
# <comment>)
# ```
# Returns
# 1. sgprinf: The original checkOut return value
# 2. regInitStr: The init instruction string
#
# Example,
# ```
# sgprinf, regInitStr = self.getRegAndInitAssembly('s', False, 1, \
# "0x3f4c422a", "FloatGeluK0", "float gelu k0")
# ```
# this will generate ``regInitStr`` as
# ```
# s_mov_b32 sXX, "0x3f4c422a" // float16 max
# ```
# if the key "FloatGeluK0" is not found in sgprDict
# 2. class ActivationRegisterPool
# A wrapper of RegisterPool. All the checkOut-ed registers will be checkIn-ed
# at the end of the numBatches for loop.
# When ActivationType is set to 'all', the registers will be checkIn-ed after
# activation's gwvw for loop.
################################################################################
################################################################################
# This is the ActivationType class
# stringList:
# This list stores the names of extra arguments, e.g.
# y = (x > 0) ? x : x * alpha
# lookup:
# This dict stores the supported activation types as keys and number of
# arguments as values. Insert any new type before 'none' and 'all'. The
# sequence of the table should match the enum in Activation.hpp.
#
# To add an activation type, see the instruction in Activation.py.
################################################################################
class ActivationAvailable:
def __init__(self, canHalf=False, canSingle=False, canDouble=False, canBFloat16=False, canInt8=False, canInt16=False, canInt32=False):
self.half = canHalf
self.single = canSingle
self.double = canDouble
self.bfloat16 = canBFloat16
self.int8 = canInt8
self.int16 = canInt16
self.int32 = canInt32
class ActivationTypeRegister:
def __init__(self, name, isGradient, extraArgs, canHalf=False, canSingle=False, canDouble=False, canBFloat16=False, canInt8=False, canInt16=False, canInt32=False):
self.name = name
self.isGradient = isGradient
self.extraArgs = extraArgs
self.can = ActivationAvailable(canHalf, canSingle, canDouble, canBFloat16, canInt8, canInt16, canInt32)
def typeAvailable(self, dataType):
if dataType.isHalf() and self.can.half:
return True
elif dataType.isSingle() and self.can.single:
return True
elif dataType.isDouble() and self.can.double:
return True
elif dataType.isBFloat16() and self.can.bfloat16:
return True
elif dataType.isInt8() and self.can.int8:
return True
elif dataType.isInt32() and self.can.int32:
return True
return False
class ActivationType:
class Export(Enum):
NORMAL = 0
GRADONLY = 1
BOTH = 2
# Use bit mask to maintain the supported information.
class SupportedBy(IntFlag):
HIPBLASLT = 0b01
TENSILE = 0b10
ALL = 0b11
stringList = ['alpha', 'beta', 'gamma', 'delta' ]
# Exp is only for verification. So we will not return exp in the supported list.
# Half,Single,Double,BFloat16, Int8, Int16, Int32
lookupVeri = OrderedDict([('exp', ActivationTypeRegister('exp', 0, True, True, False, False, False, False, False)) ])
# Note: The BFloat16 gemm uses Single type activations. The int8 gemm uses int32 type activations.
# Half,Single,Double,BFloat16, Int8, Int16, Int32
lookup = OrderedDict([('none', { \
'instance': ActivationTypeRegister('none', False, 0, True, True, True, True, True, True, True), \
'supported_by': SupportedBy.TENSILE | SupportedBy.HIPBLASLT}), \
('abs', { \
'instance': ActivationTypeRegister('abs', False, 0, True, True, True, True, False, False, True), \
'supported_by': SupportedBy.TENSILE}), \
('clippedrelu', { \
'instance': ActivationTypeRegister('clippedrelu', False, 2, True, True, True, False, False, False, True), \
'supported_by': SupportedBy.TENSILE}), \
('gelu', { \
'instance': ActivationTypeRegister('gelu', False, 0, True, True, False, False, False, False, False), \
'supported_by': SupportedBy.TENSILE | SupportedBy.HIPBLASLT}), \
('leakyrelu', { \
'instance': ActivationTypeRegister('leakyrelu', False, 1, True, True, True, False, False, False, True), \
'supported_by': SupportedBy.TENSILE}), \
('relu', { \
'instance': ActivationTypeRegister('relu', False, 0, True, True, True, False, False, False, True), \
'supported_by': SupportedBy.TENSILE | SupportedBy.HIPBLASLT}), \
('sigmoid', { \
'instance': ActivationTypeRegister('sigmoid', False, 0, True, True, False, False, False, False, False), \
'supported_by': SupportedBy.TENSILE | SupportedBy.HIPBLASLT}), \
('tanh', { \
'instance': ActivationTypeRegister('tanh', False, 2, True, True, False, False, False, False, False), \
'supported_by': SupportedBy.TENSILE}), \
('dgelu', { \
'instance': ActivationTypeRegister('dgelu', True, 0, False, True, False, False, False, False, False), \
'supported_by': SupportedBy.TENSILE | SupportedBy.HIPBLASLT}), \
('drelu', { \
'instance': ActivationTypeRegister('drelu', True, 0, False, True, False, False, False, False, False), \
'supported_by': SupportedBy.TENSILE | SupportedBy.HIPBLASLT}), \
('geluscaling', { \
'instance': ActivationTypeRegister('geluscaling', False, 1, True, True, False, False, False, False, False), \
'supported_by': SupportedBy.TENSILE}), \
('silu', { \
'instance': ActivationTypeRegister('silu', False, 0, True, True, False, False, False, False, False), \
'supported_by': SupportedBy.TENSILE | SupportedBy.HIPBLASLT}), \
('swish', { \
'instance': ActivationTypeRegister('swish', False, 1, True, True, False, False, False, False, False), \
'supported_by': SupportedBy.TENSILE}), \
('clamp', { \
'instance': ActivationTypeRegister('clamp', False, 2, True, True, True, False, False, False, True), \
'supported_by': SupportedBy.TENSILE | SupportedBy.HIPBLASLT}), \
('hipblaslt_all', { \
'instance': ActivationTypeRegister('hipblaslt_all', False, 0), \
'supported_by': SupportedBy.HIPBLASLT}), \
('all', { \
'instance': ActivationTypeRegister('all', False, 0), \
'supported_by': SupportedBy.TENSILE | SupportedBy.HIPBLASLT}) ])
def __init__(self, value):
if isinstance(value, str):
strValue = value.lower()
if strValue in self.lookup:
self.value = strValue
elif strValue in self.lookupVeri:
self.value = strValue
else:
raise RuntimeError("Unrecognized activation type %s"%value)
elif isinstance(value, ActivationType):
self.value = value.value
else:
raise RuntimeError("Unrecognized input type %s, should be string or ActivationType"%str(value))
def passActivation(self, isGradient, exportType: Export) -> bool:
if exportType == ActivationType.Export.NORMAL:
return isGradient
elif exportType == ActivationType.Export.GRADONLY:
return not isGradient
elif exportType == ActivationType.Export.BOTH:
return False
def getAdditionalArgNum(self, exportType: Export=Export.NORMAL):
if self.value in ['all', 'hipblaslt_all']:
maxArgNum = 0
for _, value in self.lookup.items():
activationInst = value['instance']
if self.passActivation(activationInst.isGradient, exportType):
continue
maxArgNum = max(maxArgNum, activationInst.extraArgs)
return maxArgNum
elif self.value in self.lookup:
return self.lookup[self.value]['instance'].extraArgs
return 0
# Check if the given components are supported by the configuration using a bit mask.
# This function performs a bitwise AND operation between the config and components
# to determine if the specified components are included in the configuration.
def fitSupported(self, config: SupportedBy, components: SupportedBy):
return (config & components)
def getAdditionalArgStringList(self, addPrefix=True):
list = []
for i in range(0, self.getAdditionalArgNum()):
if addPrefix:
list.append("activation" + self.stringList[i].capitalize())
else:
list.append(self.stringList[i])
return list
@classmethod
def getEnumIndex(cls, enumStr):
return list(cls.lookup.keys()).index(enumStr)
@classmethod
def getEnumStrList(cls, dataType, configSupported, includeNone = True, exportType: Export=Export.NORMAL):
enumList = []
for key, value in cls.lookup.items():
activationInst = value['instance']
components = value['supported_by']
if cls.passActivation(cls, activationInst.isGradient, exportType):
continue
if (((key != 'none') or includeNone) and (key not in ['all', 'hipblaslt_all'])):
if (activationInst.typeAvailable(dataType)) and (cls.fitSupported(cls, configSupported, components)) :
enumList.append(key)
if not enumList:
printWarning("No available activation for this data type %s.\n"%str(dataType))
return enumList
def state(self): return self.value.capitalize()
def __repr__(self):
return self.__str__()
def __str__(self):
return self.value.capitalize()
def __eq__(self, other):
if isinstance(other, str):
return self.value == other.lower()
elif isinstance(other, ActivationType):
return self.value == other.value
else:
raise RuntimeError("Unrecognized type in rhs, should be string or ActivationType")
def __lt__(self, other):
if isinstance(other, str):
return self.value < other.lower()
elif isinstance(other, ActivationType):
return self.value < other.value
else:
raise RuntimeError("Unrecognized type in rhs, should be string or ActivationType")
def toEnum(self):
return self.value.capitalize()
@dataclass
class actCacheInfo:
usePK: bool
saturateI8: bool
enableGuard: bool
isAlt: bool
prefix: str
vgprIdxList: List[List[RegisterContainer]]
module: Module
vgprCounter: int
sgprCounter: int
def isSame(self, usePK, saturateI8, enableGuard, isAlt, prefix):
return (self.usePK == usePK) and (self.saturateI8 == saturateI8) and \
(self.enableGuard == enableGuard) and (self.prefix == prefix) and \
(self.isAlt == isAlt)
ActivationMagicNumbers = {"FloatGeluK0": 0x3f4c422a, \
"FloatGeluK1": 0x3d372713, \
"Float16GeluK1": 0x29b9, \
# 0.0535161
"FloatDGeluK0": 0x3d5b33b3, \
# 0.398942
"FloatDGeluK1": 0x3ecc4220, \
# 0.035677
"FloatDGeluK2": 0x3d12220c, \
# 0.797885
"FloatDGeluK3": 0x3f4c4231}
# float32 union
class floatUnion(ctypes.Union):
_fields_ = [('u', ctypes.c_uint), ('f', ctypes.c_float)]
class ActivationModule:
################################################################################
################################################################################
###
### Public Functions
###
################################################################################
################################################################################
def __init__(self) -> None:
# Counter
self.vgprCounter = 0
self.sgprCounter = 0
# Properties
self.usePK = True
self.saturateI8 = False
self.vgprPrefixFormat = ""
# We only need to run CombineInstructions if the called module calls another
# module inside.
self.needCombine = False
# Cache
self.cacheDict = {}
self.useCache = False
self.labelCounter = 0
self.enableGuard = False
self.isAlt = False
def __reduce__(self):
return (ActivationModule, ())
# Public function
def getModule(self, cDataType, activationType, vgprIn, vgprOut):
if self.useCache:
module = self.getCache(cDataType, activationType, vgprIn, vgprOut)
if module:
return module
module = ""
self.needCombine = False
self.resetGprCounter()
if (activationType == 'abs'):
module = self.getAbsModule(cDataType, vgprIn, vgprOut)
elif (activationType == 'clippedrelu'):
module = self.getClippedReluModule(cDataType, vgprIn, vgprOut, "activationAlpha", "activationBeta")
elif (activationType == 'exp'):
module = self.getExpModule(cDataType, vgprIn, vgprOut)
elif (activationType == 'gelu'):
module = self.getGeluModule(cDataType, vgprIn, vgprOut)
elif (activationType == 'geluscaling'):
module = self.getGeluModule(cDataType, vgprIn, vgprOut, "activationAlpha")
elif (activationType == 'leakyrelu'):
module = self.getLeakyReluModule(cDataType, vgprIn, vgprOut, "activationAlpha")
elif (activationType == 'relu'):
module = self.getReluModule(cDataType, vgprIn, vgprOut)
elif (activationType == 'sigmoid'):
module = self.getSigmoidModule(cDataType, vgprIn, vgprOut)
elif (activationType == 'tanh'):
module = self.getTanhModule(cDataType, vgprIn, vgprOut, "activationAlpha", "activationBeta")
elif (activationType == 'dgelu'):
module = self.getDGeluModule(cDataType, vgprIn, vgprOut)
elif (activationType == 'drelu'):
module = self.getDReluModule(cDataType, vgprIn, vgprOut)
elif (activationType == 'silu'):
module = self.getSiluModule(cDataType, vgprIn, vgprOut)
elif (activationType == 'swish'):
module = self.getSwishModule(cDataType, vgprIn, vgprOut, "activationAlpha")
elif (activationType == 'clamp'):
module = self.getClampModule(cDataType, vgprIn, vgprOut, "activationAlpha", "activationBeta")
elif (activationType == 'none'):
return Module("No activation")
else:
return Module("%s not implemented"%activationType)
module = self.postProcess(cDataType, module)
# Only create cache when In != Out else will cause an error
if self.useCache and (vgprIn != vgprOut):
self.createCache(cDataType, activationType, vgprIn, vgprOut, module)
return module
def getAllGprUsage(self, cDataType, actType, exportType: ActivationType.Export=ActivationType.Export.NORMAL) -> dict:
usage = {}
if actType == 'all':
enumList = ActivationType.getEnumStrList(cDataType, configSupported=ActivationType.SupportedBy.ALL, exportType=exportType)
elif actType == 'hipblaslt_all':
enumList = ActivationType.getEnumStrList(cDataType, configSupported=ActivationType.SupportedBy.HIPBLASLT, exportType=exportType)
else:
enumList = [str(actType)]
for enumStr in enumList:
_ = self.getModule(cDataType, enumStr, 0, 1) # dummy vgpr
usage[enumStr] = {"vgpr": self.vgprCounter, "sgpr": self.sgprCounter}
return usage
def postProcess(self, cDataType, module):
if self.needCombine:
CombineInstructions(module)
module = ConvertCoeffToHex(module, cDataType, self.usePK)
return module
def assignGpr(self, module, vgprIdx, sgprIdx):
patternPrefix = ["v", "s"]
gprIdx = [vgprIdx, sgprIdx]
for idx, pf in enumerate(patternPrefix):
module = HolderToGpr(module, gprIdx[idx], pf)
return module
def setUsePK(self, usePK):
self.usePK = usePK
def setSaturationForInt8(self, sat):
self.saturateI8 = sat
def setVgprPrefixFormat(self, formatting):
self.vgprPrefixFormat = formatting
def setUseCache(self, cache: bool):
self.useCache = cache
def setGuard(self, guard: bool):
self.enableGuard = guard
def setAlt(self, alt: bool):
self.isAlt = alt
################################################################################
################################################################################
###
### Internal Helper Functions
###
################################################################################
################################################################################
def resetGprCounter(self):
self.vgprCounter = 0
self.sgprCounter = 0
def getVgpr(self, num):
value = self.vgprCounter
self.vgprCounter += num
return value
def getSgpr(self, num):
value = self.sgprCounter
self.sgprCounter += num
return value
def vgprPrefix(self, *args):
if isinstance(args[0], int) and self.vgprPrefixFormat:
vgprStr = self.vgprPrefixFormat%args[0]
else:
vgprStr = args[0]
if len(args) == 1:
return vgpr(vgprStr)
else:
args = args[1]
return vgpr(vgprStr, args)
################################################################################
################################################################################
###
### Activation Functions
###
################################################################################
################################################################################
def getAbsModule(self, cDataType, vgprIn, vgprOut):
module = Module("Abs")
if cDataType.isHalf() or cDataType.isBFloat16():
absMagic = "0x7fff7fff" if self.usePK else "0x7fff"
module.add(VAndB32(dst=self.vgprPrefix(vgprOut), src0=absMagic, src1=self.vgprPrefix(vgprIn), comment="Remove sign bit"))
elif cDataType.isSingle():
module.add(VAndB32(dst=self.vgprPrefix(vgprOut), src0="0x7fffffff", src1=self.vgprPrefix(vgprIn), comment="Remove sign bit"))
elif cDataType.isDouble():
module.add(VAndB32(dst=self.vgprPrefix(vgprOut+1), src0="0x7fffffff", src1=self.vgprPrefix(vgprIn+1), comment="Remove sign bit"))
elif cDataType.isInt32():
vgprTemp = self.getVgpr(1)
module.add(VSubI32(dst=vgpr(Holder(idx=vgprTemp)), src0=0, src1=self.vgprPrefix(vgprIn), comment="x2 = -x"))
if self.saturateI8:
vgprTemp2 = self.getVgpr(1)
module.add(VMovB32(dst=vgpr(Holder(idx=vgprTemp2)), src=hex(127), comment="value = 127"))
module.add(VMed3I32(dst=self.vgprPrefix(vgprOut), src0=self.vgprPrefix(vgprIn), src1=vgpr(Holder(idx=vgprTemp)), src2=vgpr(Holder(idx=vgprTemp2)), comment="y = min(127, max(x, x2))"))
else:
module.add(VMaxI32(dst=self.vgprPrefix(vgprOut), src0=vgpr(Holder(idx=vgprTemp)), src1=self.vgprPrefix(vgprIn), comment="y = max(x, x2)"))
else:
raise RuntimeError("Unsupported data type %s."%cDataType.toDevice("HIP"))
return module
def getClippedReluModule(self, cDataType, vgprIn, vgprOut, activationAlpha, activationBeta):
module = Module("ClippedRelu")
if cDataType.isHalf():
vgprTemp = self.getVgpr(1)
for i in range(0, 2):
select_bit = SelectBit.WORD_0 if i == 0 else SelectBit.WORD_1
module.add(VCmpGTF16(dst=VCC(), src0=self.vgprPrefix(vgprIn), src1=sgpr(activationAlpha), \
sdwa=SDWAModifiers(src0_sel=select_bit, src1_sel=SelectBit.WORD_0), comment="x > alpha?"))
module.add(VMinF16(dst=self.vgprPrefix(vgprOut), src0=sgpr(activationBeta), src1=self.vgprPrefix(vgprIn), \
sdwa=SDWAModifiers(dst_sel=select_bit, dst_unused=UnusedBit.UNUSED_PRESERVE, \
src0_sel=select_bit, src1_sel=select_bit), \
comment="min(x, beta)"))
module.add(VMinF16(dst=vgpr(Holder(idx=vgprTemp)), src0=sgpr(activationBeta), src1=0.0, \
sdwa=SDWAModifiers(dst_sel=select_bit, dst_unused=UnusedBit.UNUSED_PRESERVE, \
src0_sel=select_bit, src1_sel=select_bit), \
comment="min(0, beta)"))
module.add(VCndMaskB32(dst=self.vgprPrefix(vgprOut), src0=vgpr(Holder(idx=vgprTemp)), src1=self.vgprPrefix(vgprOut), \
sdwa=SDWAModifiers(dst_sel=select_bit, dst_unused=UnusedBit.UNUSED_PRESERVE, \
src0_sel=select_bit, src1_sel=select_bit), \
comment="set x to min(0, beta) if <= alpha"))
module.add(SNop(waitState=0, comment="1 wait states")) # workaround for emulator
elif cDataType.isSingle():
module.add(VCmpGTF32(dst=VCC(), src0=self.vgprPrefix(vgprIn), src1=sgpr(activationAlpha), comment="x > alpha ?"))
module.add(VMinF32(dst=self.vgprPrefix(vgprIn), src0=sgpr(activationBeta), src1=self.vgprPrefix(vgprIn), comment="min(x, beta)"))
vgprTemp = self.getVgpr(1)
module.add(VMinF32(dst=vgpr(Holder(idx=vgprTemp)), src0=sgpr(activationBeta), src1=0.0, comment="min(0, beta)"))
module.add(VCndMaskB32(dst=self.vgprPrefix(vgprIn), src0=vgpr(Holder(idx=vgprTemp)), src1=self.vgprPrefix(vgprIn), comment="set x to min(0, beta) if <= alpha"))
elif cDataType.isDouble():
module.add(VCmpGTF64(dst=VCC(), src0=self.vgprPrefix(vgprIn, 2), src1=sgpr(activationAlpha, 2), comment="x > alpha ?"))
module.add(VMinF64(dst=self.vgprPrefix(vgprIn, 2), src0=sgpr(activationBeta, 2), src1=self.vgprPrefix(vgprIn, 2), comment="min(x, beta)"))
vgprTemp = self.getVgpr(2)
module.add(VMinF64(dst=vgpr(Holder(idx=vgprTemp), 2), src0=sgpr(activationBeta, 2), src1=0.0, comment="min(0, beta)"))
module.add(VCndMaskB32(dst=self.vgprPrefix(vgprIn), src0=vgpr(Holder(idx=vgprTemp)), src1=self.vgprPrefix(vgprIn), comment="set x to min(0, beta) if <= alpha"))
module.add(VCndMaskB32(dst=self.vgprPrefix(vgprIn+1), src0=vgpr(Holder(idx=vgprTemp+1)), src1=self.vgprPrefix(vgprIn+1), comment="set x to min(0, beta) if <= alpha"))
elif cDataType.isInt32():
module.add(VCmpGTI32(dst=VCC(), src0=self.vgprPrefix(vgprIn), src1=sgpr(activationAlpha), comment="x > alpha ?"))
module.add(VMinI32(dst=self.vgprPrefix(vgprIn), src0=sgpr(activationBeta), src1=self.vgprPrefix(vgprIn), comment="min(x, beta)"))
vgprTemp = self.getVgpr(1)
module.add(VMinI32(dst=vgpr(Holder(idx=vgprTemp)), src0=sgpr(activationBeta), src1=0, comment="min(0, beta)"))
module.add(VCndMaskB32(dst=self.vgprPrefix(vgprIn), src0=vgpr(Holder(idx=vgprTemp)), src1=self.vgprPrefix(vgprIn), comment="set x to min(0, beta) if <= alpha"))
return module
def getExpModule(self, cDataType, vgprIn, vgprOut):
ti = rocIsa.getInstance()
module = Module("Exp")
if cDataType.isHalf():
sgprMagic = self.getSgpr(1)
module.add(SMovB32(dst=sgpr(Holder(idx=sgprMagic)), src=math.log(math.e,2), comment="exp magic"))
if self.usePK:
module.add(VMulPKF16(dst=self.vgprPrefix(vgprOut), src0=sgpr(Holder(idx=sgprMagic)), src1=self.vgprPrefix(vgprIn), comment="exp step 1"))
for i in range(0, 2):
select_bit = SelectBit.WORD_0 if i == 0 else SelectBit.WORD_1
module.add(VExpF16(dst=self.vgprPrefix(vgprOut), src=self.vgprPrefix(vgprOut), \
sdwa=SDWAModifiers(dst_sel=select_bit, dst_unused=UnusedBit.UNUSED_PRESERVE, \
src0_sel=select_bit), \
comment="exp step 2"))
if ti.getArchCaps()["TransOpWait"]:
module.add(SNop(waitState=0, comment="1 wait states"))
else:
module.add(VMulF16(dst=self.vgprPrefix(vgprOut), src0=sgpr(Holder(idx=sgprMagic)), src1=self.vgprPrefix(vgprIn), comment="exp step 1"))
module.add(VExpF16(dst=self.vgprPrefix(vgprOut), src=self.vgprPrefix(vgprOut), comment="exp step 2"))
if ti.getArchCaps()["TransOpWait"]:
module.add(SNop(waitState=0, comment="1 wait states"))
elif cDataType.isSingle():
module.add(VMulF32(dst=self.vgprPrefix(vgprOut), src0=math.log(math.e,2), src1=self.vgprPrefix(vgprIn), comment="exp step 1"))
module.add(VExpF32(dst=self.vgprPrefix(vgprOut), src=self.vgprPrefix(vgprOut), comment="exp step 2" ))
if ti.getArchCaps()["TransOpWait"]:
module.add(SNop(waitState=0, comment="1 wait states"))
else:
raise RuntimeError("Unsupported data type %s."%cDataType.toDevice("HIP"))
return module
def getGeluModule(self, cDataType, vgprIn, vgprOut, activationAlpha=None):
self.needCombine = True
module = Module("Gelu")
# Gelu(x) = 0.5 * x * (1 + tanh(k0 * x * (1 + k1 * x * x)))
if cDataType.isHalf():
flt16GeluK1Str = HexToStr(cDataType, self.usePK, ActivationMagicNumbers["Float16GeluK1"])
sgprMagicK1 = self.getSgpr(1)
sgprPKLiteral = self.getSgpr(1)
module.add(SMovB32(dst=sgpr(Holder(idx=sgprMagicK1)), src=flt16GeluK1Str, comment="Float16GeluK1" ))
module.add(SMovB32(dst=sgpr(Holder(idx=sgprPKLiteral)), src=coef.f))
vgprTemp = self.getVgpr(1)
if self.usePK:
module.add(VMulPKF16(dst=vgpr(Holder(idx=vgprTemp)), src0=self.vgprPrefix(vgprIn), src1=self.vgprPrefix(vgprIn), comment="x * x" ))
module.add(VFmaPKF16(dst=vgpr(Holder(idx=vgprTemp)), src0=vgpr(Holder(idx=vgprTemp)), src1=sgpr(Holder(idx=sgprMagicK1)), src2=1.0, \
vop3=VOP3PModifiers(op_sel_hi=[1,1,0,1]), comment="x^2 * k1 + 1"))
module.add(VMulPKF16(dst=vgpr(Holder(idx=vgprTemp)), src0=self.vgprPrefix(vgprIn), src1=vgpr(Holder(idx=vgprTemp)), comment="x * (x^2 * k1 + 1)"))
coef = floatUnion(u=ActivationMagicNumbers["FloatGeluK0"])
module.add(VMulPKF16(dst=vgpr(Holder(idx=vgprTemp)), src0=sgpr(Holder(idx=sgprPKLiteral)), src1=vgpr(Holder(idx=vgprTemp)), comment="k0 * x * (x^2 * k1 + 1)"))
module.add(self.getTanhModule(cDataType, Holder(idx=vgprTemp), Holder(idx=vgprTemp), "", ""))
module.add(VAddPKF16(dst=vgpr(Holder(idx=vgprTemp)), src0=1.0, src1=vgpr(Holder(idx=vgprTemp)), \
vop3=VOP3PModifiers(op_sel_hi=[0,1,1]), comment="1 + tanh(...)" ))
module.add(VMulPKF16(dst=vgpr(Holder(idx=vgprTemp)), src0=self.vgprPrefix(vgprIn), src1=vgpr(Holder(idx=vgprTemp)), comment="x * (1 + tanh(...))"))
if activationAlpha == None:
module.add(VMulPKF16(dst=self.vgprPrefix(vgprOut), src0=0.5, src1=vgpr(Holder(idx=vgprTemp)), \
vop3=VOP3PModifiers(op_sel_hi=[0,1,1]), comment="0.5 * x * (1 + tanh(...))"))
else:
module.add(VMulPKF16(dst=vgpr(Holder(idx=vgprTemp)), src0=0.5, src1=vgpr(Holder(idx=vgprTemp)), \
vop3=VOP3PModifiers(op_sel_hi=[0,1,1]), comment="0.5 * x * (1 + tanh(...))"))
module.add(VMulPKF16(dst=self.vgprPrefix(vgprOut), src0=sgpr(activationAlpha), src1=vgpr(Holder(idx=vgprTemp)), \
vop3=VOP3PModifiers(op_sel_hi=[0,1,1]), comment="0.5 * x * (1 + tanh(...)) * scale"))
else:
module.add(VMulF16(dst=vgpr(Holder(idx=vgprTemp)), src0=self.vgprPrefix(vgprIn), src1=self.vgprPrefix(vgprIn), comment="x * x" ))
module.add(VFmaF16(dst=vgpr(Holder(idx=vgprTemp)), src0=vgpr(Holder(idx=vgprTemp)), src1=sgpr(Holder(idx=sgprMagicK1)), src2=1.0, comment="x^2 * k1 + 1"))
module.add(VMulF16(dst=vgpr(Holder(idx=vgprTemp)), src0=self.vgprPrefix(vgprIn), src1=vgpr(Holder(idx=vgprTemp)), comment="x * (x^2 * k1 + 1)"))
coef = floatUnion(u=ActivationMagicNumbers["FloatGeluK0"])
module.add(VMulF16(dst=vgpr(Holder(idx=vgprTemp)), src0=sgpr(Holder(idx=sgprPKLiteral)), src1=vgpr(Holder(idx=vgprTemp)), comment="k0 * x * (x^2 * k1 + 1)"))
module.add(self.getTanhModule(cDataType, Holder(idx=vgprTemp), Holder(idx=vgprTemp), "", ""))
module.add(VAddF16(dst=vgpr(Holder(idx=vgprTemp)), src0=1.0, src1=vgpr(Holder(idx=vgprTemp)), comment="1 + tanh(...)" ))
module.add(VMulF16(dst=vgpr(Holder(idx=vgprTemp)), src0=self.vgprPrefix(vgprIn), src1=vgpr(Holder(idx=vgprTemp)), comment="x * (1 + tanh(...))"))
if activationAlpha == None:
module.add(VMulF16(dst=self.vgprPrefix(vgprOut), src0=0.5, src1=vgpr(Holder(idx=vgprTemp)), comment="0.5 * x * (1 + tanh(...))"))
else:
module.add(VMulF16(dst=vgpr(Holder(idx=vgprTemp)), src0=0.5, src1=vgpr(Holder(idx=vgprTemp)), comment="0.5 * x * (1 + tanh(...))"))
module.add(VMulF16(dst=self.vgprPrefix(vgprOut), src0=sgpr(activationAlpha), src1=vgpr(Holder(idx=vgprTemp)), comment="0.5 * x * (1 + tanh(...)) * scale"))
elif cDataType.isSingle():
vgprTemp = self.getVgpr(1)
flt16GeluK1Str = HexToStr(cDataType, self.usePK, ActivationMagicNumbers["FloatGeluK1"])
module.add(VMulF32(dst=vgpr(Holder(idx=vgprTemp)), src0=flt16GeluK1Str, src1=self.vgprPrefix(vgprIn), comment="k1 * x"))
module.add(VFmaF32(dst=vgpr(Holder(idx=vgprTemp)), src0=self.vgprPrefix(vgprIn), src1=vgpr(Holder(idx=vgprTemp)), src2=1.0, comment="1 + (k1 * x * x)"))
module.add(VMulF32(dst=vgpr(Holder(idx=vgprTemp)), src0=self.vgprPrefix(vgprIn), src1=vgpr(Holder(idx=vgprTemp)), comment="x * (1 + k1 * x * x)"))
coef = floatUnion(u=ActivationMagicNumbers["FloatGeluK0"])
module.add(VMulF32(dst=vgpr(Holder(idx=vgprTemp)), src0=coef.f, src1=vgpr(Holder(idx=vgprTemp)), comment="k0 * x * (x^2 * k1 + 1)"))
module.add(self.getTanhModule(cDataType, Holder(idx=vgprTemp), Holder(idx=vgprTemp), "", ""))
module.add(VAddF32(dst=vgpr(Holder(idx=vgprTemp)), src0=1.0, src1=vgpr(Holder(idx=vgprTemp)), comment="1 + tanh(...)" ))
module.add(VMulF32(dst=vgpr(Holder(idx=vgprTemp)), src0=self.vgprPrefix(vgprIn), src1=vgpr(Holder(idx=vgprTemp)), comment="x * (1 + tanh(...))"))
if activationAlpha == None:
module.add(VMulF32(dst=self.vgprPrefix(vgprOut), src0=0.5, src1=vgpr(Holder(idx=vgprTemp)), comment="0.5 * x * (1 + tanh(...))"))
else:
module.add(VMulF32(dst=vgpr(Holder(idx=vgprTemp)), src0=0.5, src1=vgpr(Holder(idx=vgprTemp)), comment="0.5 * x * (1 + tanh(...))"))
module.add(VMulF32(dst=self.vgprPrefix(vgprOut), src0=sgpr(activationAlpha), src1=vgpr(Holder(idx=vgprTemp)), comment="0.5 * x * (1 + tanh(...)) * scale"))
else:
raise RuntimeError("Unsupported data type %s."%cDataType.toDevice("HIP"))
return module
def getLeakyReluModule(self, cDataType, vgprIn, vgprOut, activationAlpha):
module = Module("LeakyRelu")
if cDataType.isHalf():
vgprTemp = self.getVgpr(1)
module.add(VMulPKF16(dst=vgpr(Holder(idx=vgprTemp)), src0=sgpr(activationAlpha), src1=self.vgprPrefix(vgprIn), comment="tmp = x * alpha"))
for i in range(0, 2):
select_bit = SelectBit.WORD_0 if i == 0 else SelectBit.WORD_1
module.add(VCmpGEF16(dst=VCC(), src0=self.vgprPrefix(vgprIn), src1=0.0, \
sdwa=SDWAModifiers(src0_sel=select_bit, src1_sel=SelectBit.WORD_0), \
comment="x > 0 ?"))
module.add(VCndMaskB32(dst=self.vgprPrefix(vgprOut), src0=vgpr(Holder(idx=vgprTemp)), src1=self.vgprPrefix(vgprIn), \
sdwa=SDWAModifiers(dst_sel=select_bit, dst_unused=UnusedBit.UNUSED_PRESERVE, \
src0_sel=select_bit, src1_sel=select_bit), \
comment="set x to tmp if < 0"))
elif cDataType.isSingle():
vgprTemp = self.getVgpr(1)
module.add(VMulF32(dst=vgpr(Holder(idx=vgprTemp)), src0=sgpr(activationAlpha), src1=self.vgprPrefix(vgprIn), comment="tmp = x * alpha"))
module.add(VCmpGEF32(dst=VCC(), src0=self.vgprPrefix(vgprIn), src1=0.0, comment="x >= 0 ?"))
module.add(VCndMaskB32(dst=self.vgprPrefix(vgprOut), src0=vgpr(Holder(idx=vgprTemp)), src1=self.vgprPrefix(vgprIn), comment="set x to tmp if < 0"))
elif cDataType.isDouble():
vgprTemp = self.getVgpr(2)
module.add(VMulF64(dst=vgpr(Holder(idx=vgprTemp), 2), src0=sgpr(activationAlpha, 2), src1=self.vgprPrefix(vgprIn, 2), comment="tmp = x * alpha"))
module.add(VCmpGEF64(dst=VCC(), src0=self.vgprPrefix(vgprIn, 2), src1=0.0, comment="x >= 0 ?"))
module.add(VCndMaskB32(dst=self.vgprPrefix(vgprOut), src0=vgpr(Holder(idx=vgprTemp)), src1=self.vgprPrefix(vgprIn), comment="set x to tmp if < 0"))
module.add(VCndMaskB32(dst=self.vgprPrefix(vgprOut+1), src0=vgpr(Holder(idx=vgprTemp+1)), src1=self.vgprPrefix(vgprIn+1), comment="set x to tmp if < 0"))
elif cDataType.isInt32():
vgprTemp = self.getVgpr(1)
module.add(VMulLOU32(dst=vgpr(Holder(idx=vgprTemp)), src0=sgpr(activationAlpha), src1=self.vgprPrefix(vgprIn), comment="tmp = x * alpha"))
module.add(VCmpGEI32(dst=VCC(), src0=self.vgprPrefix(vgprIn), src1=0, comment="x >= 0 ?"))
module.add(VCndMaskB32(dst=self.vgprPrefix(vgprOut), src0=vgpr(Holder(idx=vgprTemp)), src1=self.vgprPrefix(vgprIn), comment="set x to tmp if < 0"))
else:
raise RuntimeError("Unsupported data type %s."%cDataType.toDevice("HIP"))
return module
def getReluModule(self, cDataType, vgprIn, vgprOut):
module = Module("LeakyRelu")
if cDataType.isHalf():
module.add(VMaxPKF16(dst=self.vgprPrefix(vgprOut), src0=self.vgprPrefix(vgprIn), src1=0, comment="x = max(0, x)" ))
elif cDataType.isSingle():
module.add(VMaxF32(dst=self.vgprPrefix(vgprOut), src0=self.vgprPrefix(vgprIn), src1=0, comment="x = max(0, x)" ))
elif cDataType.isDouble():
module.add(VMaxF64(dst=self.vgprPrefix(vgprOut, 2), src0=self.vgprPrefix(vgprIn, 2), src1=0, comment="x = max(0, x)" ))
elif cDataType.isInt32():
if self.saturateI8:
vgprTemp = self.getVgpr(1)
module.add(VMovB32(dst=vgpr(Holder(idx=vgprTemp)), src=hex(127), comment="value = 127"))
module.add(VMed3I32(dst=self.vgprPrefix(vgprOut), src0=self.vgprPrefix(vgprIn), src1=0, src2=vgpr(Holder(idx=vgprTemp)), comment="x = min(127, max(0, x))" ))
else:
module.add(VMaxI32(dst=self.vgprPrefix(vgprOut), src0=self.vgprPrefix(vgprIn), src1=0, comment="x = max(0, x)" ))
else:
raise RuntimeError("Unsupported data type %s."%cDataType.toDevice("HIP"))
return module
def getSigmoidModule(self, cDataType, vgprIn, vgprOut):
ti = rocIsa.getInstance()
self.needCombine = True
module = Module("Sigmoid")
if cDataType.isHalf():
if self.usePK:
module.add(VMulPKF16(dst=self.vgprPrefix(vgprOut), src0=-1.0, src1=self.vgprPrefix(vgprIn), comment=" x = -x"))
module.add(self.getExpModule(cDataType, vgprOut, vgprOut))
module.add(VAddPKF16(dst=self.vgprPrefix(vgprOut), src0=1.0, src1=self.vgprPrefix(vgprOut), \
vop3=VOP3PModifiers(op_sel_hi=[0,1,1]), comment="1 + exp(-x)"))
for i in range(0, 2):
select_bit = SelectBit.WORD_0 if i == 0 else SelectBit.WORD_1
module.add(VRcpF16(dst=self.vgprPrefix(vgprOut), src=self.vgprPrefix(vgprOut), \
sdwa=SDWAModifiers(dst_sel=select_bit, dst_unused=UnusedBit.UNUSED_PRESERVE, src0_sel=select_bit), \
comment="1 / (1 + exp(-x))"))
if ti.getArchCaps()["TransOpWait"]:
module.add(SNop(waitState=0, comment="1 wait states"))
else:
module.add(VMulF16(dst=self.vgprPrefix(vgprOut), src0=-1.0, src1=self.vgprPrefix(vgprIn), comment=" x = -x"))
module.add(self.getExpModule(cDataType, vgprOut, vgprOut))
module.add(VAddF16(dst=self.vgprPrefix(vgprOut), src0=1.0, src1=self.vgprPrefix(vgprOut), comment="1 + exp(-x)"))
module.add(VRcpF16(dst=self.vgprPrefix(vgprOut), src=self.vgprPrefix(vgprOut), comment="1 / (1 + exp(-x))"))
if ti.getArchCaps()["TransOpWait"]:
module.add(SNop(waitState=0, comment="1 wait states"))
elif cDataType.isSingle():
module.add(VMulF32(dst=self.vgprPrefix(vgprOut), src0=-1.0, src1=self.vgprPrefix(vgprIn), comment=" x = -x"))
module.add(self.getExpModule(cDataType, vgprOut, vgprOut))
module.add(VAddF32(dst=self.vgprPrefix(vgprOut), src0=1.0, src1=self.vgprPrefix(vgprOut), comment="1 + exp(-x)" ))
module.add(VRcpF32(dst=self.vgprPrefix(vgprOut), src=self.vgprPrefix(vgprOut), comment="1 / (1 + exp(-x))" ))
if ti.getArchCaps()["TransOpWait"]:
module.add(SNop(waitState=0, comment="1 wait states"))
else:
raise RuntimeError("Unsupported data type %s."%cDataType.toDevice("HIP"))
return module
def getTanhModule(self, cDataType, vgprIn, vgprOut, activationAlpha, activationBeta):
ti = rocIsa.getInstance()
self.needCombine = True
module = Module("Tanh")
if cDataType.isHalf():
# We don't need s_pack_ll_b32_b16 cause the input is already duplicated
if self.usePK:
if activationAlpha:
module.add(VMulPKF16(dst=self.vgprPrefix(vgprOut), src0=sgpr(activationAlpha), src1=self.vgprPrefix(vgprIn), comment="x * alpha"))
module.add(VMulPKF16(dst=self.vgprPrefix(vgprOut), src0=2, src1=self.vgprPrefix(vgprOut), comment=" x = 2 * x"))
else:
module.add(VMulPKF16(dst=self.vgprPrefix(vgprOut), src0=2, src1=self.vgprPrefix(vgprIn), comment=" x = 2 * x"))
module.add(self.getExpModule(cDataType, vgprOut, vgprOut))
module.add(VAddPKF16(dst=self.vgprPrefix(vgprOut), src0=1.0, src1=self.vgprPrefix(vgprOut), \
vop3=VOP3PModifiers(op_sel_hi=[0,1,1]), comment="e^2x + 1"))
for i in range(0, 2):
select_bit = SelectBit.WORD_0 if i == 0 else SelectBit.WORD_1
vgprCtrl = "dst_sel:WORD_%d dst_unused:UNUSED_PRESERVE src0_sel:WORD_%d"%(i, i)
module.add(VRcpF16(dst=self.vgprPrefix(vgprOut), src=self.vgprPrefix(vgprOut), \
sdwa=SDWAModifiers(dst_sel=select_bit, dst_unused=UnusedBit.UNUSED_PRESERVE, \
src0_sel=select_bit), \
comment="1 / (1 + exp(-x))"))
if ti.getArchCaps()["TransOpWait"]:
module.add(SNop(waitState=0, comment="1 wait states")) #workaround for emulator
module.add(VFmaPKF16(dst=self.vgprPrefix(vgprOut), src0=-2.0, src1=self.vgprPrefix(vgprOut), src2=1.0, \
vop3=VOP3PModifiers(op_sel_hi=[0,1,0,1]), comment="tanh(x) = (1 / (e^2x + 1)) * (-2) + 1"))
if activationBeta:
module.add(VMulPKF16(dst=self.vgprPrefix(vgprOut), src0=sgpr(activationBeta), src1=self.vgprPrefix(vgprOut), comment="beta * tanh(x)"))
else:
if activationAlpha:
module.add(VMulF16(dst=self.vgprPrefix(vgprOut), src0=sgpr(activationAlpha), src1=self.vgprPrefix(vgprIn), comment="x * alpha"))
module.add(VMulF16(dst=self.vgprPrefix(vgprOut), src0=2, src1=self.vgprPrefix(vgprOut), comment=" x = 2 * x"))
else:
module.add(VMulF16(dst=self.vgprPrefix(vgprOut), src0=2, src1=self.vgprPrefix(vgprIn), comment=" x = 2 * x"))
module.add(self.getExpModule(cDataType, vgprOut, vgprOut))
module.add(VAddF16(dst=self.vgprPrefix(vgprOut), src0=1.0, src1=self.vgprPrefix(vgprOut), comment="e^2x + 1"))
module.add(VRcpF16(dst=self.vgprPrefix(vgprOut), src=self.vgprPrefix(vgprOut), comment="1 / (1 + exp(-x))"))
if ti.getArchCaps()["TransOpWait"]:
module.add(SNop(waitState=0, comment="1 wait states")) #workaround for emulator
module.add(VFmaF16(dst=self.vgprPrefix(vgprOut), src0=-2.0, src1=self.vgprPrefix(vgprOut), src2=1.0, comment="tanh(x) = (1 / (e^2x + 1)) * (-2) + 1"))
if activationBeta:
module.add(VMulF16(dst=self.vgprPrefix(vgprOut), src0=sgpr(activationBeta), src1=self.vgprPrefix(vgprOut), comment="beta * tanh(x)"))
elif cDataType.isSingle():
if activationAlpha:
module.add(VMulF32(dst=self.vgprPrefix(vgprOut), src0=sgpr(activationAlpha), src1=self.vgprPrefix(vgprIn), comment="x * alpha"))
module.add(VMulF32(dst=self.vgprPrefix(vgprOut), src0=2, src1=self.vgprPrefix(vgprOut), comment=" x = 2 * x"))
else:
module.add(VMulF32(dst=self.vgprPrefix(vgprOut), src0=2, src1=self.vgprPrefix(vgprIn), comment=" x = 2 * x"))
module.add(self.getExpModule(cDataType, vgprOut, vgprOut))
module.add(VAddF32(dst=self.vgprPrefix(vgprOut), src0=1.0, src1=self.vgprPrefix(vgprOut), comment="e^2x + 1"))
module.add(VRcpF32(dst=self.vgprPrefix(vgprOut), src=self.vgprPrefix(vgprOut), comment="1 / (e^2x + 1)"))
if ti.getArchCaps()["TransOpWait"]:
module.add(SNop(waitState=0, comment="1 wait states")) #workaround for emulator
module.add(VFmaF32(dst=self.vgprPrefix(vgprOut), src0=-2.0, src1=self.vgprPrefix(vgprOut), src2=1.0, comment="(-2) * (1 / (e^2x + 1)) + 1"))
if activationBeta:
module.add(VMulF32(dst=self.vgprPrefix(vgprOut), src0=sgpr(activationBeta), src1=self.vgprPrefix(vgprOut), comment="beta * tanh(x)"))
else:
raise RuntimeError("Unsupported data type %s."%cDataType.toDevice("HIP"))
return module
def getDGeluModule(self, cDataType, vgprIn, vgprOut):
ti = rocIsa.getInstance()
self.needCombine = True
module = Module("Gradient Gelu")
# x1 = (0.0535161 * pow(x, 3) + 0.398942 * x)
# xx = 0.0356774 * pow(x, 3)+ 0.797885 * x
# x2 = cosh-2(xx) = 4/pow(math.exp(-xx) + math.exp(xx),2)
# => 0.5 * math.tanh(xx) + x1 * x2 + 0.5
if cDataType.isSingle():
vgprTemp1 = self.getVgpr(1)
vgprTemp2 = self.getVgpr(1)
vgprTemp3 = self.getVgpr(1)
sgprTemp = self.getSgpr(1)
module.add(VMulF32(dst=vgpr(Holder(idx=vgprTemp1)), src0=self.vgprPrefix(vgprIn), src1=self.vgprPrefix(vgprIn), comment="tmp1 = pow(x * 2)"))
module.add(VMulF32(dst=vgpr(Holder(idx=vgprTemp1)), src0=vgpr(Holder(idx=vgprTemp1)), src1=self.vgprPrefix(vgprIn), comment="tmp1 = pow(x * 3)"))
coef = floatUnion(u=ActivationMagicNumbers["FloatDGeluK1"])
module.add(VMulF32(dst=vgpr(Holder(idx=vgprTemp2)), src0=hex(coef.u), src1=self.vgprPrefix(vgprIn), comment="tmp2 = 0.398942 * x"))
coef = floatUnion(u=ActivationMagicNumbers["FloatDGeluK0"])
module.add(SMovB32(dst=sgpr(Holder(idx=sgprTemp)), src=hex(coef.u), comment="move magic number to sgpr"))
module.add(VFmaF32(dst=vgpr(Holder(idx=vgprTemp2)), src0=sgpr(Holder(idx=sgprTemp)), src1=vgpr(Holder(idx=vgprTemp1)), src2=vgpr(Holder(idx=vgprTemp2)), comment="tmp2 = 0.0535161 * x^3 + tmp2"))
coef = floatUnion(u=ActivationMagicNumbers["FloatDGeluK3"])
module.add(VMulF32(dst=vgpr(Holder(idx=vgprTemp3)), src0=hex(coef.u), src1=self.vgprPrefix(vgprIn), comment="tmp3 = 0.797885 * x"))
coef = floatUnion(u=ActivationMagicNumbers["FloatDGeluK2"])
module.add(SMovB32(dst=sgpr(Holder(idx=sgprTemp)), src=hex(coef.u), comment="move magic number to sgpr"))
module.add(VFmaF32(dst=vgpr(Holder(idx=vgprTemp1)), src0=sgpr(Holder(idx=sgprTemp)), src1=vgpr(Holder(idx=vgprTemp1)), src2=vgpr(Holder(idx=vgprTemp3)), comment="tmp1 = 0.035677 * x^3 + tmp3"))
module.add(self.getExpModule(cDataType, Holder(idx=vgprTemp1), Holder(idx=vgprTemp3)))
if self.isAlt:
module.add(VMulF32(dst=vgpr(Holder(idx=vgprTemp1)), src0=-1.0, src1=vgpr(Holder(idx=vgprTemp1)), comment="tmp1 = -tmp1"))
module.add(self.getExpModule(cDataType, Holder(idx=vgprTemp1), Holder(idx=vgprTemp1)))
module.add(VAddF32(dst=self.vgprPrefix(vgprOut), src0=vgpr(Holder(idx=vgprTemp3)), src1=vgpr(Holder(idx=vgprTemp1)), comment="out = e^xx + e^-xx"))
module.add(VSubF32(dst=vgpr(Holder(idx=vgprTemp1)), src0=vgpr(Holder(idx=vgprTemp3)), src1=vgpr(Holder(idx=vgprTemp1)), comment="tmp1 = e^xx - e^-xx"))
module.add(VRcpF32(dst=vgpr(Holder(idx=vgprTemp3)), src=self.vgprPrefix(vgprOut), comment="tmp3 = 1/out"))
if ti.getArchCaps()["TransOpWait"]:
module.add(SNop(waitState=0, comment="1 wait states")) #workaround for emulator
module.add(VMulF32(dst=vgpr(Holder(idx=vgprTemp3)), src0=vgpr(Holder(idx=vgprTemp1)), src1=vgpr(Holder(idx=vgprTemp3)), comment="tmp3 = tmp1 * tmp3"))
if self.enableGuard:
module.add(SMovB32(dst=sgpr(Holder(idx=sgprTemp)), src=hex(0x200), comment="move magic number to sgpr"))
module.add(VCmpXClassF32(dst=EXEC(), src0=self.vgprPrefix(vgprOut), src1=sgpr(Holder(idx=sgprTemp)), comment="True if tmp1 = inf"))
module.add(VMovB32(dst=vgpr(Holder(idx=vgprTemp3)), src=hex(0x3f800000), comment="tmp3 = 1 if True"))
module.add(VCmpXLtF32(dst=EXEC(), src0=vgpr(Holder(idx=vgprTemp2)), src1=0, comment="check if x < 0" ))
module.add(VMovB32(dst=vgpr(Holder(idx=vgprTemp3)), src=hex(0xbf800000), comment="tmp3 = -1 if True"))
module.add(SSetMask(dst=EXEC(), src=-1, comment="reset mask" ))
module.add(VMulF32(dst=vgpr(Holder(idx=vgprTemp1)), src0=0.5, src1=vgpr(Holder(idx=vgprTemp3)), comment="tmp1 = 0.5 * tmp1"))
module.add(VMulF32(dst=self.vgprPrefix(vgprOut), src0=self.vgprPrefix(vgprOut), src1=self.vgprPrefix(vgprOut), comment="out = out * out"))
module.add(VRcpF32(dst=self.vgprPrefix(vgprOut), src=self.vgprPrefix(vgprOut), comment="out = 1/out"))
if ti.getArchCaps()["TransOpWait"]:
module.add(SNop(waitState=0, comment="1 wait states")) #workaround for emulator
else:
module.add(self.getTanhModule(cDataType, Holder(idx=vgprTemp1), vgprOut, "", ""))
module.add(VMulF32(dst=vgpr(Holder(idx=vgprTemp1)), src0=-1.0, src1=vgpr(Holder(idx=vgprTemp1)), comment="tmp1 = -tmp1"))
module.add(self.getExpModule(cDataType, Holder(idx=vgprTemp1), Holder(idx=vgprTemp1)))
module.add(VAddF32(dst=vgpr(Holder(idx=vgprTemp3)), src0=vgpr(Holder(idx=vgprTemp3)), src1=vgpr(Holder(idx=vgprTemp1)), comment="out = e^xx + e^-xx"))
module.add(VMulF32(dst=vgpr(Holder(idx=vgprTemp1)), src0=0.5, src1=self.vgprPrefix(vgprOut), comment="tmp1 = 0.5 * tmp1"))
module.add(VMulF32(dst=vgpr(Holder(idx=vgprTemp3)), src0=vgpr(Holder(idx=vgprTemp3)), src1=vgpr(Holder(idx=vgprTemp3)), comment="out = out * out"))
module.add(VRcpF32(dst=self.vgprPrefix(vgprOut), src=vgpr(Holder(idx=vgprTemp3)), comment="out = 1/out"))
if ti.getArchCaps()["TransOpWait"]:
module.add(SNop(waitState=0, comment="1 wait states")) #workaround for emulator
coef = floatUnion(f=4)
module.add(VMulF32(dst=self.vgprPrefix(vgprOut), src0=hex(coef.u), src1=self.vgprPrefix(vgprOut), comment="out = 4 * out"))
module.add(VFmaF32(dst=self.vgprPrefix(vgprOut), src0=self.vgprPrefix(vgprOut), src1=vgpr(Holder(idx=vgprTemp2)), src2=vgpr(Holder(idx=vgprTemp1)), comment="out = out * tmp2 + tmp1"))
module.add(VAddF32(dst=self.vgprPrefix(vgprOut), src0=0.5, src1=self.vgprPrefix(vgprOut), comment="out = out + 0.5"))
else:
raise RuntimeError("Unsupported data type %s."%cDataType.toDevice("HIP"))
return module
def getDReluModule(self, cDataType, vgprIn, vgprOut):
ti = rocIsa.getInstance()
self.needCombine = True
module = Module("Gradient Relu")
if cDataType.isSingle():
module.add(VCmpGTF32(dst=VCC(), src0=self.vgprPrefix(vgprIn), src1=0.0, comment=" VCC = (x > 0) ? 1 : 0" ))
module.add(VCndMaskB32(dst=self.vgprPrefix(vgprOut), src0=0.0, src1=1.0, src2=VCC(), comment=" y = VCC ? 1.0 : 0.0" ))
else:
raise RuntimeError("Unsupported data type %s."%cDataType.toDevice("HIP"))
return module
def getSiluModule(self, cDataType, vgprIn, vgprOut):
self.needCombine = True
module = Module("Silu")
vgprTemp = self.getVgpr(1)
module.addModuleAsFlatItems(self.getSigmoidModule(cDataType, vgprIn, Holder(idx=vgprTemp)))
if cDataType.isHalf():
if self.usePK:
mulFunction = VMulPKF16
else:
mulFunction = VMulF16
elif cDataType.isSingle():
mulFunction = VMulF32
else:
raise RuntimeError("Unsupported data type %s."%cDataType.toDevice("HIP"))
module.add(mulFunction(dst=self.vgprPrefix(vgprOut), src0=self.vgprPrefix(vgprIn), src1=self.vgprPrefix(Holder(idx=vgprTemp)), comment="x / (1 + exp(-x))"))
return module
def getSwishModule(self, cDataType, vgprIn, vgprOut, activationAlpha):
self.needCombine = True
module = Module("Swish")
if cDataType.isHalf():
if self.usePK:
mulFunction = VMulPKF16
else:
mulFunction = VMulF16
elif cDataType.isSingle():
mulFunction = VMulF32
else:
raise RuntimeError("Unsupported data type %s."%cDataType.toDevice("HIP"))
vgprTempIn = self.getVgpr(1)
vgprTempOut = self.getVgpr(1)
module.add(mulFunction(dst=self.vgprPrefix(Holder(idx=vgprTempIn)), src0=self.vgprPrefix(vgprIn), src1=sgpr(activationAlpha), comment="x * beta"))
module.addModuleAsFlatItems(self.getSigmoidModule(cDataType, Holder(idx=vgprTempIn), Holder(idx=vgprTempOut)))
module.add(mulFunction(dst=self.vgprPrefix(vgprOut), src0=self.vgprPrefix(vgprIn), src1=self.vgprPrefix(Holder(idx=vgprTempOut)), comment="x / (1 + exp(-x * beta))"))
return module
def getClampModule(self, cDataType, vgprIn, vgprOut, activationAlpha, activationBeta):
module = Module("Clamp")
if cDataType.isDouble():
Vin, Vout = self.vgprPrefix(vgprIn, 2), self.vgprPrefix(vgprOut, 2)
alpha, beta = sgpr(activationAlpha, 2), sgpr(activationBeta, 2)
else:
Vin, Vout = self.vgprPrefix(vgprIn), self.vgprPrefix(vgprOut)
alpha, beta = sgpr(activationAlpha), sgpr(activationBeta)
if cDataType.isHalf():
MIN, MAX = VMinF16, VMaxF16
elif cDataType.isSingle():
MIN, MAX = VMinF32, VMaxF32
elif cDataType.isDouble():
MIN, MAX = VMinF64, VMaxF64
elif cDataType.isInt32():
MIN, MAX = VMinI32, VMaxI32
if cDataType.isHalf():
for i in range(0, 2):
select_bit = SelectBit.WORD_0 if i == 0 else SelectBit.WORD_1
sdwa = SDWAModifiers(dst_sel = select_bit, dst_unused = UnusedBit.UNUSED_PRESERVE,
src0_sel = select_bit, src1_sel = select_bit)
module.add(MIN(dst = Vout, src0 = beta, src1 = Vin, sdwa = sdwa, comment = "min(x, beta)"))
module.add(MAX(dst = Vout, src0 = alpha, src1 = Vout, sdwa = sdwa, comment = "max(alpha, min(x, beta))"))
else:
module.add(MIN(dst = Vout, src0 = beta, src1 = Vin, comment = "min(x, beta)"))
module.add(MAX(dst = Vout, src0 = alpha, src1 = Vout, comment = "max(alpha, min(x, beta))"))
return module
################################################################################
################################################################################
###
### Cache Functions
###
################################################################################
################################################################################
def createCache(self, cDataType: DataType, activationType: str, vgprIn, vgprOut, module: Module):
typeChar = cDataType.toChar()
if activationType not in self.cacheDict:
self.cacheDict[activationType] = {}
actDict = self.cacheDict[activationType]
copied = deepcopy(module)
# Get reg name
regName = self.vgprPrefixFormat.split("+")[0] if self.vgprPrefixFormat else ""
vgprIdxList = createVgprIdxList(copied, [vgprIn, vgprOut], regName)
actInfo = actCacheInfo(usePK=self.usePK, saturateI8=self.saturateI8, enableGuard=self.enableGuard, \
isAlt=self.isAlt, prefix=self.vgprPrefixFormat, vgprIdxList=vgprIdxList, module=copied, \
vgprCounter=self.vgprCounter, sgprCounter=self.sgprCounter)
if typeChar in actDict:
actDict[typeChar].append(actInfo)
else:
actDict[typeChar] = [actInfo]
def getCache(self, cDataType: DataType, activationType: str, vgprIn, vgprOut) -> Union[Module, None]:
if activationType in self.cacheDict:
actDict = self.cacheDict[activationType]
typeChar = cDataType.toChar()
if typeChar in actDict:
actInfoList = actDict[typeChar]
for actInfo in actInfoList:
if actInfo.isSame(usePK=self.usePK, saturateI8=self.saturateI8, isAlt=self.isAlt, \
enableGuard=self.enableGuard, prefix=self.vgprPrefixFormat):
if self.vgprPrefixFormat:
for vgpr in actInfo.vgprIdxList[0]:
vgpr.regName.setOffset(0, vgprIn)
for vgpr in actInfo.vgprIdxList[1]:
vgpr.regName.setOffset(0, vgprOut)
else:
for vgpr in actInfo.vgprIdxList[0]:
vgpr.regIdx = vgprIn
for vgpr in actInfo.vgprIdxList[1]:
vgpr.regIdx = vgprOut
self.vgprCounter = actInfo.vgprCounter
self.sgprCounter = actInfo.sgprCounter
return deepcopy(actInfo.module)
return None
################################################################################
################################################################################
###
### Post Functions
###
################################################################################
################################################################################
__FUSE_MAGIC_NAME__ = "dbiw@I$HONIhnjf4_fused"
# Public
def CombineInstructions(module, fuseDebug = False):
moduleAndIndex = dict()
CombineInstructionsBetweenModules(module, moduleAndIndex, fuseDebug)
# Remove Empty Blocks
module = RemoveEmptyBlocks(module)
return module
# Does not support modules with branches
def CombineInstructionsBetweenModules(module, moduleAndIndex, fuseDebug):
index = 0
while index < len(module.items()):
item = module.items()[index]
if isinstance(item, Module):
CombineInstructionsBetweenModules(item, moduleAndIndex, fuseDebug)
index = module.items().index(item)
elif isinstance(item, SNop):
pass
elif isinstance(item, Instruction):
newItem = item
if moduleAndIndex:
newItem = FuseInstruction(item, moduleAndIndex, fuseDebug)
index = module.items().index(newItem)
if isinstance(newItem.dst, RegisterContainer):
# Update the dict
moduleAndIndex[newItem.dst] = newItem
index += 1
def RemoveEmptyBlocks(module):
for idx, item in enumerate(module.items()):
if isinstance(item, Module):
newItem = RemoveEmptyBlocks(item)
module.setItem(idx, newItem)
if len(module.items()) == 1 and isinstance(module.items()[0], Module):
return module.items()[0]
return module
def FuseInstruction(currentInst, moduleAndIndex, fuseDebug):
assert isinstance(currentInst, Instruction)
newInst = None
# Fuses if v_add_f16 to v_fma_f16 if v_add_f16 is a self adding instruction.
# Currently, we only fuse when the vgpr is add by 1 in both instructions.
# ex. v_add_f16 v0, 1.0, v0
# + v_fma_f16 v0, -2.0, v0, 1.0
# => v_fma_f16 v0, -2.0, v0, 2.0
if type(currentInst) in {VAddF16, VAddPKF16, VAddF32}:
isPK = type(currentInst) is VAddPKF16
outVgpr = currentInst.dst
addConst = ""
isSelfAddConst = False
for param in currentInst.srcs:
if param == outVgpr:
isSelfAddConst = True
if isinstance(param, (float, int)):
if param == 1:
addConst = param
if isSelfAddConst and addConst:
oldInst = moduleAndIndex.get(outVgpr)
if isinstance(oldInst, Instruction):
if currentInst.instType == InstType.INST_F16:
func = VFmaPKF16 if isPK else VFmaF16
elif currentInst.instType == InstType.INST_F32:
func = VFmaF32
else:
assert("You should not reach here.")
if type(oldInst) is func and oldInst.srcs[2] == 1.0:
# Cannot fuse if the target instruction has any rvalue reassigned or its lvalue
# used before the current instruction
if not FindAssignAndUse(oldInst, currentInst, outVgpr, outVgpr):
newInst = type(oldInst)(oldInst.dst, *oldInst.srcs, oldInst.sdwa)
newInst.setSrc(2, addConst + newInst.srcs[2])
newInst.comment += " ( + 1 (fused))"
replaceInst(currentInst, newInst, fuseDebug)
removeOldInst(oldInst, currentInst, newInst, fuseDebug)
# Fuses if v_mul_f16 to v_mul_f16 if the later one is a self multiplying instruction.
# Only fuses when both instructions multiply constant
elif type(currentInst) in {VMulF16, VMulPKF16, VMulF32, VMulF64}:
isPK = type(currentInst) is VMulPKF16
outVgpr = currentInst.dst
mulConst = ""
newFuseInst = ""
isSelfMulConst = False
for param in currentInst.srcs:
if param == outVgpr:
isSelfMulConst = True
# The constant may be an sgpr
if type(param) is RegisterContainer and param.regType == 's':
oldInst = moduleAndIndex.get(param)
if type(oldInst) is SMovB32:
oldparam = oldInst.srcs[0]
if oldInst.dst == param and isinstance(oldparam, (float, int)):
# Cannot fuse if another instruction is using the same sgpr before a new assignment occurs
if not FindUse(oldInst, currentInst, param):
mulConst = oldparam
newFuseInst = oldInst
if isinstance(param, (float, int)):
mulConst = param
if isSelfMulConst and mulConst:
oldInst = moduleAndIndex.get(outVgpr)
if isinstance(oldInst, Instruction):
if currentInst.instType == InstType.INST_F16:
func = VMulPKF16 if isPK else VMulF16
elif currentInst.instType == InstType.INST_F32:
func = VMulF32
elif currentInst.instType == InstType.INST_F64:
func = VMulF64
else:
assert("You should not reach here.")
if type(oldInst) is func:
# Cannot fuse if the target instruction has any rvalue reassigned or its lvalue
# used before the current instruction
if not FindAssignAndUse(oldInst, currentInst, outVgpr, outVgpr):
for paramIdx, param in enumerate(oldInst.srcs):
if isinstance(param, (float, int)):
newInst = type(oldInst)(oldInst.dst, *oldInst.srcs, oldInst.sdwa)
newValue = param * mulConst
formatting = " (fused %f)" if isinstance(param, float) else " (fused %d)"
if newFuseInst:
newFuseInst.setSrc(0, newValue)
newInst.setSrc(paramIdx, newFuseInst.dst)
newFuseInst.comment += formatting%newValue
else:
newInst.setSrc(paramIdx, newValue)
newInst.comment += formatting%newValue
replaceInst(currentInst, newInst, fuseDebug)
removeOldInst(oldInst, currentInst, newInst, fuseDebug)
break
return newInst if newInst else currentInst
# This only works for Activation.py
def FindUse(startInst, targetInst, varTarget):
_, isUse = FindUseIter(startInst, targetInst, varTarget)
return isUse
# This only works for Activation.py
def FindUseIter(startItem, targetInst, varTarget):
module = startItem
idx = -1
isEnd = False
isUse = False
if isinstance(startItem, Instruction):
module = startItem.parent
idx = module.items().index(startItem)
assert(isinstance(module, Module))
if idx + 1 < len(module.items()[idx + 1:]):
for item in module.items()[idx + 1:]:
if item is targetInst:
pass
elif isinstance(item, SNop):
pass
elif isinstance(item, Instruction):
if item.srcs:
for param in item.srcs:
if param == varTarget:
isEnd = True
isUse = True
break
elif item.dst == varTarget:
isEnd = True
isUse = False
elif isinstance(item, Module):
isEnd, isUse = FindUseIter(item, targetInst, varTarget)
if isEnd:
return isEnd, isUse
return False, isUse
# This only works for Activation.py
def FindAssignAndUse(startInst, endInst, assignVar, useVar):
_, isUse = FindAssignAndUseIter(startInst, endInst, assignVar, useVar)
return isUse
# This only works for Activation.py
def FindAssignAndUseIter(startItem, endInst, assignVar, useVar):
module = startItem
idx = -1
isEnd = False
isUse = False
if issubclass(type(startItem), Instruction):
module = startItem.parent
idx = module.items().index(startItem)
assert issubclass(type(module), Module)
moduleToIter = module.items()[idx + 1:]
if idx + 1 < len(moduleToIter):
for item in moduleToIter:
# Use
itemType = type(item)
if item is endInst:
isEnd = True
elif itemType is SNop:
pass
elif issubclass(itemType, Instruction):
if item.dst == assignVar:
isEnd = True
isUse = True
# Check use
if item.srcs:
for param in item.srcs:
if param == useVar:
isEnd = True
isUse = True
break
elif issubclass(itemType, Module):
isEnd, isUse = FindAssignAndUseIter(item, endInst, assignVar, useVar)
if isEnd:
return isEnd, isUse
return isEnd, isUse
def removeOldInst(removeInst, dstInst, fusedInst, debug):
module = removeInst.parent
targetIdx = -1
for idx, item in enumerate(module.items()):
if item == removeInst:
if debug:
tb = TextBlock("\n/* Fused to block %s + %s -> %s */\n"%(str(removeInst), str(dstInst), str(fusedInst)))
tb.name = __FUSE_MAGIC_NAME__
module.setItem(idx, tb)
else:
targetIdx = idx
break
if targetIdx > -1:
module.removeItemByIndex(targetIdx)
def replaceInst(srcInst, dstInst, debug):
module = srcInst.parent
targetIdx = -1
for idx, item in enumerate(module.items()):
if item == srcInst:
if debug:
dstInst.comment += " (Block replaced %s)"%(str(srcInst))
targetIdx = idx
break
if targetIdx > -1:
module.replaceItemByIndex(targetIdx, dstInst)
################################################################################
################################################################################
###
### Helper Functions
###
################################################################################
################################################################################
def getMagic(cDataType, value, isPack=False):
if cDataType.isDouble():
printExit("Currently magic does not support double.")
elif cDataType.isHalf():
fu = value if isinstance(value, floatUnion) else floatUnion(f=value)
magicNum = struct.unpack('<H', struct.pack('<e', fu.f))[0]
if isPack:
magicNum = ctypes.c_uint(magicNum).value
magicNum = ((magicNum << 16) | magicNum)
elif cDataType.isSingle():
fu = value if isinstance(value, floatUnion) else floatUnion(f=value)
magicNum = fu.u
return hex(magicNum)
def getMagicStr(cDataType, value, isPack=False):
return str(getMagic(cDataType, value, isPack))
def HexToStr(cDataType, isPack, *args):
if len(args) == 1:
magicNum = args[0]
uint32 = ctypes.c_uint(magicNum).value
if isPack and cDataType.isHalf():
uint32 = ((uint32 << 16) | uint32)
hexStr = str(hex(uint32))
else:
raise RuntimeError("Currently does not support multiple args.")
return hexStr
def ConvertCoeffToHex(module, cDataType, isPack):
if (module.name == "Exp"):
param = module.getItem(0).srcs[0]
module.getItem(0).setSrc(0, getMagic(cDataType, param, isPack))
return module
for itemIdx, item in enumerate(module.items()):
if isinstance(item, Module):
newItem = ConvertCoeffToHex(item, cDataType, isPack)
module.setItem(itemIdx, newItem)
return module
def HolderToGpr(module, idx, pf):
for itemIdx, item in enumerate(module.items()):
if isinstance(item, Module):
newItem = HolderToGpr(item, idx, pf)
module.setItem(itemIdx, newItem)
elif isinstance(item, SNop):
pass
elif isinstance(item, Instruction):
if isinstance(item.dst, HolderContainer) and item.dst.regType == pf:
item.dst.setRegNum(idx)
item.dst = item.dst.getCopiedRC()
if item.srcs:
for itemIdx, param in enumerate(item.srcs):
if isinstance(param, HolderContainer) and param.regType == pf:
param.setRegNum(idx)
item.setSrc(itemIdx, param.getCopiedRC())
return module
def addSpace(alignStr, str):
totalLength = len(alignStr) + len(str)
return '{message: >{width}}'.format(message=str, width=totalLength)
class ActivationInline:
def __init__(self, dataType, enableGuard) -> None:
self.dataType = dataType
self.enableGuard = enableGuard
self.asmStr = "asm("
def replaceGpr(self, module):
for item in module.items():
if isinstance(item, SNop):
pass
elif isinstance(item, Instruction):
if isinstance(item.dst, RegisterContainer):
if not item.dst.regName:
item.dst.setInlineAsm(True)
if item.srcs:
for param in item.srcs:
if isinstance(param, RegisterContainer):
if not param.regName:
param.setInlineAsm(True)
elif isinstance(item, Module):
self.replaceGpr(item)
def getActivationAsmStr(self, activation, module, spaces):
module = activation.postProcess(self.dataType, module)
self.replaceGpr(module)
activation.assignGpr(module, 0, 0)
module.setInlineAsmPrintMode(True)
kStr = str(module)
newStr = ""
for instStr in kStr.split("\n"):
if instStr:
newStr += spaces + instStr + "\n"
return newStr
# Internal Function
def generateInlineAssemblyBody(self, spaces, activationType):
ptrStr = self.dataType.toDevice("HIP")
activation = ActivationModule()
activation.setGuard(self.enableGuard)
activation.setUsePK(False)
activation.resetGprCounter()
kStr = ""
padSpacesStr = ' ' * spaces
asm = padSpacesStr + self.asmStr
if (activationType == 'abs'):
if self.dataType.isHalf() or self.dataType.isBFloat16():
unionDataTypeStr = "_Float16" if self.dataType.isHalf() else "BFloat16"
unionName = "f16_union" if self.dataType.isHalf() else "bf16_union"
kStr += (padSpacesStr + "union {\n")
kStr += (padSpacesStr + " %s f;\n"%unionDataTypeStr)
kStr += (padSpacesStr + " short s;\n")
kStr += (padSpacesStr + "} %s;\n"%unionName)
kStr += (padSpacesStr + "%s.f = value;\n"%unionName)
kStr += (padSpacesStr + "%s.s = %s.s & 0x7fff;\n"%(unionName, unionName))
kStr += (padSpacesStr + "value = %s.f;\n"%unionName)
elif (self.dataType.isSingle() or self.dataType.isDouble() or self.dataType.isInt32()):
kStr += (padSpacesStr + "value = (value < decltype(value)(0)) ? -value : value;\n")
else:
raise RuntimeError("Unrecognized data type %s."%self.dataType)
elif (activationType == 'clippedrelu'):
if (self.dataType.isSingle() or self.dataType.isHalf() or self.dataType.isDouble()):
kStr += (padSpacesStr + "value = (value > alpha) ? std::min(value, beta) : std::min(decltype(beta)(0), beta);\n")
elif self.dataType.isInt32():
kStr += (padSpacesStr + "value = (value > alpha) ? std::min(value, beta) : std::min(0, beta);\n")
elif (activationType == 'exp'):
kStr += (asm + " // Exp\n")
module = activation.getExpModule(self.dataType, 0, 0)
kStr += self.getActivationAsmStr(activation, module, (len(asm) * " "))
kStr += addSpace(asm, ": \"+v\"(value) : \n")
kStr += self.getRequiredRegStr(asm, activation.vgprCounter, activation.sgprCounter)
elif (activationType == 'gelu'):
kStr += (asm + " // gelu\n")
module = activation.getGeluModule(self.dataType, 0, 0)
kStr += self.getActivationAsmStr(activation, module, (len(asm) * " "))
kStr += addSpace(asm, ": \"+v\"(value) : \n")
kStr += self.getRequiredRegStr(asm, activation.vgprCounter, activation.sgprCounter)
elif (activationType == 'geluscaling'):
kStr += (asm + " // geluscaling\n")
module = activation.getGeluModule(self.dataType, 0, 0, 1)
kStr += self.getActivationAsmStr(activation, module, (len(asm) * " "))
kStr += addSpace(asm, ": \"+v\"(value) : \"s\"(alpha)\n")
kStr += self.getRequiredRegStr(asm, activation.vgprCounter, activation.sgprCounter)
elif (activationType == 'leakyrelu'):
if (self.dataType.isSingle() or self.dataType.isHalf() or self.dataType.isDouble()):
kStr += (padSpacesStr + "value = (value >= 0.0) ? value : (value * alpha);\n")
elif self.dataType.isInt32():
kStr += (padSpacesStr + "value = (value >= 0) ? value : (value * alpha);\n")
else:
raise RuntimeError("Unsupported data type %s."%ptrStr)
elif (activationType == 'relu'):
if (self.dataType.isSingle() or self.dataType.isHalf() or self.dataType.isDouble()):
kStr += (padSpacesStr + "value = std::max(decltype(value)(0), value);\n")
elif self.dataType.isInt32():
kStr += (padSpacesStr + "value = std::max(0, value);\n")
else:
raise RuntimeError("Unsupported data type %s."%ptrStr)
elif (activationType == 'sigmoid'):
kStr += (asm + " // Sigmoid\n")
module = activation.getSigmoidModule(self.dataType, 0, 0)
kStr += self.getActivationAsmStr(activation, module, (len(asm) * " "))
kStr += addSpace(asm, ": \"+v\"(value) : \n")
kStr += self.getRequiredRegStr(asm, activation.vgprCounter, activation.sgprCounter)
elif (activationType == 'tanh'):
kStr += (asm + " // tanh\n")
module = activation.getTanhModule(self.dataType, 0, 0, 1, 2)
kStr += self.getActivationAsmStr(activation, module, (len(asm) * " "))
kStr += addSpace(asm, ": \"+v\"(value) : \"s\"(alpha), \"s\"(beta)\n")
kStr += self.getRequiredRegStr(asm, activation.vgprCounter, activation.sgprCounter)
elif (activationType == 'dgelu'):
kStr += (asm + " // dgelu\n")
module = activation.getDGeluModule(self.dataType, 0, 0)
kStr += self.getActivationAsmStr(activation, module, (len(asm) * " "))
kStr += addSpace(asm, ": \"+v\"(value) : \n")
needExec = True if self.enableGuard else False
kStr += self.getRequiredRegStr(asm, activation.vgprCounter, activation.sgprCounter, needExec=needExec)
elif (activationType == 'drelu'):
if (self.dataType.isSingle()):
kStr += (padSpacesStr + "value = (value > 0.0f) ? 1.0f : 0.0f;\n")
else:
raise RuntimeError("Unsupported data type %s."%ptrStr)
elif (activationType == 'silu'):
kStr += (asm + " // Silu\n")
module = activation.getSiluModule(self.dataType, 0, 0)
kStr += self.getActivationAsmStr(activation, module, (len(asm) * " "))
kStr += addSpace(asm, ": \"+v\"(value) : \n")
kStr += self.getRequiredRegStr(asm, activation.vgprCounter, activation.sgprCounter)
elif (activationType == 'swish'):
kStr += (asm + " // Swish\n")
module = activation.getSwishModule(self.dataType, 0, 0, 1)
kStr += self.getActivationAsmStr(activation, module, (len(asm) * " "))
kStr += addSpace(asm, ": \"+v\"(value) : \"s\"(alpha)\n")
kStr += self.getRequiredRegStr(asm, activation.vgprCounter, activation.sgprCounter)
elif (activationType == 'clamp'):
kStr += (padSpacesStr + "value = std::max(alpha, std::min(value, beta));\n") # clamp
else:
if (activationType != 'none'):
raise RuntimeError("Unrecognized type %s."%activationType)
return kStr
# Internal use. Automatically gets the required vgprs and sgprs for inline assembly
def getRequiredRegStr(self, spaceAlignStr, numOfVgpr, numOfSgpr, needExec=False):
requiredReg = []
for i in range(0, numOfVgpr):
requiredReg.append("\"v%d\""%i)
for i in range(0, numOfSgpr):
requiredReg.append("\"s%d\""%i)
requiredStr = ""
if (len(requiredReg) > 0):
requiredStr = requiredReg[0]
for i in range(1, len(requiredReg)):
requiredStr += ", %s"%requiredReg[i]
if needExec:
if requiredStr:
requiredStr += ", \"exec\""
else:
requiredStr += "\"exec\""
kStr = ""
kStr += addSpace(spaceAlignStr,":%s);\n"%requiredStr)
return kStr
def createVgprIdxList(module, vgprList: list, regName):
vlist = [[], []]
for item in module.items():
if isinstance(item, Module):
tmplist = createVgprIdxList(item, vgprList, regName)
vlist[0].extend(tmplist[0])
vlist[1].extend(tmplist[1])
elif isinstance(item, Instruction):
for param in item.getParams():
if isinstance(param, RegisterContainer):
for index, vgprIdx in enumerate(vgprList):
if param.regName and (param.regName.name == regName) and (param.regName.getOffset()[0] == vgprIdx):
vlist[index].append(param)
elif param.regIdx == vgprIdx:
vlist[index].append(param)
return vlist