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

5857 lines
298 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
# 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.
#
################################################################################
import collections
import math
import sys
from enum import Enum
from typing import List, Dict, Literal, Tuple
from Tensile.AsmStoreState import VectorDataTypes
from Tensile.Activation import ActivationType
from Tensile.Activation import ActivationType
from Tensile.AsmStoreState import VectorDataTypes
from Tensile.Common import assignParameterWithDefault, IsaInfo, \
print2, printExit, printWarning, \
roundUp, INDEX_CHARS, IsaVersion, SemanticVersion, \
roundUpToNearestMultiple, effectiveMatrixInstMN
from Tensile.Common.DataType import DataType
from Tensile.Common.TypeValidationErrors import ConfigTypeError
from Tensile.SolutionStructs.LdsPadding import get_fp4_mt_config, get_fp8_mt_config, get_mxs_mt_config, \
get_fp16_mt_config, get_fp32_mt_config
from Tensile.Common.GlobalParameters import defaultSolution, \
defaultInternalSupportParams
from Tensile.Common.ValidParameters import validParameters, \
_getExpectedTypes, \
_expectedParamTypes, \
_skipTypeCheck
from Tensile.SolutionStructs.Naming import getSolutionNameFull
from Tensile.SolutionStructs.Problem import ProblemType
from Tensile.SolutionStructs.segment_interleave import evaluate as segIntEval, aligned_budget_ok as segAlignedBudget
from Tensile.Toolchain.Component import Assembler
from Tensile.Components.CustomSchedule import hasCustomSchedule
from ..Component import TensorDataMover
from ..Components.TensorDataMover import TensorDataMoverLoad
from .Utilities import reject, roundupRatio, pvar
from .Validators.MXScaleFormat import validateMXScaleFormatCombination
def _deriveAndValidateMXScaleLayoutAndTransport(state, asmCaps, archCaps, printRejectionReason):
"""Resolve the ``MXLoadInst`` / ``MXScaleFormat`` / ``TDMInst`` triple.
Lifted out of ``Solution.assignDerivedParameters`` so it can be exercised
by unit tests without standing up an entire solution-derivation pipeline.
The function:
* Resolves the ``"Auto"`` sentinels on ``MXLoadInst`` / ``MXScaleFormat``
against the current ISA's caps and the problem's MX block presence.
* Promotes ``TDMInst`` to ``3`` (A + B) when ``MXLoadInst="TDM"`` is
paired with the default ``TDMInst=0``, honoring the "both-or-none"
TDMInst invariant.
* Enforces every reject the original PR added: ``GlobalLoad`` not yet
implemented, ``TDM`` requires ``HasTDM``, non-TDM transport with a
non-zero ``TDMInst`` is incompatible, any swizzled format requires
``HasMXScaleSwizzle``, the format/transport pairing rules, and the
gfx1250 "MX needs TDMInst unless StreamK" guard.
Args:
state: Solution state dict; mutated in place. Must contain
``MXLoadInst``, ``MXScaleFormat``, ``TDMInst``, ``ISA``,
``StreamK``, and ``ProblemType[MXBlockA|MXBlockB]``.
asmCaps: Mapping with at least ``"HasTDM"`` (bool).
archCaps: Mapping that may contain ``"HasMXScaleSwizzle"`` (bool).
printRejectionReason: Forwarded to :func:`reject`.
Returns:
``True`` if the state is valid (no reject fired); ``False`` if a
reject was emitted, in which case ``state["Valid"]`` is also ``False``
and the caller should propagate the early return upstream.
"""
hasMXBlock = bool(state["ProblemType"]["MXBlockA"] or state["ProblemType"]["MXBlockB"])
hasMxSwizCap = bool(archCaps.get("HasMXScaleSwizzle", False))
isGfx950 = state["ISA"] == (9, 5, 0)
# MXLoadInst default: TDM iff TDMInst != 0; otherwise BufferLoad.
if state["MXLoadInst"] == "Auto":
state["MXLoadInst"] = "TDM" if state["TDMInst"] != 0 else "BufferLoad"
# GlobalLoad is reserved for a future flat/global_load MX path.
if state["MXLoadInst"] == "GlobalLoad":
reject(state, printRejectionReason, "MXLoadInst=GlobalLoad not implemented yet")
return False
# Transport-vs-cap.
if state["MXLoadInst"] == "TDM" and not asmCaps["HasTDM"]:
reject(state, printRejectionReason, "MXLoadInst=TDM requires asmCaps.HasTDM")
return False
# TDMInst must agree with MXLoadInst so downstream enableTDMA/B is consistent.
# When MXLoadInst=TDM and TDMInst=0, promote TDMInst to 3 (A and B), which
# honors the "both-or-none" invariant on TDMInst. Disagreements are rejected.
if state["MXLoadInst"] == "TDM" and state["TDMInst"] == 0:
state["TDMInst"] = 3
elif state["MXLoadInst"] != "TDM" and state["TDMInst"] != 0:
reject(state, printRejectionReason,
"MXLoadInst=%s is incompatible with TDMInst=%d" % (state["MXLoadInst"], state["TDMInst"]))
return False
# MXScaleFormat default:
# MXLoadInst=TDM -> InMemorySwizzle
# MX on gfx950 + BufferLoad -> HostPreSwizzle (gfx950's only swizzling mode)
# otherwise -> NoSwizzle (canonical)
if state["MXScaleFormat"] == "Auto":
if state["MXLoadInst"] == "TDM":
state["MXScaleFormat"] = "InMemorySwizzle"
elif hasMXBlock and isGfx950:
state["MXScaleFormat"] = "HostPreSwizzle"
else:
state["MXScaleFormat"] = "NoSwizzle"
# Cap guard: any swizzled format requires HasMXScaleSwizzle.
if state["MXScaleFormat"] in ("InMemorySwizzle", "HostPreSwizzle") and not hasMxSwizCap:
reject(state, printRejectionReason,
"MXScaleFormat=%s requires archCaps.HasMXScaleSwizzle" % state["MXScaleFormat"])
return False
# Format-vs-transport. These compatibility rules apply only when MX scales
# are present; without MX scales MXScaleFormat is a don't-care.
if hasMXBlock:
if state["MXScaleFormat"] == "InMemorySwizzle" and state["MXLoadInst"] != "TDM":
reject(state, printRejectionReason,
"MXScaleFormat=InMemorySwizzle requires MXLoadInst=TDM "
"(in-memory MX scale swizzling is not supported with %s)" % state["MXLoadInst"])
return False
if state["MXScaleFormat"] == "HostPreSwizzle" and state["MXLoadInst"] != "BufferLoad":
reject(state, printRejectionReason,
"MXScaleFormat=HostPreSwizzle requires MXLoadInst=BufferLoad")
return False
if state["MXScaleFormat"] == "HostPreSwizzle" and not isGfx950:
reject(state, printRejectionReason,
"MXScaleFormat=HostPreSwizzle is only implemented on the gfx950 host pipeline")
return False
if state["MXLoadInst"] == "TDM" and state["MXScaleFormat"] != "InMemorySwizzle":
reject(state, printRejectionReason,
"MXLoadInst=TDM currently always produces MXScaleFormat=InMemorySwizzle "
"(got %s)" % state["MXScaleFormat"])
return False
# gfx1250 MX scales require a swizzled format (InMemorySwizzle via TDM).
# NoSwizzle is not supported on this arch.
if state["ISA"] == (12, 5, 0) and state["MXScaleFormat"] == "NoSwizzle":
reject(state, printRejectionReason,
"MXScaleFormat=NoSwizzle is not supported on gfx1250")
return False
return True
def _disableRuntimeStaggerU(state):
state["StaggerU"] = 0
state["StaggerUMapping"] = 0
state["StaggerUStride"] = 0
state["InternalSupportParams"]["SupportCustomStaggerU"] = False
def _disableUnsupportedRuntimeStaggerU(state):
# PAP+TDM StaggerU is not implemented (the staggered TDM descriptor isn't carried
# across the PAP persistent-tile handoff), so force runtime StaggerU off.
if state["PrefetchAcrossPersistent"] and state["TDMInst"] == 3:
_disableRuntimeStaggerU(state)
def _subtileGRKPartitionIsBuggy(loadRatioGR, localSubtileGrid):
# TODO: TEMPORARY FIX. Encodes the trigger for the subtile global-read
# cooperative-group + K-partition LDS bug (see fix_subtile_gr_missing_k_partition):
# when one buffer_load covers multiple consecutive M-subtiles (loadRatioGR > 1)
# and the per-wave M-subtile count (localSubtileGrid[0]) is not a multiple of
# loadRatioGR, the last partial M-group writes a "ghost" slot that overlaps the
# next K-partition's data, and the K>=1 representative subtile can be silently
# dropped. This only manifests when there is more than one K-partition
# (localSubtileGrid[1] > 1). Remove once the emit-side fix lands.
return (loadRatioGR > 1
and localSubtileGrid[1] > 1
and localSubtileGrid[0] % int(loadRatioGR) != 0)
def _validateSubtileGRKPartition(state, printRejectionReason):
# TODO: TEMPORARY FIX. Reject gfx950 subtile solutions that hit the GR
# K-partition bug (see _subtileGRKPartitionIsBuggy). Remove once
# fix_subtile_gr_missing_k_partition is merged.
if not state["UseSubtileImpl"]:
return True
if tuple(state["ISA"]) != (9, 5, 0):
return True
# Lazy import: Components/Subtile pulls the Components package and would
# deadlock at module-load time if imported from Solution.py's top level.
from Tensile.Components.Subtile.Kernel import selectABGeometry, TileInfo
for tc in ("A", "B"):
tileInfo = TileInfo(selectABGeometry(state, tc), tc, None, state)
loadRatioGR = tileInfo.loadRatioGR
localSubtileGrid = tileInfo.localSubtileGrid
if _subtileGRKPartitionIsBuggy(loadRatioGR, localSubtileGrid):
reject(state, printRejectionReason,
"UseSubtileImpl=1 hits the subtile GR K-partition bug on tensor %s: "
"loadRatioGR=%s with localSubtileGrid=%s (M-subtile count %d is not a "
"multiple of loadRatioGR and there is more than one K-partition)"
% (tc, loadRatioGR, localSubtileGrid, localSubtileGrid[0]))
return False
return True
def _validateStreamKForceDPOnly(state, printRejectionReason):
if state["StreamKForceDPOnly"]:
if state["StreamK"] != 3:
reject(state, printRejectionReason, "StreamKForceDPOnly requires DP-first two-tile Stream-K")
return False
if state["StreamKAtomic"] == 1:
reject(state, printRejectionReason, "StreamKForceDPOnly does not support atomic Stream-K")
return False
return True
# _getExpectedTypes / _expectedParamTypes / _skipTypeCheck were moved into
# Tensile/Common/ValidParameters.py to keep the registry and its derived
# type map co-located (and to keep the Common -> Solution import direction).
# They are re-imported above and re-exported here for the existing test
# module (Tensile/Tests/unit/test_validateParameterTypes.py) that imports
# them from Solution.
_cacheHintTensors = ("A", "B", "C", "D", "E", "MXSA", "MXSB", "WS", "Metadata")
_cacheHintLoadTensors = ("A", "B", "C", "E", "MXSA", "MXSB", "WS", "Metadata")
# Module-level collector that accumulates type mismatches across all Solution
# instances during a build. Key is (param_name, actual_type_name,
# expected_type_str); value is a dict with 'count', 'values' (set of unique
# repr(value)), and 'files' (set of source file paths).
_typeMismatchCollector = {}
def resetTypeMismatchCollector():
"""Clear the module-level type mismatch collector.
Call this before a new build pass or in test setUp to ensure a clean
slate. Safe to call even when the collector is already empty.
Uses dict.clear() so that any existing references to
``_typeMismatchCollector`` (e.g. in tests) see the same empty dict.
"""
_typeMismatchCollector.clear()
def getTypeMismatchCollector():
"""Return a copy of the current collector state.
Used by worker processes to return their collected mismatch data to the
main process after parallel YAML parsing.
"""
return {k: {"count": v["count"], "values": set(v["values"]), "files": set(v["files"])}
for k, v in _typeMismatchCollector.items()}
def mergeTypeMismatchCollector(data):
"""Merge mismatch data returned from a worker process into the main collector.
Args:
data: A dict in the same format as ``_typeMismatchCollector``, as
returned by ``getTypeMismatchCollector()`` in a worker process.
"""
for key, entry in data.items():
if key not in _typeMismatchCollector:
_typeMismatchCollector[key] = {"count": 0, "values": set(), "files": set()}
_typeMismatchCollector[key]["count"] += entry["count"]
_typeMismatchCollector[key]["values"] |= entry["values"]
_typeMismatchCollector[key]["files"] |= entry["files"]
def mergeMismatchRecords(records):
"""Fold a list of mismatch records into the module-level collector.
This is the single place that knows the collector's internal dict
shape. Each record is a ``(collectorKey, valueRepr, srcFile)`` tuple as
returned by :func:`validateParameterTypes`, where ``collectorKey`` is
``(param_name, actual_type_name, expected_type_str)``.
The records are an in-process return value; this merge populates the
same module-level ``_typeMismatchCollector`` that
``getTypeMismatchCollector`` snapshots across the joblib worker
boundary, so the cross-process transport is unchanged.
Args:
records: list of ``(collectorKey, valueRepr, srcFile)`` tuples.
"""
for collectorKey, valueRepr, srcFile in records:
if collectorKey not in _typeMismatchCollector:
_typeMismatchCollector[collectorKey] = {
"count": 0,
"values": set(),
"files": set(),
}
entry = _typeMismatchCollector[collectorKey]
entry["count"] += 1
entry["values"].add(valueRepr)
if srcFile:
entry["files"].add(srcFile)
def validateParameterTypes(state, srcFile=""):
"""Validate that every solution parameter has the correct Python type.
Compares each value in *state* against the types implied by
``validParameters``. A ``bool`` where ``int`` is expected (or vice
versa) is the most common error -- YAML ``false``/``true`` vs ``0``/``1``
are different Python types and produce different msgpack wire types,
which causes ``std::bad_cast`` at C++ deserialization time.
Instead of raising on the first mismatch, the function builds and
returns a list of mismatch records (one per offending key) without
touching module-level state. A record is a
``(collectorKey, valueRepr, srcFile)`` tuple, where ``collectorKey`` is
``(param_name, actual_type_name, expected_type_str)``. Returning ``[]``
means the state is clean. Callers fold the records into the warn-only
collector via :func:`mergeMismatchRecords`.
Args:
state: The solution state dict (parameter name -> value).
srcFile: The YAML source file path, included in warning messages.
Returns:
list: mismatch records, empty if every parameter is well-typed.
"""
records = []
for key, value in state.items():
if key not in _expectedParamTypes or key in _skipTypeCheck:
continue
expectedTypes = _expectedParamTypes[key]
actualType = type(value)
# Use type() not isinstance() so that bool and int are distinguished
if actualType not in expectedTypes:
expectedStr = " or ".join(sorted(t.__name__ for t in expectedTypes))
collectorKey = (key, actualType.__name__, expectedStr)
records.append((collectorKey, repr(value), srcFile))
return records
def raiseIfTypeMismatches() -> None:
"""Raise when collected type mismatches exist.
The library-logic loading path intentionally collects mismatches from every
parsed file before reporting. This checker preserves that aggregate behavior
but makes the final create-library gate fatal.
"""
if not _typeMismatchCollector:
return
totalCount = sum(e["count"] for e in _typeMismatchCollector.values())
allFiles = set()
for e in _typeMismatchCollector.values():
allFiles |= e["files"]
lines = []
lines.append("===========================================================")
lines.append(
f"ERROR: YAML parameter type mismatches detected "
f"({totalCount} total across {len(allFiles)} files):"
)
lines.append("===========================================================")
# Sort by parameter name for stable output
for (paramName, actualType, expectedStr), entry in sorted(
_typeMismatchCollector.items(), key=lambda kv: kv[0][0]
):
valuesStr = ", ".join(sorted(entry["values"]))
lines.append(
f" {paramName}: found {actualType} in {entry['count']} solutions "
f"(values: {valuesStr}) - expected {expectedStr}"
)
lines.append("-----------------------------------------------------------")
lines.append(" This will cause std::bad_cast at runtime because msgpack")
lines.append(" serializes bool and int as different wire types.")
lines.append(" Fix these type mismatches before building.")
lines.append("===========================================================")
raise ConfigTypeError("\n".join(lines))
class Fbs(Enum):
Free=0 # Expect to be free dimension
Batch=1 # Expect to be batch dimension
Sum=2 # Expect to be summation dimension
################################################################################
# Factor Type
################################################################################
class FactorDimArgs:
########################################
def __init__(self, problemType, config):
self.factorDims = []
self.totalProblemSizes = 0
if problemType["UseScaleAlphaVec"] or problemType["UseBias"]:
for fdim in config:
dim = int(fdim)
if dim not in [0, 1]:
printWarning("Factor Dim: must be 0 or 1, current is %s."%(dim))
self.factorDims.append(dim)
self.totalProblemSizes = len(self.factorDims)
def __str__(self):
s = "FactorDimArgs\n"
return s
class BiasTypeArgs:
########################################
def __init__(self, problemType, config):
self.biasTypes = []
self.totalProblemSizes = 0
if problemType["UseBias"]:
for btype in config:
datatype = DataType(btype)
if datatype not in problemType["BiasDataTypeList"]:
printWarning("Datatype: %s not support in this kernel (%s)"%(datatype, str(problemType["BiasDataTypeList"])))
self.biasTypes.append(datatype)
if not self.biasTypes:
printExit("Must provide a bias type in benchmark parameters if UseBias is set to True.")
self.totalProblemSizes = len(self.biasTypes)
def __str__(self):
s = "BiasTypesArgs\n"
return s
class GateTypeArgs:
########################################
def __init__(self, problemType, config):
self.gateTypes = []
self.totalProblemSizes = 0
if problemType["UseGateResidual"]:
for btype in config:
datatype = DataType(btype)
if datatype not in problemType["GateResidualDataTypeList"]:
printWarning("Datatype: %s not support in this kernel (%s)"%(datatype, str(problemType["GateResidualDataTypeList"])))
self.gateTypes.append(datatype)
if not self.gateTypes:
printExit("Must provide a gate type in benchmark parameters if UseGateResidual is set to True.")
self.totalProblemSizes = len(self.gateTypes)
def __str__(self):
s = "GateTypesArgs\n"
return s
################################################################################
# Activation
################################################################################
class activationSetting:
def __init__(self):
self.activationEnum = ""
class ActivationArgs:
########################################
def __init__(self, problemType, config):
self.settingList = []
self.totalProblemSizes = 0
if problemType["ActivationType"] == 'none':
return
if config:
for settings in config:
actSetting = activationSetting()
for dictionary in settings:
for sizeTypeKey in dictionary:
if sizeTypeKey == "Enum":
actSetting.activationEnum = ActivationType(dictionary[sizeTypeKey])
if problemType["ActivationType"] in ['all', 'hipblaslt_all']:
if (not actSetting.activationEnum):
printExit("Must provide an activation enum if Activation is set to True.")
else:
actSetting.activationEnum = problemType["ActivationType"]
self.settingList.append(actSetting)
self.totalProblemSizes += 1
if (problemType["ActivationType"] == 'all') and (not self.settingList):
printExit("Must provide an activation enum in benchmark parameters if Activation is set to True.")
def __str__(self):
s = "ActivationArgs\n"
return s
# kds is class Solution or class Kernel
# All free dims are packed
def isPackedIndex(ks, index):
problemType = ks["ProblemType"]
return index in problemType["IndicesFree"]
def isExtractableIndex(ks, index, tc='x'):
xA = index in ks['PackedC0IndicesX'][:-1]
xB = index in ks['PackedC1IndicesX'][:-1]
if tc=='A':
return xA
elif tc=='B':
return xB
else:
return xA or xB
################################################################################
# Solution
################################################################################
class Solution(collections.abc.Mapping):
MAX_NUM_DS_LOAD_VGPRS: int = 4
MAX_NUM_DS_LOAD_BYTES: int = 4 * MAX_NUM_DS_LOAD_VGPRS
######################################## # need to be sure PSRR is passing to all fxns
def __init__(
self,
config,
splitGSU: bool,
printSolutionRejectionReason: bool,
printIndexAssignmentInfo: bool,
assembler: Assembler,
isaInfoMap: Dict[IsaVersion, IsaInfo],
srcName: str = "",
*,
raiseProblemTypeOnTypeMismatch: bool = True,
):
"""Construct a Solution."""
self._name = None
self.assembler = assembler
self.isaInfoMap = isaInfoMap
self.srcName = srcName
self.splitGSU = splitGSU
config = config
targetIsas = list(isaInfoMap.keys())
self._state = {}
# problem type
if "ProblemType" in config:
self["ProblemType"] = ProblemType(
config["ProblemType"],
printIndexAssignmentInfo,
srcFile=srcName,
raiseOnTypeMismatch=raiseProblemTypeOnTypeMismatch,
)
else:
self["ProblemType"] = ProblemType.FromDefaultConfig(printIndexAssignmentInfo)
if "InternalSupportParams" in config:
self["InternalSupportParams"] = {}
for key in defaultInternalSupportParams:
assignParameterWithDefault(self["InternalSupportParams"], key, config["InternalSupportParams"], defaultInternalSupportParams)
else:
self["InternalSupportParams"] = defaultInternalSupportParams
# Assign solution state from config, filling missing from the defaultSolution
for key in defaultSolution:
assignParameterWithDefault(self._state, key, config, defaultSolution)
if 'ISA' not in self._state:
if 'ISA' in config:
# The ISA is expected to be defined when calling from TensileCreateLibrary
isa = config['ISA']
isa = IsaVersion(isa[0], isa[1], isa[2])
assert self.isaInfoMap[isa].asmCaps["SupportedISA"]
self._state['ISA'] = IsaVersion(isa[0], isa[1], isa[2])
else:
# When calling from Tensile, the ISA is typically not defined.
printWarning(f"ISA not set on config using {targetIsas[0]}.")
self._state['ISA'] = targetIsas[0]
if "CodeObjectVersion" not in self._state:
if "CodeObjectVersion" in config:
self._state["CodeObjectVersion"] = str(config["CodeObjectVersion"])
else:
self._state["CodeObjectVersion"] = self.assembler.code_object_version
# assign parameters without defaults
for key in config:
if (key != "ProblemType" or key != "InternalSupportParams") and key not in self._state:
self._state[key] = config[key]
self["Valid"] = True
# this could prevent OriginalSolution from re-assigning the parameters, save lots of time
if "AssignedProblemIndependentDerivedParameters" not in self._state:
self["AssignedProblemIndependentDerivedParameters"] = False
if "AssignedDerivedParameters" not in self._state:
self["AssignedDerivedParameters"] = False
# Validate parameter types against the validParameters registry.
# Catches bool-vs-int mismatches (YAML false vs 0) that would cause
# std::bad_cast at C++ msgpack deserialization time. The mismatch
# records are folded into the module-level warn-only collector here;
# the library-logic path snapshots that collector across the joblib
# worker boundary for the end-of-run summary. On the input-YAML path
# the state was already type-checked strictly upstream
# (checkParametersAreValid in BenchmarkStructs, ProblemType's
# raise-mode validator), so validateParameterTypes returns [] and
# nothing is merged -- no duplicate collector noise.
#
# Validate the complete pre-derived state. We still run derivation to preserve
# existing Solution construction behavior, but if this state is already bad we
# skip post-derived validation to avoid cascading/noisy type mismatch records.
pre_records = validateParameterTypes(self._state, srcFile=srcName)
mergeMismatchRecords(pre_records)
Solution.assignDerivedParameters(
self._state,
splitGSU,
printSolutionRejectionReason,
printIndexAssignmentInfo,
isaInfoMap,
assembler.rocm_version
)
self._name = config["CustomKernelName"] if "CustomKernelName" in config and config["CustomKernelName"] else None
# Only merge and report mismatches if there were no pre-existing mismatches
# To avoid duplicates and noise from cascading issues.
if not pre_records:
mergeMismatchRecords(validateParameterTypes(self._state, srcFile=srcName))
# these keys are copied from ProblemType to internal that may be overridden
InternalKeys = ["UseSgprForGRO","VectorStore"]
########################################
# get a list of kernel parameters for this solution
def getKernels(self):
kernel = self
kernel._state.update({"Kernel": True})
kernels = []
kernels.append(kernel)
return kernels
########################################
# get Helper Kernels
def getKernelBetaOlnyObjects(self):
return self.betaOnlyKernelObjects
########################################
# get Helper Kernels
def getKernelConversionObjects(self):
return self.conversionKernelObjects
@staticmethod
def getMIOutputInfo(state, isaInfoMap: Dict[str, IsaInfo]):
outputVectorWidth = 4
RegsPerOut = 1
isa = tuple(state["ISA"])
if isaInfoMap[isa].asmCaps['HasMFMA']:
if state["ProblemType"]["DataType"].MIOutputTypeNameAbbrev() == 'f64':
outputVectorWidth, RegsPerOut = 1, 2
else:
outputVectorWidth, RegsPerOut = 4, 1
elif isaInfoMap[isa].asmCaps['HasWMMA_V1']:
outputVectorWidth, RegsPerOut = 1, 1
elif isaInfoMap[isa].asmCaps['HasWMMA_V2'] or isaInfoMap[isa].asmCaps['HasWMMA_V3']:
outputVectorWidth, RegsPerOut = 8, 1
else:
print("WARNING: unexpect code flow")
return outputVectorWidth, RegsPerOut
########################################
# assign tile sizes
@staticmethod
def assignProblemIndependentDerivedParameters(state, printRejectionReason: bool, isaInfoMap: Dict[str, IsaInfo]):
if "AssignedProblemIndependentDerivedParameters" in state:
if state["AssignedProblemIndependentDerivedParameters"]:
return
state["AssignedProblemIndependentDerivedParameters"] = False
if "Valid" not in state:
state["Valid"] = True
# SIA=4 means "use StinkyTofu with full optimization". Remap to SIA=0 so all
# existing scheduling logic is unaffected, and record the opt level separately.
if state["ScheduleIterAlg"] == 4:
import rocisa
if not rocisa.hasStinkyTofuBackend():
reject(state, printRejectionReason,
"ScheduleIterAlg=4 requires the StinkyTofu backend, but rocisa was built without it")
return
if not rocisa.isSupportedByStinkyTofu(state["ISA"]):
supported = rocisa.getRegisteredArchKeys()
reject(state, printRejectionReason,
f"ScheduleIterAlg=4 is not supported for {state['ISA']}: no StinkyTofu backend for this architecture. "
f"Supported: {supported}")
return
state["_ScheduleIterAlg"] = 0
state["_StinkyTofuOptLevel"] = 3
else:
state["_ScheduleIterAlg"] = state["ScheduleIterAlg"]
state["_StinkyTofuOptLevel"] = 0
if (not state["ProblemType"]["StridedBatched"]) and (not state["ProblemType"]['Batched']):
reject(state, printRejectionReason, "General Batched GEMM only support Batched Problem")
if (not state["ProblemType"]["StridedBatched"]) and (state["ProblemType"]["OperationType"] != 'GEMM'):
reject(state, printRejectionReason, "General Batched GEMM only support GEMM OperationType")
### ---> This is where we previously called matrixInstructionToMIParameters
EnableMatrixInstruction = state["EnableMatrixInstruction"] if "EnableMatrixInstruction" in state else None
if EnableMatrixInstruction == None:
if ("MIBlock" in state and len(state["MIBlock"]) == 6) \
and ("MIWaveGroup" in state and len(state["MIWaveGroup"]) == 2) \
and ("MIWaveTile" in state and len(state["MIWaveTile"]) == 2):
EnableMatrixInstruction = True
elif ("WorkGroup" in state and len(state["WorkGroup"]) == 3) \
and ("ThreadTile" in state and len(state["ThreadTile"]) == 2) :
EnableMatrixInstruction = False
else:
reject(state, printRejectionReason, "EnableMatrixInstruction undetermined")
if EnableMatrixInstruction == True:
# MatrixInstM/N hold the *effective* per-instruction extents: SourceSwap on a
# non-square MI transposes the accumulator, so M/N swap. The *physical* opcode
# dims stay in MIBlock[0]/[1] (used for the WMMA opcode + hardware lane layout).
state["MatrixInstM"], state["MatrixInstN"] = \
effectiveMatrixInstMN(state["MIBlock"][0], state["MIBlock"][1], state.get("SourceSwap", False))
state["MatrixInstK"] = state["MIBlock"][2]
state["MatrixInstB"] = state["MIBlock"][3]
state["MatrixInstBM"] = state["MIBlock"][4]
state["MatrixInstBN"] = state["MIBlock"][5]
state["LocalSplitU"] = state["WorkGroup"][2]
state["NumWaveSplitK"] = 1
state["MIOutputVectorWidth"], state["MIRegPerOut"] = Solution.getMIOutputInfo(state, isaInfoMap)
if state["MIBlock"][0] == 4:
state["ThreadTile0"] = state["MIWaveTile"][0] * state["MIOutputVectorWidth"]
state["ThreadTile1"] = state["MIWaveTile"][1]
state["SubGroup0"] = state["MIWaveGroup"][0] * state["MatrixInstM"] * state["MatrixInstBM"] // state["MIOutputVectorWidth"]
state["SubGroup1"] = state["MIWaveGroup"][1] * state["MatrixInstN"] * state["MatrixInstBN"]
else:
state["ThreadTile0"] = state["MatrixInstBM"] * state["MIWaveTile"][0] * (state["MatrixInstM"] * state["MatrixInstN"] // state["WavefrontSize"])
state["ThreadTile1"] = state["MatrixInstBN"] * state["MIWaveTile"][1]
state["SubGroup0"] = state["MIWaveGroup"][0] * (state["WavefrontSize"] // state["MatrixInstN"])
state["SubGroup1"] = state["MIWaveGroup"][1] * state["MatrixInstN"]
#for the old Logic yaml file which does not contain keys: MIInputPerThreadA/B
if not "MIInputPerThreadA" in state:
state["MIInputPerThreadA"] = state["MIInputPerThread"]
state["MIInputPerThreadB"] = state["MIInputPerThread"]
# SS1 non-square: SourceSwap feeds B into wide src0 / A into narrow src1, so swap
# per-thread input counts (A sized for N-side, B for M-side) to match mfmaIter;
# else B has too few VGPRs for src0 -> "invalid operand for instruction".
if state.get("SourceSwap", False) and state["MatrixInstM"] != state["MatrixInstN"]:
state["MIInputPerThreadA"], state["MIInputPerThreadB"] = \
state["MIInputPerThreadB"], state["MIInputPerThreadA"]
elif EnableMatrixInstruction == False:
state["ThreadTile0"] = state["ThreadTile"][0]
state["ThreadTile1"] = state["ThreadTile"][1]
state["SubGroup0"] = state["WorkGroup"][0]
state["SubGroup1"] = state["WorkGroup"][1]
# choose between 2 split modes
state["LocalSplitU"] = 1 if state["WaveSplitK"] else state["WorkGroup"][2]
state["NumWaveSplitK"] = state["WorkGroup"][2] if state["WaveSplitK"] else 1
state["MIWaveGroup"] = [state["SubGroup0"] // state["WavefrontSize"], state["SubGroup1"]]
if "SubGroup0" in state and "SubGroup1" in state and "LocalSplitU" in state and "NumWaveSplitK" in state:
state["NumThreads"] = state["SubGroup0"] * state["SubGroup1"] * state["LocalSplitU"] * state["NumWaveSplitK"]
if (state["NumThreads"] % state['WavefrontSize']) != 0:
reject(state, printRejectionReason, f"size of WorkGroup {state['NumThreads']} should be multiple of WavefrontSize {state['WavefrontSize']}")
state["NumWaves"] = state["NumThreads"] // state['WavefrontSize']
# macro tile sizes
if "SubGroup0" in state and "ThreadTile0" in state:
state["MacroTile0"] = state["SubGroup0"]*state["ThreadTile0"]
if "SubGroup1" in state and "ThreadTile1" in state:
state["MacroTile1"] = state["SubGroup1"]*state["ThreadTile1"]
if "MacroTile" in state:
if state["MacroTile0"] != state["MacroTile"][0] \
or state["MacroTile1"] != state["MacroTile"][1]:
reject(state, printRejectionReason, "MacroTile mismatch")
# dot2: currently only support fp16 with HPA on gfx942 or fp16 &bf16 on gfx950
state["UseDotInstruction"] = (not state["EnableMatrixInstruction"]) \
and state["ProblemType"]["HighPrecisionAccumulate"] \
and ((state["ISA"] == IsaVersion(9,4,2) and state["ProblemType"]["DataType"].isHalf()) \
or (state["ISA"] == IsaVersion(9,5,0) and (state["ProblemType"]["DataType"].isBFloat16() or state["ProblemType"]["DataType"].isHalf())))
if state["UseDotInstruction"]:
# need modification for dot4 or dot8
state["NumDotElements"] = 2
if not state["UseDotInstruction"] and state["WaveSplitK"]:
reject(state, "WaveSplitK currently only support dot2 kernel.")
return
# workaround for MX
# set ASEM=minASEMforMX for not TLUA or not TLUB
# so far, kernel code can support 16, but host code cannot hanlde it
# TODO: enable 16 (or less)
minASEMforMX = 32
if (state["ProblemType"]["MXBlockA"] or state["ProblemType"]["MXBlockB"]) and \
((not state["ProblemType"]["TLUA"]) or (not state["ProblemType"]["TLUB"])):
if state["AssertSummationElementMultiple"] % minASEMforMX != 0:
state["AssertSummationElementMultiple"] = minASEMforMX
# tail loop optimization
state["tailLoopOptA"] = True
state["tailLoopOptB"] = True
if state["ProblemType"]["MXBlockA"]:
state["tailLoopOptMXSA"] = True
if state["ProblemType"]["MXBlockB"]:
state["tailLoopOptMXSB"] = True
# Use nonDTL loads in DTL tail loop
state["NonDTLTailLoopA"] = False
state["NonDTLTailLoopB"] = False
state["NonDTLTailLoopMetadata"] = False
if state["ProblemType"]["MXBlockA"]:
state["NonDTLTailLoopMXSA"] = False
if state["ProblemType"]["MXBlockB"]:
state["NonDTLTailLoopMXSB"] = False
# Initialize DTLA, DTLB for tailLoopOpt/NonDTLTailLoop and initial calcLdsBlockSizePerPad() call
state["DirectToLdsA"] = state["DirectToLds"] == 1 or state["DirectToLds"] == 2
state["DirectToLdsB"] = state["DirectToLds"] == 1 or state["DirectToLds"] == 3
bpeA = state["ProblemType"]["DataTypeA"].numBytes()
bpeB = state["ProblemType"]["DataTypeB"].numBytes()
aemA = state["AssertSummationElementMultiple"] if not state["ProblemType"]["TLUA"] else state["AssertFree0ElementMultiple"]
aemB = state["AssertSummationElementMultiple"] if not state["ProblemType"]["TLUB"] else state["AssertFree1ElementMultiple"]
# For DTL, we use nonDTL loads in tail loop only if
# a partial 32b read is required to read the last few elements of a row/col of A/B
# i.e. ASEM * BPE % 4 != 0. In this case dword/dwordx4 DTL load will
# zero out the entire partial 32b read and cause accuracy issues.
# TLU case, we check AF{0,1}EM instead of ASEM.
# TLU + ShiftPtr case, we can use wider global read for TailLoop. Enable Tailloop opt for DTL
# (ShiftPtr is default and not set to state["EdgeType"] yet here)
# MX case, force enable NonDTLTailLoopA/B
if ((aemA * bpeA) % 4 != 0 or not state["BufferLoad"]) or state["ProblemType"]["MXBlockA"]:
if (not state["ProblemType"]["TLUA"]) and state["DirectToLdsA"] and not state["DirectToVgprA"]:
state["NonDTLTailLoopA"] = True
state["tailLoopOptA"] = False
if state["ProblemType"]["MXBlockA"]:
state["NonDTLTailLoopMXSA"] = True
state["tailLoopOptMXSA"] = False
if ((aemB * bpeB) % 4 != 0 or not state["BufferLoad"]) or state["ProblemType"]["MXBlockB"]:
if (not state["ProblemType"]["TLUB"]) and state["DirectToLdsB"] and not state["DirectToVgprB"]:
state["NonDTLTailLoopB"] = True
state["tailLoopOptB"] = False
if state["ProblemType"]["MXBlockB"]:
state["NonDTLTailLoopMXSB"] = True
state["tailLoopOptMXSB"] = False
if state["ProblemType"]["Sparse"] and state["DirectToLdsMetadata"]:
state["NonDTLTailLoopMetadata"] = True
# tailLoopOpt (mode 3) uses wide unguarded loads that rely on buffer
# load SRD limits for K-edge protection. Flat loads lack an SRD, so
# force mode 2 (globalReadGuardK with per-element exec-mask checks).
if not state["BufferLoad"]:
state["tailLoopOptA"] = False
state["tailLoopOptB"] = False
if (state["ISA"] != (9, 4, 2) and state["ISA"] != (9, 5, 0)) or \
(state["ProblemType"]["Sparse"]) or \
(state["UseDotInstruction"]):
state["tailLoopOptA"] = False
state["tailLoopOptB"] = False
if state["ProblemType"]["MXBlockA"]:
state["tailLoopOptMXSA"] = False
if state["ProblemType"]["MXBlockB"]:
state["tailLoopOptMXSB"] = False
if (not state["ProblemType"]["TLUA"]) and (state["DirectToVgprA"]):
state["tailLoopOptA"] = False
if (not state["ProblemType"]["TLUB"]) and (state["DirectToVgprB"]):
state["tailLoopOptB"] = False
if state["ProblemType"]["MXBlockA"] and state["DirectToVgprMXSA"]:
state["tailLoopOptMXSA"] = False
if state["ProblemType"]["MXBlockB"] and state["DirectToVgprMXSB"]:
state["tailLoopOptMXSB"] = False
# so far, disable tailLoopOpt in MX case
# TODO: enable tailLoopOpt for MX
if state["ProblemType"]["MXBlockA"] or state["ProblemType"]["MXBlockB"]:
state["tailLoopOptA"] = False
state["tailLoopOptB"] = False
state["tailLoopOptMXSA"] = False
state["tailLoopOptMXSB"] = False
# reorder globalread instructions if dtv and TN cases. (along coalesced dim)
if state["_ScheduleIterAlg"] == 3:
state["reorderGRInstForDTVA"] = True if state["ProblemType"]["TransposeA"] and \
state["DirectToVgprA"] and \
not state["ProblemType"]["SwizzleTensorA"] else False
state["reorderGRInstForDTVB"] = True if not state["ProblemType"]["TransposeB"] and \
state["DirectToVgprB"] and \
not state["ProblemType"]["SwizzleTensorB"] else False
else:
state["reorderGRInstForDTVA"] = False
state["reorderGRInstForDTVB"] = False
# F32XEmulation related initialization
state["UseDot2F32XEmulation"] = False
state["UseMFMAF32XEmulation"] = False
state["UseDirect32XEmulation"] = False # directly local read into temporary vgpr
#ignore the F32 xDL MathOp by default.
#enable F32 xDL MathOp only when the input type is f32.
if state["UseF32XEmulation"]:
state["UseF32XEmulation"] = True
state["UseDirect32XEmulation"] = True
state["UseDirect32XEmulationInterleaveTreg"] = False # True: enable conventional T reg allocation
# select conversion logic for X3
# (1) UseMFMAF32XEmulation = True
# (2) UseDot2F32XEmulation = True (set (1) to False)
# (3) cvt + sub (set both (1) and (2) False)
isa = state["ISA"]
if isaInfoMap[isa].asmCaps.get("HasMFMA", False):
state["UseMFMAF32XEmulation"] = True # MFMA version for gfx950 etc.
state["MfmaInitCVgprs"] = False
# UseDualFMAC (VOPD v_dual_fmac_f32) applies only to plain f32 source/MAC (non-MFMA) kernels
# on archs whose assembler accepts the dual-issue form (gfx11/gfx12). The 2x2 block-diagonal
# pairing requires an even x even ThreadTile for full coverage. Auto-disable elsewhere so a
# config may enable it broadly without rejecting unrelated solutions.
# Exclude xf32: DataType=f32 but F32XdlMathOp selects xf32 rounding; v_dual_fmac_f32 is plain-f32 only.
_isXF32 = ("F32XdlMathOp" in state["ProblemType"]
and not state["ProblemType"]["F32XdlMathOp"].isSingle()
and state["ProblemType"]["DataType"].isSingle())
if state.get("UseDualFMAC", False) and (
state["KernelLanguage"] != "Assembly"
or EnableMatrixInstruction
or not state["ProblemType"]["DataType"].isSingle()
or _isXF32
or (state["ThreadTile0"] % 2) or (state["ThreadTile1"] % 2)
or not isaInfoMap[state["ISA"]].asmCaps.get("v_dual_fmac_f32", False)):
state["UseDualFMAC"] = False
# Enable UseSubtileImpl on gfx950 and gfx1250; ignore user request on other ISAs.
isa = tuple(state["ISA"])
isgfx950 = isa[:2] == (9, 5)
isgfx1250 = isa[:2] == (12, 5)
state["UseSubtileImpl"] = state["UseSubtileImpl"] and (isgfx950 or isgfx1250)
if isgfx950 and (state["ProblemType"]["MXBlockA"] or state["ProblemType"]["MXBlockB"]) and not state["UseSubtileImpl"]:
reject(state, printRejectionReason, "gfx950 MX requires UseSubtileImpl")
if state["UseSubtileImpl"]:
state["VectorWidthA"] = 1
state["VectorWidthB"] = 1
state["SourceSwap"] = False
# Force BufferStore=True: UseSubtileImpl optimized storeD path is only implemented
# for buffer stores for now.
state["BufferStore"] = True
# Not currently implemented in subtile implementation
state["Use64bShadowLimit"] = False
state["Use64bShadowLimitMX"] = False
# DepthU must be a multiple of numSubIterK * MIK * LSU, where numSubIterK is the
# number of K-subtiles per depth-U iteration: 1 for fp8 (AB_B8, subtileShape K=1),
# 2 for fp4/bf16 (AB_B4/AB_B16, subtileShape K=2).
dtype_a = state["ProblemType"]["DataTypeA"]
numSubIterK = 1 if dtype_a.is8bitFloat() else 2
duUnit = numSubIterK * state["MatrixInstK"] * state["LocalSplitU"]
if state["DepthU"] == -1:
state["DepthU"] = duUnit
if state["DepthU"] % duUnit != 0:
reject(state, printRejectionReason, f"UseSubtileImpl=1 support only DepthU multiple of {numSubIterK} * MatrixInstK * LocalSplitU")
for tc in ('A', 'B'):
dtype = state["ProblemType"][f"DataType{tc}"]
tlu = state["ProblemType"].get(f"TLU{tc}", False)
if tlu:
if dtype.isBFloat16() or dtype.isHalf():
state[f"_ABTilePair{tc}"] = "AB_B16_TLU1"
else:
reject(state, printRejectionReason, f"No TLU=1 subtile geometry for dtype {dtype}")
return
elif dtype.isBFloat16() or dtype.isHalf():
if state["WavefrontSize"] == 32:
state[f"_ABTilePair{tc}"] = "AB_B16_W32"
else:
state[f"_ABTilePair{tc}"] = "AB_B16"
elif dtype.is8bitFloat():
state[f"_ABTilePair{tc}"] = "AB_B8"
elif dtype.is6bitFloat() or dtype.isFloat4():
state[f"_ABTilePair{tc}"] = "AB_B4"
else:
reject(state, printRejectionReason, f"No subtile geometry for dtype {dtype}")
return
bytesLoaded = state["NumThreads"] * 16
if state["ProblemType"]["MXBlockA"]:
numBytesMXSA = (state["DepthU"] // state["ProblemType"]["MXBlockA"]) * state["MacroTile0"]
if bytesLoaded < numBytesMXSA:
reject(state, printRejectionReason, "Unable to load MXSA scales using one load per wave")
if state["ProblemType"]["MXBlockB"]:
numBytesMXSB = (state["DepthU"] // state["ProblemType"]["MXBlockB"]) * state["MacroTile1"]
if bytesLoaded < numBytesMXSB:
reject(state, printRejectionReason, "Unable to load MXSB scales using one load per wave")
for tc in ("MXSA", "MXSB"):
if state["ProblemType"]["MXBlock" + tc[-1]]:
for field in ("LSC", "LSP", "NumLoadsCoalesced", "NumLoadsPerpendicular", "NumTotalPackedLoads"):
state["%s%s" % (field, tc)] = 0
if state["PrefetchGlobalRead"] not in [0, 1, 2]:
reject(state, printRejectionReason,
"UseSubtileImpl=1 requires PrefetchGlobalRead 0, 1 or 2, got %d" % state["PrefetchGlobalRead"])
if not (state["MatrixInstM"] == 16 and state["MatrixInstN"] == 16):
reject(state, printRejectionReason, "UseSubtileImpl=1 requires MatrixInst 16x16")
if state["_ScheduleIterAlg"] == 1 or state["_ScheduleIterAlg"] == 2:
reject(state, printRejectionReason, "UseSubtileImpl=1 does not support ScheduleIterAlg")
if state["StreamK"] == 0:
if state["GlobalSplitU"] != 1:
reject(state, printRejectionReason, "UseSubtileImpl=1 with StreamK=0 requires GlobalSplitU=1 (no GSU reduction support)")
state["InternalSupportParams"]["SupportUserGSU"] = False
# Lazy import: Components/StreamK.py pulls ..Component which
# back-imports the Components package and would deadlock at
# module-load time if pulled from Solution.py's top-level
# imports.
from Tensile.Components.StreamK import streamKVariantClass
if state["StreamK"] != 0 and not streamKVariantClass(state["StreamK"]).supportsSubtileImpl:
reject(state, printRejectionReason, "UseSubtileImpl=1 requires StreamK in {0, 3, 4, 5}")
if state["DebugStreamK"] != 0:
reject(state, printRejectionReason, "UseSubtileImpl=1 does not support DebugStreamK (must be 0)")
if state["PrefetchAcrossPersistent"]:
if state["ISA"] != (9, 5, 0):
reject(state, printRejectionReason, "UseSubtileImpl=1 PrefetchAcrossPersistent is currently audited only for gfx950")
if state["PrefetchGlobalRead"] != 2:
reject(state, printRejectionReason, "UseSubtileImpl=1 PrefetchAcrossPersistent requires PrefetchGlobalRead=2")
if state["DirectToVgprMXSA"] or state["DirectToVgprMXSB"]:
reject(state, printRejectionReason, "UseSubtileImpl=1 PrefetchAcrossPersistent not supported with DirectToVgpr MX scale tensors")
state["Multicast"] = False
state["ClusterBarrier"] = False
# Multicast uses a mask fixed to the physical cluster position, but Stream-K remaps
# each WG's tile per iteration, so the broadcast would target the wrong partner.
# Keep the cluster WG-id decode (gated on ClusterDim) but leave multicast off for Stream-K.
if state["ClusterDim"] != [1, 1] and state["StreamK"] == 0:
state["Multicast"] = True
# ClusterBarrier emits SCmp/branch on sgpr("WaveIdx"), which is only allocated when TDM is enabled.
if state["TDMInst"] != 0 and isaInfoMap[state["ISA"]].asmCaps.get("HasClusterBarrier", False):
state["ClusterBarrier"] = True
# done
state["AssignedProblemIndependentDerivedParameters"] = True
########################################
# This is the "classic" algorithm which requires that each threads load the same number of bytes
# Called with tc=A and then with tc=B
# totalVectors is totalElements/GRVW, this is #vectors loaded by the LoadTile
# Reduces the GlobalReadVectorWidth if necessary if each thread has a small amount of work to do.
# Output from this function:
# state[GlobalReadVectorWidth*]
# state[NumLoads*] # only used in SolutionStructs, with classic alg
@staticmethod
def setGlobalReadVectorWidth(state, tc, totalVectors, grvw, printRejectionReason: bool):
validDepthU = True
# Skip GRVW range check for subtile impl: scale uses serial*loadWidth DTL addressing, not standard GRVW chunks
if grvw not in [1,2,4,8,16,32] and not state["UseSubtileImpl"]:
validDepthU = False
# TDM uses tensor_load_to_lds (not per-thread buffer_load), so the
# per-thread NumLoads* tile-split params are meaningless — normalize them
# to 1 so the later setGlobalLoadTileDimClassic nlc/nlp checks stay
# consistent. GRVW is kept as small as possible so small-size solution
# rejection still fires, but the global-read width (GRVW*bpe/bpr) must map
# to a real load instruction: the smallest is b8 = 0.25 dwords (1 byte),
# and the matcher requires width to be a whole multiple of it. That means
# GRVW*bpe must be a whole number of bytes. For sub-byte types GRVW=1 is
# too small (FP4: 0.5B -> 0.125 dw; FP6: 0.75B -> 0.1875 dw), so bump GRVW
# to the minimum that yields an integer byte count: FP4 -> 2 (1 byte),
# FP6 -> 4 (3 bytes = exactly 4 packed 6-bit elements).
usesTDM = tc in ("A", "B", "MXSA", "MXSB") and state.get("TDMInst", 0) == 3
if usesTDM:
tdmGrvw = 1
if tc in ("A", "B"):
if state["ProblemType"]["DataType%s"%tc].isFloat4():
tdmGrvw = 2
elif state["ProblemType"]["DataType%s"%tc].is6bitFloat():
tdmGrvw = 4
# The sparse tensor (Sparse==1 -> A, Sparse==2 -> B) loads 2:4 metadata
# that requires GRVW % 4 == 0, so it cannot be forced down to 1.
elif (state["ProblemType"]["Sparse"] == 1 and tc == "A") or (state["ProblemType"]["Sparse"] == 2 and tc == "B"):
tdmGrvw = 4
state["GlobalReadVectorWidth%s"%tc] = tdmGrvw
state["NumLoads%s"%tc] = 1
state["NumLoadsCoalesced%s"%tc] = 1
state["NumLoadsPerpendicular%s"%tc] = 1
return validDepthU
if totalVectors % state["NumThreads"] != 0:
reject(state, printRejectionReason, "totalVectors%s %u %% NumThreads %u != 0" \
% (tc, totalVectors, state["NumThreads"]))
validDepthU = False
state["GlobalReadVectorWidth%s"%tc] = grvw
# NumLoads is NOT used on the fractional path
# NumLoads is number of vector loads per-thread
state["NumLoads%s"%tc] = totalVectors // state["NumThreads"]
#print "result: ", pvar(state, "GlobalReadVectorWidth%s"%tc), \
# pvar(state, "NumLoads%s"%tc)
return validDepthU
########################################
# Sets the Global Read Tile dims (para, perp)
# This information controls which threads read which addresses from global mem)
# Output from this function:
# state[NumLoadsCoalescedA]
# state[NumLoadsPerpendicularA]
# state[LSCA]
# state[LSPA]
@staticmethod
def setGlobalLoadTileDimClassic(state, tc, numLoads, totalVectorsCoalesced, totalElementsPerp, depthU, printRejectionReason: bool):
if state["WaveSeparateGlobalRead%s"%tc]:
totalElementsPerp = roundupRatio(totalElementsPerp, state["NumThreads"] // state["WavefrontSize"])
if (tc == "A" or tc == "B") and state["enableGLTr%s"%tc]:
totalVectorsCoalesced, totalElementsPerp = totalElementsPerp, totalVectorsCoalesced
# nlc = 1
if state["NumLoadsCoalesced%s"%tc] == 1:
foundValid = False
nlcStart = 1
if (tc == "A" or tc == "B") and state["DirectToVgpr%s"%tc]:
# adjust nlc for DirectToVgprA/B
if state["ProblemType"]["TLU%s"%tc] and not state["enableGLTr%s"%tc]:
nlcStart = roundupRatio(state["MIWaveTile%s"%tc], state["GlobalReadVectorWidth%s"%tc])
else:
nlcStart = roundupRatio(depthU, state["MatrixInstK"] * state["GlobalReadVectorWidth%s"%tc] * state["LocalSplitU"] // state["MIInputPerThread"])
for nlc in range(nlcStart, int(state["NumLoads%s"%tc]+1)):
nlp = state["NumLoads%s"%tc] // nlc
if state["NumLoads%s"%tc] % nlc == 0 \
and totalVectorsCoalesced % nlc == 0 \
and totalElementsPerp % nlp == 0:
state["NumLoadsCoalesced%s"%tc] = nlc
state["NumLoadsPerpendicular%s"%tc] = nlp
# print("NumLoads%s:"%tc,state["NumLoads%s"%tc])
# print("NumLoadsCoalesced%s:"%tc,state["NumLoadsCoalesced%s"%tc])
# print("NumLoadsPerpendicular%s:"%tc,state["NumLoadsPerpendicular%s"%tc])
# print("\n")
foundValid = True
break
if not foundValid:
reject(state, printRejectionReason, "%s: No NumLoadsCoalesced=1 found"%tc)
return False
# nlc = -1
elif state["NumLoadsCoalesced%s"%tc] == -1:
foundValid = False
for nlc in range(state["NumLoads%s"%tc], 0, -1):
nlp = state["NumLoads%s"%tc] // nlc
if state["NumLoads%s"%tc] % nlc == 0 \
and totalVectorsCoalesced % nlc == 0 \
and totalElementsPerp % nlp == 0:
state["NumLoadsCoalesced%s"%tc] = nlc
state["NumLoadsPerpendicular%s"%tc] = nlp
foundValid = True
break
if not foundValid:
reject(state, printRejectionReason, "%s: No NumLoadsCoalesced=-1 found"%tc)
return False
# nlc = other
else:
if state["NumLoadsCoalesced%s"%tc] > state["NumLoads%s"%tc]:
reject(state, printRejectionReason, "%s nlc > numLoads"%tc)
return False
state["NumLoadsPerpendicular%s"%tc] = state["NumLoads%s"%tc] \
// state["NumLoadsCoalesced%s"%tc]
if state["NumLoads%s"%tc] % state["NumLoadsCoalesced%s"%tc] != 0:
reject(state, printRejectionReason, "%s: numLoads %u %% numLoadsCoalesced %u != 0" \
% (tc, state["NumLoads%s"%tc], state["NumLoadsCoalesced%s"%tc]))
return False
if totalVectorsCoalesced % state["NumLoadsCoalesced%s"%tc] != 0 :
reject(state, printRejectionReason, "%s: totalVectorsCoalesced %u %% numLoadsPara %u != 0" \
% (tc, totalVectorsCoalesced, state["NumLoadsCoalesced%s"%tc]))
return False
if totalElementsPerp % state["NumLoadsPerpendicular%s"%tc] != 0:
reject(state, printRejectionReason, "%s: totalElementsPerp %u %% numLoadsPerp %u != 0" \
% (tc, totalElementsPerp, state["NumLoadsPerpendicular%s"%tc]))
return False
if (tc == "A" or tc == "B") and state["enableGLTr%s"%tc]:
state["NumLoadsCoalesced%s"%tc], state["NumLoadsPerpendicular%s"%tc] = state["NumLoadsPerpendicular%s"%tc], state["NumLoadsCoalesced%s"%tc]
# Generalized nlc = 1 case
idxWG = 0 if tc == 'A' else 1
totalLoadsNeeded = (state["MacroTile%s"%tc] * depthU) // (state["GlobalReadVectorWidth%s"%tc] * state["WavefrontSize"])
MT = state["MacroTile%s"%tc]
numWaves = state["NumThreads"] // state["WavefrontSize"]
if state["UseGeneralizedNLCOne%s"%tc]:
if (totalLoadsNeeded % numWaves) != 0:
reject(state, printRejectionReason, "GNLC%s: totalLoadsNeeded (%u) %% numWaves (%u) != 0"%(tc, totalLoadsNeeded, numWaves))
state["NumTotalPackedLoads%s"%tc] = totalLoadsNeeded // numWaves
state["NumLoadsPerpendicular%s"%tc] = state["NumTotalPackedLoads%s"%tc]
state["NumLoadsCoalesced%s"%tc] = 1
else:
state["NumTotalPackedLoads%s"%tc] = -1
if state["ProblemType"]["TLU%s"%tc]:
state["LSC%s"%tc] = state["MacroTile%s"%tc] // state["NumLoadsCoalesced%s"%tc]
state["LSP%s"%tc] = int(math.ceil(float(depthU) / state["NumLoadsPerpendicular%s"%tc]))
else:
state["LSC%s"%tc] = int(math.ceil(float(depthU) / state["NumLoadsCoalesced%s"%tc]))
state["LSP%s"%tc] = state["MacroTile%s"%tc] // state["NumLoadsPerpendicular%s"%tc]
if state["WaveSeparateGlobalRead%s"%tc] == 1:
state["LSP%s"%tc] = roundupRatio(state["LSP%s"%tc], state["NumThreads"] // state["WavefrontSize"])
elif state["WaveSeparateGlobalRead%s"%tc] == 2:
state["LSP%s"%tc] = state["NumThreads"] // state["WavefrontSize"]
return True
##############################################
# check and calculate Wave Separate Global Read
@staticmethod
def checkAndAssignWaveSeparateGlobalRead(state, tc, printRejectionReason: bool):
# check can we use WaveSeparateGlobalRead
numOfWaves = state["NumThreads"] // state["WavefrontSize"]
if state["WaveSeparateGlobalRead%s"%tc]:
if state["ProblemType"]["TLU%s"%tc] and (state["_DepthU%s"%tc] > 0) and (state["_DepthU%s"%tc] % numOfWaves != 0):
reject(state, printRejectionReason, "didn't support WaveSeparateGlobalRead when DepthU is not multiple of wave %u in TLU%s" % (state["_DepthU%s"%tc], tc))
if not state["ProblemType"]["TLU%s"%tc] and (state["MacroTile%s" % tc] % numOfWaves != 0):
reject(state, printRejectionReason, "didn't support WaveSeparateGlobalRead when MacroTile is not multiple of wave %u in TLU%s" % (state["MacroTile%s"%tc], tc))
########################################
# determine can we use VgprForLocalReadPacking
@staticmethod
def isVgprForLocalReadPackingDoable(state, isaInfoMap: Dict[str, IsaInfo]):
isa = tuple(state["ISA"])
doable = True
# MatrixInstruction only
if not state["EnableMatrixInstruction"]:
doable = False
# only for HasEccHalf
if not isaInfoMap[isa].archCaps["HasEccHalf"]:
doable = False
# only for PLR>=1 (except for DTVA+B)
if state["PrefetchLocalRead"] < 1 and not (state["DirectToVgprA"] and state["DirectToVgprB"]):
doable = False
# only for 1 or 2 byte input (numRegister < 1)
if state["ProblemType"]["DataType"].numRegisters() >= 1:
doable = False
return doable
########################################
# determine if current datatype can support DirectToVgpr
@staticmethod
def isDirectToVgprSupportDataType(state):
return (state["ProblemType"]["DataType"].isSingle() or state["ProblemType"]["DataType"].isDouble() or state["ProblemType"]["DataType"].isComplex() or \
state["ProblemType"]["DataType"].isHalf() or state["ProblemType"]["DataType"].isBFloat16() or state["ProblemType"]["DataType"].isInt8()) or \
state["ProblemType"]["DataType"].is8bitFloat()
########################################
# determine can we use DirectToVgpr
@staticmethod
def isDirectToVgprDoable(state, tc, printRejectionReason: bool, isaInfoMap: Dict[str, IsaInfo]):
numBytes = state["ProblemType"]["MacDataType%s"%tc].numBytes()
numBytesGR = state["ProblemType"]["DataType%s"%tc].numBytes()
isSwizzle = state["ProblemType"]["SwizzleTensor%s"%tc]
# With MatrixInstruction only
if not state["EnableMatrixInstruction"] :
reject(state, printRejectionReason, "DirectToVgpr is for MatrixInstruction only")
return False
if state["LocalReadVectorWidth%s" % tc] < state["MIInputPerThread"]:
reject(state, "LocalReadVectorWidth < MIInputPerThread %d" % state["MIInputPerThread"])
return False
# disable the following combinations for initial implementation
# TODO: enable them (for pure DTV)
if (state["LocalSplitU"] != 1) and (not state["ProblemType"]["TLU%c"%tc]) and (not isSwizzle):
reject(state, printRejectionReason, "Non-Swizzled DirectToVgpr + LSU + TLU=False has not been enabled yet(tentative)")
return False
if state["DirectToVgprA"] and state["DirectToVgprB"]:
# change the following parameter values
state["PrefetchGlobalRead"] = 1
state["ExpandPointerSwap"] = False
state["1LDSBuffer"] = 0
state["PrefetchLocalRead"] = 0
# So far, DTVA + DTVB does not perform well (waitcnt is not ideal).
# Disable it for now (TODO: improve waitcnt and re-enable)
# Exception: allow if enableGLTrA or enableGLTrB are set; this still provides better performance than enabling only one side
if not (state["enableGLTrA"] or state["enableGLTrB"]):
reject(state, printRejectionReason, "DirectToVgprA + DirectToVgprB disabled")
return False
# DTV + input type conversion
if state["ProblemType"]["DataType%s"%tc] != state["ProblemType"]["MacDataType%s"%tc]:
if not state["ConvertAfterDS"]:
reject(state, printRejectionReason, "DirectToVgpr%s + input conversion + ConvertAfterDS=False not supported"%(tc))
return False
# check if the DataType can support DirectToVgpr
if not Solution.isDirectToVgprSupportDataType(state):
reject(state, printRejectionReason, "no DirectToVgpr support for this input data type")
return False
# Does not work with TLU = False and PrefetchLocalRead = 0
if (not state["ProblemType"]["TLU%c"%tc]) and state["PrefetchLocalRead"] == 0 and not (state["DirectToVgprA"] and state["DirectToVgprB"]):
reject(state, printRejectionReason, "DirectToVgpr%c does not supports TLU%c = False and PrefetchLocalRead = 0"%(tc, tc))
return False
# Does not work with TLU = False and CGEMM/DGEMM/DGEMM (not supported)
if (not state["ProblemType"]["TLU%c"%tc]) and (state["ProblemType"]["MacDataType%s"%tc].isDouble() or \
state["ProblemType"]["MacDataType%s"%tc].isComplex()):
reject(state, printRejectionReason, "DirectToVgpr%c does not supports TLU%c = False + S/C/D/ZGEMM"%(tc, tc))
return False
if numBytesGR * state["GlobalReadVectorWidth%c"%tc] < 4:
# no support for DTV + numBytesGR * GlobalReadVectorWidth< 4
reject(state, printRejectionReason, "DirectToVgpr%c does not support TLU%c + numByte * GlobalReadVectorWidth%c < 4"%(tc, tc, tc))
return False
if numBytes < 4:
# numBytes < 4 case
if state["ProblemType"]["TLU%c"%tc] and not state["enableGLTr%c"%tc]:
# use pack logic (with v_perm) same as local read (only if VgprForLocalReadPacking is doable)
if not Solution.isVgprForLocalReadPackingDoable(state, isaInfoMap):
reject(state, printRejectionReason, "Does not meet the requirement for DirectToVgpr%c + TLU%c + numByte < 4"%(tc, tc))
return False
# force ClusterLocalRead=1 for DTV + pack
state["ClusterLocalRead"] = 1
else:
# numBytes >= 4 case
if state["ProblemType"]["TLU%c"%tc] and state["MIInputPerThread"] > 1:
# no support for numBytes >= 4 + MIInputPerThread > 1
reject(state, printRejectionReason, "DirectToVgpr%c does not support TLU%c+ numByte >= 4 + MIInputPerThread > 1"%(tc, tc))
return False
# MatrixInstBM,BN check
# for A, MatrixInstBN should be 1
# for B, MatrixInstBM should be 1
# This is to limit the number of Vgpr
if tc == 'A' and not (state['MatrixInstBN'] == 1):
reject(state, printRejectionReason, "MatrixInstBN should be 1 for DirectToVgprA. Current value is %d"%(state['MatrixInstBN']))
return False
if tc == 'B' and not (state['MatrixInstBM'] == 1):
reject(state, printRejectionReason, "MatrixInstBM should be 1 for DirectToVgprB. Current value is %d"%(state['MatrixInstBM']))
return False
# Does not work with WaveSeparateGlobalRead
if state["WaveSeparateGlobalRead%c"%tc]:
reject(state, printRejectionReason, "DirectToVgpr%c does not supports WaveSeparateGlobalRead%c"%(tc, tc))
return False
# Does not work with TLU + VectorWidth != GlobalReadVectorWidth (VW = 2 + GRVW = 1 or VW = 1 + GRVW = 2 does not work)
if state["ProblemType"]["TLU%c"%tc] and (state["VectorWidth%s"%tc] != state["GlobalReadVectorWidth%c"%tc]) and (not state["enableGLTr%s"%tc]):
reject(state, printRejectionReason, "DirectToVgpr%c does not supports TLU + VectorWidth%s(=%u) != GlobalReadVectorWidth%c(%u)"%(tc, tc, state["VectorWidth%s"%tc], tc, state["GlobalReadVectorWidth%c"%tc]))
return False
# Does not work with TLU=False and NumLoadsCoalesced != DepthU//(MatrixInstK*GRVW*LSU//MIInputPerThread)
if (not state["ProblemType"]["TLU%c"%tc]) and \
state["NumLoadsCoalesced%c"%tc] != state["DepthU"] // (state["MatrixInstK"] * state["GlobalReadVectorWidth%c"%tc] * state["LocalSplitU"] // state["MIInputPerThread"]):
reject(state, printRejectionReason, "DirectToVgpr%c does not supports TLU=False and NumLoadsCoalesced%c != DepthU//(MatrixInstK*GlobalReadVectorWidth*LocalSplitU//MIInputPerThread(=%u))"%(tc, tc, state["MIInputPerThread"]))
return False
# Does not work with enableGLTr and GRVW != 8
if state["enableGLTr%c"%tc] and state["GlobalReadVectorWidth%c"%tc] != 8:
reject(state, printRejectionReason, "DirectToVgpr%c does not supports enableGLTr%c and GlobalReadVectorWidth != 8"%(tc, tc))
return False
# TLU=False or enableGLTr case, need GlobalReadVectorWidth == LocalReadVectorWidth
if ((not state["ProblemType"]["TLU%c"%tc]) or state["enableGLTr%c"%tc]) and \
state["GlobalReadVectorWidth%c"%tc] != state["LocalReadVectorWidth%s"%tc]:
reject(state, printRejectionReason, "DirectToVgpr%c does not supports TLU=False GlobalReadVectorWidth%c(%u) != LocalReadVectorWidth(%u)"%(tc, tc, state["GlobalReadVectorWidth%c"%tc], state["LocalReadVectorWidth%s"%tc]))
return False
# Does not work with SIA<3
if state["_ScheduleIterAlg"] < 3:
reject(state, printRejectionReason, "DirectToVgpr%c does not supports ScheduleIterAlg < 3"%(tc))
return False
# Does not work with InnerUnroll>1
if state["InnerUnroll"]>1:
reject(state, printRejectionReason, "DirectToVgpr%c does not supports InnerUnroll>1"%(tc))
return False
# Reject TLU = UnrollMajorLDS
if state["ProblemType"]["TLU%c"%tc] == state["UnrollMajorLDS%c"%tc]:
reject(state, printRejectionReason, "DirectToVgpr%c does not supports TLU%c = UnrollMajorLDS%c"%(tc, tc, tc))
return False
# does not work with UnrollLoopSwapGlobalReadOrder
if state["UnrollLoopSwapGlobalReadOrder"]:
reject(state, printRejectionReason, "DirectToVgpr%c does not supports UnrollLoopSwapGlobalReadOrder"%(tc))
return False
# does not work with PGR>=2 + EPS
if state["PrefetchGlobalRead"] >= 2 and state["ExpandPointerSwap"]:
# force EPS=0 and continue
state["ExpandPointerSwap"] = False
# does not work with Sparse
if state["ProblemType"]["Sparse"]:
reject(state, printRejectionReason, "DirectToVgpr%c does not supports Sparse"%(tc))
return False
# for DTVA/DTVB, does not work with PGR0
if state["PrefetchGlobalRead"] == 0:
reject(state, printRejectionReason, "DirectToVgpr%c does not supports PrefetchGlobalRead == 0."%(tc))
return False
# for DTVA, does not work with NN and TLDS0
if tc == 'A' and state["TransposeLDS"] == 0 and (not state["ProblemType"]["TransposeA"] and not state["ProblemType"]["TransposeB"]):
reject(state, printRejectionReason, "DirectToVgpr%c does not supports NN case with TransposeLDS == 0."%(tc))
return False
# for DTVA, does not work with TT and Tail-loop
if tc == 'A' and (state["ProblemType"]["TransposeA"] and state["ProblemType"]["TransposeB"]):
# Use AssertSummationElementMultiple (BoundSizeMultiple in predicates) to exclude failed tail-loop cases
state["AssertSummationElementMultiple"] = max(state["AssertSummationElementMultiple"], state["DepthU"])
# for DTVB, does not work with NN and Tail-loop
if tc == 'B' and (not state["ProblemType"]["TransposeA"] and not state["ProblemType"]["TransposeB"]):
# Use AssertSummationElementMultiple (BoundSizeMultiple in predicates) to exclude failed tail-loop cases
state["AssertSummationElementMultiple"] = max(state["AssertSummationElementMultiple"], state["DepthU"])
# Does not work with DirectToLDS
# -> this will be checked after DirectToLDS doable check is done
return True
########################################
# determine can we use DirectToLds
@staticmethod
def isDirectToLdsDoable(state, tc, isaInfoMap, printRejectionReason: bool):
isa = state["ISA"]
if state["UseSubtileImpl"]:
return True
if not isaInfoMap[isa].asmCaps["HasDirectToLds"]:
reject(state, printRejectionReason, "DirectToLds not supported on ISA %s" % (isa,))
return False
# x4 support for directToLds
canDTLx4 = isaInfoMap[isa].asmCaps["HasDirectToLdsx4"]
# numelements_perlane = 4/numBytes
# TN with transposeLDS feature should work as long as state["AssertSummationElementMultiple"] % (numelements_perlane*2) = 0
# state["AssertSummationElementMultiple"] % (numelements_perlane*4) = 0
#NT
# use only for f32 & DGEMM and TLU = 1
#TN
# use for all precisions with TransposeLDS=1
if tc in ["MXSA", "MXSB"]:
numBytesAB = 1
else:
numBytesAB = state["ProblemType"]["DataType%s"%tc].numBytes()
numBytesPerLoad = state["GlobalReadVectorWidth%s"%tc] * numBytesAB
# x2 DTL is not supported
if numBytesPerLoad == 8:
printWarning("can't use DirectToLds with b64 buffer load, using non DirectToLds version instead")
return False
if numBytesPerLoad == 16 and not canDTLx4:
reject(state, printRejectionReason, "b128 DirectToLds not supported")
return False
if numBytesPerLoad < 4:
reject(state, printRejectionReason, "DirectToLds not supported for loads less than 32bits")
return False
if tc in ["MXSA", "MXSB"]:
# MXSA/B case, check numBytesPerLoad only
return True
MT = state["MacroTile0"] if tc == 'A' else state["MacroTile1"]
if (MT & (MT-1)) != 0 and not state["UseGeneralizedNLCOne%s"%tc]: # Check of MT not power of 2
# so far, numBytesAB<=4 case, TLU=False only (continue with False)
if (numBytesAB <= 4) and state["ProblemType"]["TLU%c"%tc]:
return False
# so far MFMA only (TODO: enable non MFMA case)
if not state["EnableMatrixInstruction"]:
reject(state, printRejectionReason, "DirectToLds is for MatrixInstruction only for now (tentative)")
return False
# ToDo: Review def of lrvw and this check
# DTL + LocalReadVectorWidth > MIInputPerThread does not work
# Need support for TailLoop
if not state["ProblemType"]["Sparse"] and not(state["ProblemType"]["MacDataType%s"%tc].is8bitFloat() and (state["MatrixInstK"] == 64 or state["MatrixInstK"] == 128)):
if state["LocalReadVectorWidth%s"%tc] > state["MIInputPerThread"]:
reject(state, printRejectionReason, "DirectToLds does not work with LocalReadVectorWidth > MIInputPerThread")
return False
if state["NumThreads"] % state["WavefrontSize"] != 0:
reject(state, printRejectionReason, "can't use DirectToLds for NumThreads % WavefrontSize != 0")
return False
if state["ProblemType"]["TLU%c"%tc] == state["UnrollMajorLDS%c" % tc]:
printWarning("can't use DirectToLds for TLU%c == UnrollMajorLDS%c, using nonDirectToLds version instead"%(tc, tc))
return False
# avoid picking x2&x4 for precisions < f32/f64 in [ProblemType][TLU] == TRUE
if not state["EnableMatrixInstruction"]:
if state["GlobalReadVectorWidth%c"%tc] * numBytesAB * state["WavefrontSize"] > 256:
reject(state, printRejectionReason, "can't use DirectToLds for not EnableMatrixInstruction and GlobalReadVectorWidth%c * bpe%c * WavefrontSize > 256"%(tc,tc))
return False
if state["WaveSeparateGlobalRead%c" % tc]:
if state["LSC%c"%tc] * state["LSP%c"%tc] * numBytesAB != state["WavefrontSize"] * state["GlobalReadVectorWidth%c"%tc] * numBytesAB:
reject(state, printRejectionReason, "can't use DirectToLds for LSC%c and LSP%c * bpe!= WavefrontSize * GlobalReadVectorWidth%c * bpe%c > 4"%(tc, tc, tc, tc))
return False
if state["WaveSeparateGlobalRead%c" % tc] == 2:
reject(state, printRejectionReason, "can't use DirectToLds for WSGR%s = 2"%(tc))
return False
elif not state["UseGeneralizedNLCOne%s"%tc]:
if state["LSC%c"%tc] * state["LSP%c"%tc] * numBytesAB != state["NumThreads"] * state["GlobalReadVectorWidth%c"%tc] * numBytesAB:
reject(state, printRejectionReason, "can't use DirectToLds for LSC%c and LSP%c * bpe != NumThreads * GlobalReadVectorWidth%c * bpe%c > 4"%(tc, tc, tc, tc))
return False
# so far, DirectToLds does not work with LRVW=2
if state["LocalReadVectorWidth%s"%tc] == 2:
reject(state, printRejectionReason, "can't use DirectToLds for LocalReadVectorWidth == 2")
return False
# Does not work with (NumLoadsCoalesced>1 and UseInstOffsetForGRO) + DGEMM
if state["ProblemType"]["MacDataType%s"%tc].isDouble() and \
(state["NumLoadsCoalesced%c"%tc] > 1 and state["UseInstOffsetForGRO"]):
reject(state, printRejectionReason, "DirectToLds%c does not supports NumLoadsCoalesced%c > 1 and UseInstOffsetForGRO for dgemm"%(tc, tc))
return False
# Does not work with NumLoadsCoalesced>1 + ZGEMM
if state["ProblemType"]["MacDataType%s"%tc].isDoubleComplex() and state["NumLoadsCoalesced%c"%tc] > 1:
reject(state, printRejectionReason, "DirectToLds%c does not supports NumLoadsCoalesced%c > 1 for zgemm"%(tc, tc))
return False
# DirectToLds does not work with TLU=False and bpe > bpr and DepthU//NumLoadsCoalesced < 8
# bpe > bpr case, Lower and upper 4 bytes elements are stored separately.
# if TLU=False and DepthU//NumLoadsCoalesced is smaller than lower block size (8 elements),
# current offset swap logic does not work
if (not state["ProblemType"]["TLU%c"%tc]) and state["ProblemType"]["MacDataType%s"%tc].numRegisters() > 1 and \
state["_DepthU%s"%tc] // state["NumLoadsCoalesced%c"%tc] < 8:
reject(state, printRejectionReason, "DirectToLds%c does not work with TLU=False and bpe > bpr and DepthU//NumLoadsCoalesced%c < 8"%(tc, tc))
return False
# TODO: Currently DTL with input types of different size is not support. There are functional issues
# This needs to be fixed.
if state["ProblemType"]["DataType%s"%tc].numBytes() != state["ProblemType"]["MacDataType%s"%tc].numBytes():
reject(state, printRejectionReason, "DirectToLds%s with conversion to different sized data types is not supported"%tc)
return False
# DTL + input type conversion
if state["ProblemType"]["DataType%s"%tc] != state["ProblemType"]["MacDataType%s"%tc]:
if not state["ConvertAfterDS"]:
reject(state, printRejectionReason, "DirectToLds%s + input conversion + ConvertAfterDS=False not supported"%(tc))
return False
return True
@staticmethod
def getDivisorName(state, tC):
if state["GlobalReadCoalesceGroup{}".format(tC)]:
if state["GlobalReadCoalesceVector{}".format(tC)]:
divisorName = "LVC{}".format(tC)
else:
# Fractional load use the more accurate lsc, multiply by VW later
divisorName = "LSC{}".format(tC)
else:
if state["GlobalReadCoalesceVector{}".format(tC)]:
divisorName = "LSP{}".format(tC)
else:
divisorName = "LVP{}".format(tC)
return divisorName
########################################
# assign all derived parameters
@staticmethod
def assignDerivedParameters(
state,
splitGSU: bool,
printRejectionReason: bool,
printIndexAssignmentInfo: bool,
isaInfoMap,
rocmVersion: SemanticVersion
):
isa = tuple(state["ISA"])
if state["WavefrontSize"] == -1:
state["WavefrontSize"] = 32 if isaInfoMap[isa].archCaps["HasWave32"] else 64
if state["MaxLDS"] == -1:
state["MaxLDS"] = isaInfoMap[isa].archCaps["DeviceLDS"]
# NOTE: This entry should instead should already be set on the solution within the logic
# files. This code will be removed once all logic files are updated to contain both
# the keys "EnableF32XdlMathOp" and "F32XdlMathOp".
state["EnableF32XdlMathOp"] = False
state["UseF32XEmulation"] = False # enable emulation for missing hardware support
#ignore the F32 xDL MathOp by default.
#enable F32 xDL MathOp only when the input type is f32.
if "F32XdlMathOp" in state["ProblemType"] \
and (not state["ProblemType"]["F32XdlMathOp"].isSingle()) \
and (state["ProblemType"]["DataType"].isSingle()):
state["EnableF32XdlMathOp"] = True
if isaInfoMap[isa].archCaps["HasF32XEmulation"]:
state["UseF32XEmulation"] = True
# initial info to be exported for solution prediction
state["CUOccupancy"] = -1
state["MathClocksUnrolledLoop"] = 0
Solution.assignProblemIndependentDerivedParameters(state, printRejectionReason, isaInfoMap)
if "AssignedDerivedParameters" in state:
if state["AssignedDerivedParameters"]:
return
state["AssignedDerivedParameters"] = False
for s in Solution.InternalKeys:
if '_'+s not in state:
state['_'+s] = state[s]
#del state[s]
# Force update _GlobalAccumulation
computeBytes = int(state["ProblemType"]["ComputeDataType"].numBytes())
state["_GlobalAccumulation"] = None
computeName = state["ProblemType"]["ComputeDataType"].toName()
if state["UseDotInstruction"] and state["GlobalSplitUAlgorithm"] == 'MultipleBufferSingleKernel':
# dot2 kernel does not support MBSK
state["GlobalSplitUAlgorithm"] = 'MultipleBuffer'
state["MbskPrefetchMethod"] = 0
if state["StreamK"] > 0 and state["StreamKAtomic"] == 0:
# StreamK Workspace size
state["_GlobalAccumulation"] = 'PartialsBuffer'
elif state["GlobalSplitUAlgorithm"] == 'SingleBuffer':
if computeName != state["ProblemType"]["DestDataType"].toName():
state["_GlobalAccumulation"] = 'SingleBuffer'
elif state["GlobalSplitUAlgorithm"] == 'MultipleBuffer':
state["_GlobalAccumulation"] = 'MultipleBuffer'
elif state["GlobalSplitUAlgorithm"] == 'MultipleBufferSingleKernel':
if (not splitGSU):
state["_GlobalAccumulation"] = 'MultipleBufferSingleKernel'
else:
if state["GlobalSplitU"] > 1 or state["GlobalSplitU"] == -1:
state["_GlobalAccumulation"] = 'MultipleBufferSingleKernel'
if state["_GlobalAccumulation"] == 'MultipleBufferSingleKernel':
state["SynchronizerSizeCheck"] = 1
# state["BatchSizeEqual"] = 1
if state["StreamK"] == 0 and state["GlobalSplitU"] == 0:
reject(state, printRejectionReason, "Either GSU or StreamK must be enabled")
return
if state["GlobalSplitU"] == 0 and state["AdaptiveGemmGSUA"] == 1:
reject(state, printRejectionReason, "AdaptiveGemmGSUA requires GSU enablement")
return
if state.get("AdaptiveGemmNTAB", 0) != 0:
# KernArgsVersion >= 3 is required so internalArg0
# reserves bits 12/13 for the NTA/NTB selector.
if state["InternalSupportParams"]["KernArgsVersion"] < 3:
state["InternalSupportParams"]["KernArgsVersion"] = 3
if state["StreamK"] != 0:
#state["AssertSummationElementMultiple"] = 1 # Cannot keep ASEM with Stream-K
state["GlobalSplitU"] = 0 # Cannot enable both Stream-K and GSU
state["InternalSupportParams"]["SupportUserGSU"] = False # Disable UserGSU for Stream-K
state["GlobalSplitUAlgorithm"] = "MultipleBuffer" # Set default Algorithm
state["AdaptiveGemmGSUA"] = 0 # Disable AdaptiveGemmGSUA for Stream-K
if state["ClusterDim"] != [1, 1]:
# Only SK3 (two-tile DP-first) is cluster-aware; SK4 (dynamic per-XCD
# work queues) and SK5 (hybrid) have no cluster WG-id decode support.
if state["StreamK"] in (4, 5):
reject(state, printRejectionReason,
"Stream-K modes 4 and 5 do not support ClusterDim != [1, 1]")
# Stream-K launches a 1-D grid in X, so StreamKIdx = WorkGroup0 = cluster_x*nwg_x
# + wg_x must stay a unique linear index. A Y-extent > 1 collides WorkGroup0 across
# WGs that differ only in Y, so restrict clustering to the X dimension.
if state["ClusterDim"][1] != 1:
reject(state, printRejectionReason,
"Stream-K + ClusterDim requires ClusterDim Y-extent == 1")
# StreamKXCCMapping remaps WorkGroup0 with no cluster awareness; disable it.
state["StreamKXCCMapping"] = 0
if not state["EnableMatrixInstruction"]:
# Source/MAC (non-MI) Stream-K: partial-write + fixup are datapath-agnostic,
# so allow it for Assembly source kernels (other SK constraints still apply).
if state["KernelLanguage"] != "Assembly":
reject(state, printRejectionReason, "Stream-K (non-MatrixInstruction) requires Assembly source kernels")
# if state["PersistentKernel"]:
# reject(state, printRejectionReason, "Cannot enable both Stream-K and PersistentKernel")
if not state["ProblemType"]["StridedBatched"]:
reject(state, printRejectionReason, "General batch not supported with Stream-K")
if state["ProblemType"]["GroupedGemm"]:
reject(state, printRejectionReason, "Grouped gemm not yet supported with Stream-K")
if state["ScheduleGlobalRead"] != 1:
reject(state, printRejectionReason, "ScheduleGlobalRead not supported with Stream-K")
if state["ScheduleLocalWrite"] != 1:
reject(state, printRejectionReason, "ScheduleLocalWrite not supported with Stream-K")
# SIA 4 is remapped to _ScheduleIterAlg 0 upstream, so it is covered here too.
if state["_ScheduleIterAlg"] not in (0, 1, 2, 3):
reject(state, printRejectionReason, "ScheduleIterAlg not supported with Stream-K")
if state["TDMInst"] == 3 and state["PrefetchGlobalRead"] not in (1, 2) \
and not state["UseSubtileImpl"]:
reject(state, printRejectionReason,
"Stream-K + TDMInst=3 requires PrefetchGlobalRead in (1, 2)")
if not state["BufferStore"]:
reject(state, printRejectionReason, "Stream-K requires BufferStore")
_validateStreamKForceDPOnly(state, printRejectionReason)
if state["StreamKAtomic"] == 1:
if state["StreamK"] == 4:
reject(state, printRejectionReason, "Atomic Stream-K is not supported with dynamic work queue mode")
if state["StreamK"] == 5:
reject(state, printRejectionReason, "Atomic Stream-K is not supported with hybrid mode (StreamK=5)")
if not state["ProblemType"]["DataType"].isSingle():
reject(state, printRejectionReason, "Atomic Stream-K currently only tested for SGEMM")
if state["LocalSplitU"] > 1:
reject(state, printRejectionReason, "Atomic Stream-K not working with LocalSplitU")
if state.get("PrefetchAcrossPersistent", 0):
# StreamK PAP emits a next-tile first-PGR handoff inside the NLL
# window, then restores borrowed tile identity, descriptors/shadow
# limits, stagger state, and LDS bank state before current-tile code
# resumes. Keep rejecting axes whose borrowed-state contract is not
# audited below.
# Note: HalfPLR+PAP is rejected in the HalfPLR block below (HalfPLR forces
# SuppressNoLoadLoop after this guard runs, so it is caught there instead).
if state["StreamK"] != 3:
reject(state, printRejectionReason, "PrefetchAcrossPersistent is currently supported only with StreamK=3")
if not state["BufferLoad"]:
reject(state, printRejectionReason, "PrefetchAcrossPersistent requires BufferLoad")
if state["PrefetchGlobalRead"] < 1:
reject(state, printRejectionReason, "PrefetchAcrossPersistent requires PGR >= 1")
if state["PrefetchGlobalRead"] > 2:
reject(state, printRejectionReason, "PrefetchAcrossPersistent requires PrefetchGlobalRead in [1, 2]")
if state["1LDSBuffer"] == 1:
reject(state, printRejectionReason, "PrefetchAcrossPersistent requires 1LDSBuffer != 1 (double LDS buffer)")
if state["DirectToVgprA"] or state["DirectToVgprB"]:
reject(state, printRejectionReason, "PrefetchAcrossPersistent not supported with DirectToVgpr")
if state["ProblemType"]["NumIndicesSummation"] > 1:
reject(state, printRejectionReason, "PrefetchAcrossPersistent not supported with multiple summation indices")
if not state["BufferStore"]:
reject(state, printRejectionReason, "PrefetchAcrossPersistent NLL path requires BufferStore")
# HalfPLR sets SuppressNoLoadLoop *after* this guard runs, so it is not
# caught here; the HalfPLR block rejects HalfPLR+PAP order-independently.
if state.get("SuppressNoLoadLoop", False):
reject(state, printRejectionReason, "PrefetchAcrossPersistent NLL path requires NoLoadLoop")
if state["ProblemType"]["Sparse"]:
reject(state, printRejectionReason, "PrefetchAcrossPersistent NLL path not supported with sparse")
if state["StoreRemapVectorWidth"]:
reject(state, printRejectionReason, "PrefetchAcrossPersistent NLL path not supported with StoreRemap")
# DP-only (StreamKForceDPOnly) + PAP is supported (mirror phase): DP-only
# StreamK==3 is a persistent grid-stride kernel (graWorkGroup pre-advances
# StreamKIter += skGrid*ItersPerTile each persistent iteration), so the
# standard PAP next-tile handoff applies unchanged. AddressFlags is
# non-zero for DP-only (tree reduction passes the Synchronizer pointer),
# so the PAP AddressFlags guard falls through correctly. All
# partial/fixup/workspace machinery is already bypassed by
# StreamKForceDPOnly guards in storeBranches/writePartials/
# computeStoreSrdStart, and StreamKLocalStart/End are constant
# (0 / ItersPerTile). No DP-only-specific gating is required here;
# Phase 2 strips the now-redundant snapshot/restore of those constants.
if state["DebugPersistentKernelLoopForever"] and state["StreamK"] != 3:
# Mode 4 exits via KernelEnd in graWorkGroup, so the flag would no-op.
reject(state, printRejectionReason,
"DebugPersistentKernelLoopForever requires StreamK=3 (got %d)"
% state["StreamK"])
if state["StreamKWorkStealing"]:
# Codegen-time rejections only; there is no hardware context here
# (MI300A/MI300X both compile as gfx942). The kernel bakes a power-of-two
# per-XCD queue count from origami; the host (ContractionSolution.cpp)
# enforces the device's runtime NUM_XCD against that baked count and
# otherwise serves a non-work-stealing solution. Work stealing only exists
# in the dynamic-queue fetch (auto-mode SK4 and the SK4 sub-path of SK5).
if state["StreamK"] not in (4, 5):
reject(state, printRejectionReason,
"StreamKWorkStealing requires StreamK in {4,5} (got %d)"
% state["StreamK"])
# Stealing is only defined for the non-atomic partials+fixup path.
# Atomic SK4/SK5 is already rejected above; keep this explicit guard so
# the combination can never slip through.
if state["StreamKAtomic"]:
reject(state, printRejectionReason,
"StreamKWorkStealing is not supported with StreamKAtomic")
# Reject DebugStreamK with work stealing: it can leave a tile-owning
# queue with no home workgroup, breaking the W_q>=1 auto-reset precondition.
if state["DebugStreamK"]:
reject(state, printRejectionReason,
"StreamKWorkStealing requires DebugStreamK=0 (the per-queue "
"auto-reset relies on W_q>=1 whenever tiles_q>=1); got %d"
% state["DebugStreamK"])
# The steal path (streamKWorkStealingSteal) emits s_atomic_inc
# unconditionally, but the home fetch (_fetchNextWorkItem) only falls
# back to a returning vector atomic when scalar atomics are absent. On
# arches without scalar atomics (HasSAtomic=false, e.g. gfx1250) the
# steal would emit an unsupported s_atomic_inc with no vector fallback,
# so reject work stealing there.
if not isaInfoMap[isa].asmCaps["HasSAtomic"]:
reject(state, printRejectionReason,
"StreamKWorkStealing requires scalar atomics (HasSAtomic); the "
"work-stealing steal path emits s_atomic_inc with no vector "
"fallback (e.g. gfx1250 has HasSAtomic=false)")
if not state["Valid"]:
print2("in assignDerivedParameters, state['Valid'] = False")
return
else:
# If not using StreamK, clear other stream-k settings to avoid duplicate kernels
state["StreamKForceDPOnly"] = 0
state["StreamKAtomic"] = 0
state["StreamKWorkStealing"] = 0
state["StreamKXCCMapping"] = 0
state["StreamKFixupTreeReduction"] = 0
state["DebugStreamK"] = 0
state["PrefetchAcrossPersistent"] = 0
state["DebugPersistentKernelLoopForever"] = False
if not state["BufferStore"]:
if state["GlobalSplitU"] != 0:
reject(state, printRejectionReason, "GlobalSplitU requires BufferStore (flat store workspace addressing not supported)")
elif state["_GlobalAccumulation"]:
reject(state, printRejectionReason, "GlobalAccumulation requires BufferStore (workspace SRD addressing not supported)")
computeBytes = int(state["ProblemType"]["ComputeDataType"].numBytes())
state["_WorkspaceSizePerElemC"] = computeBytes
state["_WorkspaceSizePerElemBias"] = 0
if state["ProblemType"]["UseBias"] and state["ProblemType"]["Gradient"]:
state["_WorkspaceSizePerElemBias"] = computeBytes
state["WorkspaceCheck"] = [state["_WorkspaceSizePerElemC"], state["_WorkspaceSizePerElemBias"], state["GlobalSplitU"] if (state["GlobalSplitUAlgorithm"] == 'MultipleBuffer' or state["_GlobalAccumulation"] == 'MultipleBufferSingleKernel') else 1]
if state["VectorStore"] == -1:
state["_VectorStore"] = 1 # default, may be changed if needed to generate a valid kernel
ProblemType.assignDerivedParameters(state["ProblemType"], printIndexAssignmentInfo)
if not state["Valid"]:
print2("in assignDerivedParameters, state['Valid'] = False")
return
if not isaInfoMap[isa].asmCaps["HasNTModifier"] and not isaInfoMap[isa].asmCaps.get("HasTHModifier", False):
for ch in ["", "A", "B", "C", "D", "E", "WS", "Metadata"]:
if state["NonTemporal%s"%ch] >= 4:
state["NonTemporal%s"%ch] -= 4
if state["TemporalHint"] != -1:
for ch in _cacheHintTensors:
state["TemporalHint%s"%ch] = state["TemporalHint"]
if state["NonVolatile"] != -1:
for ch in _cacheHintTensors:
state["NonVolatile%s"%ch] = state["NonVolatile"]
if not isaInfoMap[isa].asmCaps.get("HasTHModifier", False):
unsupportedTH = [
"TemporalHint%s"%ch for ch in _cacheHintTensors
if state["TemporalHint%s"%ch] != 0
]
if unsupportedTH:
reject(state, printRejectionReason, "TemporalHint is not supported on this platform (%s)" % ", ".join(unsupportedTH))
return
if not isaInfoMap[isa].asmCaps.get("HasNVModifier", False):
unsupportedNV = [
"NonVolatile%s"%ch for ch in _cacheHintTensors
if state["NonVolatile%s"%ch] != 0
]
if unsupportedNV:
reject(state, printRejectionReason, "NonVolatile is not supported on this platform (%s)" % ", ".join(unsupportedNV))
return
if state["WavefrontSize"] == 32 and not isaInfoMap[isa].archCaps["HasWave32"]:
reject(state, printRejectionReason, "WavefrontSize=32 not supported for ISA {}".format(isa))
return
if state["WavefrontSize"] == 32 and state["KernelLanguage"] == "Source":
reject(state, printRejectionReason, "WavefrontSize=32 not yet supported for source kernels.")
return
if state["EnableMatrixInstruction"]:
if not (isaInfoMap[isa].asmCaps["HasMFMA"] or isaInfoMap[isa].asmCaps["HasWMMA"]):
reject(state, printRejectionReason, f"isa {isa} doesn't support matrix instruction")
return
key = state["ProblemType"]["MacDataTypeA"].toChar() + state["ProblemType"]["MacDataTypeB"].toChar()
if key not in ("SS", "XX", "DD", "BB", "HH", "CC", "ZZ", "I8I8", \
"F8F8", "F8B8", "B8B8", "B8F8", \
"F8NF8N", "F8NB8N", "B8NB8N", "B8NF8N", \
"F8F6", "F6F8", "F8F4", "F4F8","B8F6", "F6B8", "B8F4", "F4B8", "F8B6", "B6F8","B8B6", "B6B8",\
"F6F6", "B6B6", "F6B6", "B6F6","B6F4", "F4B6", "F6F4", "F4F6", "F4F4"):
reject(state, printRejectionReason, "didn't support Matrix Instruction with type %s" % key)
return
if (not isaInfoMap[isa].asmCaps["HasMFMA"] and isaInfoMap[isa].asmCaps["HasWMMA"] and (state["WavefrontSize"] == 64)):
print2("!! Warning: WMMA only well tune on WGP mode, wave size = 32")
# reject(state, printRejectionReason, "WMMA only suppport on WGP mode, wave size = 32")
# return
if not state["MIBlock"] or len(state["MIBlock"]) != 6:
reject(state, printRejectionReason, "invalid MIBlock")
return
if not state["MIWaveGroup"] or len(state["MIWaveGroup"]) != 2:
reject(state, printRejectionReason, "invalid MIWaveGroup")
return
if not state["MIWaveTile"] or len(state["MIWaveTile"]) != 2:
reject(state, printRejectionReason, "invalid MIWaveTile")
return
# LraTileAssignment computes wave/block offsets with vectorStaticRemainder
# using num1DWaves (=MIWaveGroup[0|1]) or num1DBlocks (=MatrixInstBM|BN)
# as the divisor, and reuses one vgpr for dividend / quotient / remainder.
# That aliasing is safe only on the power-of-2 fast path (single
# v_and_b32); the magic-number path would overwrite the dividend with the
# quotient before computing the remainder.
for _name, _val in (("MIWaveGroup[0]", state["MIWaveGroup"][0]),
("MIWaveGroup[1]", state["MIWaveGroup"][1]),
("MatrixInstBM", state["MatrixInstBM"]),
("MatrixInstBN", state["MatrixInstBN"])):
if _val > 1 and (_val & (_val - 1)) != 0:
reject(state, printRejectionReason,
f"{_name}={_val} must be a power of two (LraTileAssignment vectorStaticRemainder fast path)")
return
if state["UseSubtileImpl"] and (state["ProblemType"]["MXBlockA"] or state["ProblemType"]["MXBlockB"]):
if state["MIWaveTile"][0] % 2 != 0 or state["MIWaveTile"][1] % 2 != 0:
reject(state, printRejectionReason,
"UseSubtileImpl=1 with MX datatype requires even MIWaveTile, got [%d, %d]"
% (state["MIWaveTile"][0], state["MIWaveTile"][1]))
return
if isaInfoMap[isa].asmCaps["HasMFMA"]:
if not state["ProblemType"]["HighPrecisionAccumulate"] \
and state["ProblemType"]["DataType"].numRegisters() < 1 \
and state["ProblemType"]["DataTypeA"].numRegisters() <= state["ProblemType"]["MacDataTypeA"].numRegisters() \
and state["ProblemType"]["DataTypeB"].numRegisters() <= state["ProblemType"]["MacDataTypeB"].numRegisters():
reject(state, printRejectionReason, "Matrix instructions for half, bf16 (or i8) types are natively accumulated" + \
" in fp32 (or i32) precision. Please add the following config:" + \
"\n - HighPrecisionAccumulate: True")
return
#FIXME: remove this section?
if isaInfoMap[isa].asmCaps["HasWMMA"]:
if state["ProblemType"]["DataType"].numRegisters() > 2:
reject(state, printRejectionReason, "WMMA only support f32, half, bf16 and i8 type")
return
if state["ProblemType"]["DataType"].isDouble() and not (isaInfoMap[isa].asmCaps["HasMFMA_f64"] or isaInfoMap[isa].asmCaps["HasWMMA_V3_f64"]):
reject(state, printRejectionReason, f"isa {isa} doesn't support matrix instruction with type f64")
return
if state["InterleaveAlpha"]:
reject(state, printRejectionReason, "Matrix instruction doesn't support InterleaveAlpha")
return
if state["ProblemType"]["DataType"].isInt8():
if isa[:2] == (9, 4):
if tuple(state["MatrixInstruction"])[:3] in ((32, 32, 8), (16, 16, 16)):
reject(state, printRejectionReason, "v_mfma_i32_32x32x8 and v_mfma_i32_16x16x16 have been deprecated in gfx94x")
return
if state["ProblemType"]["ComputeDataType"].isDouble():
# See [4,4,4,4] snop for more info
if state["MatrixInstruction"] == [4,4,4,4] and (not state['ISA'] == IsaVersion(9,0,10)) and state["_ScheduleIterAlg"] == 3:
reject(state, printRejectionReason, "Currently Matrix instructions [4,4,4,4] is disabled.")
return
if state["UseF32XEmulation"]:
if not isaInfoMap[isa].archCaps["HasF32XEmulation"]:
reject(state, printRejectionReason, "Missing emulation for F32X")
return
if state["_ScheduleIterAlg"] not in (0, 1, 3):
reject(state, printRejectionReason, "F32X emulation requires ScheduleIterAlg to be one of {0, 1, 3}")
return
if tuple(state["MatrixInstruction"])[:3] in ((16, 16, 8), (16, 16, 16), (32, 32, 4)):
reject(state, printRejectionReason, "tf32 emulation currently only supports mfma MI 16x16x32 and 32x32x16")
return
else:
if not state["ProblemType"]["HighPrecisionAccumulate"] \
and state["ProblemType"]["ComputeDataType"].numRegisters() > state["ProblemType"]["DataType"].numRegisters() :
reject(state, printRejectionReason, "For non-MI Kernel, if sizeof(ComputeDataType) > sizeof(DataType), " + \
"Please add the following config:" + \
"\n - HighPrecisionAccumulate: True")
return
if state["ProblemType"]["Sparse"]:
reject(state, printRejectionReason, "Sparse A problem is only supported by SMFMA MI kernel.")
return
if state["ThreadTile0"] > 16 or state["ThreadTile1"] > 16:
reject(state, printRejectionReason, "Invalid value for ThreadTile")
return
if state["_ScheduleIterAlg"] == 2 or state["_ScheduleIterAlg"] == 3:
reject(state, printRejectionReason, "SIA2 and SIA3 only support MatrixInstruction")
return
if state["ProblemType"]["Tensor0"]==0:
state["ThreadTileA"] = state["ThreadTile0"]
state["ThreadTileB"] = state["ThreadTile1"]
state["SubGroupA"] = state["SubGroup0"]
state["SubGroupB"] = state["SubGroup1"]
state["MacroTileA"] = state["MacroTile0"]
state["MacroTileB"] = state["MacroTile1"]
if state["EnableMatrixInstruction"]:
state["MIWaveTileA"] = state["MIWaveTile"][0]
state["MIWaveTileB"] = state["MIWaveTile"][1]
else:
state["ThreadTileB"] = state["ThreadTile0"]
state["ThreadTileA"] = state["ThreadTile1"]
state["SubGroupB"] = state["SubGroup0"]
state["SubGroupA"] = state["SubGroup1"]
state["MacroTileB"] = state["MacroTile0"]
state["MacroTileA"] = state["MacroTile1"]
if state["EnableMatrixInstruction"]:
state["MIWaveTileA"] = state["MIWaveTile"][1]
state["MIWaveTileB"] = state["MIWaveTile"][0]
if state["ProblemType"]["Sparse"] == 2 and state["DirectToVgprSparseMetadata"]:
reject(state, printRejectionReason, "Sparse B does not supprot DirectToVgprSparseMetadata")
return
if state["ProblemType"]["Sparse"]:
state["LocalWriteUseSgprMetadata"] = False
if state["ProblemType"]["Sparse"] == 2:
if not state["DirectToVgprSparseMetadata"]:
state["ThreadTileMetadata"] = state["ThreadTileB"]
state["SubGroupMetadata"] = state["SubGroupB"]
state["MacroTileMetadata"] = state["MacroTileB"]
state["WaveSeparateGlobalReadMetadata"] = state["WaveSeparateGlobalReadB"]
state["ProblemType"]["MirrorDimsMetadata"] = list(state["ProblemType"]["MirrorDimsB"])
state["VectorWidthMetadata"] = state["VectorWidthB"]
if state["EnableMatrixInstruction"]:
state["MIWaveTileMetadata"] = state["MIWaveTileB"]
if state["DirectToLdsMetadata"] and not state["DirectToLdsB"]:
state["DirectToLdsMetadata"] = 0
else:
if not state["DirectToVgprSparseMetadata"]:
state["ThreadTileMetadata"] = state["ThreadTileA"]
state["SubGroupMetadata"] = state["SubGroupA"]
state["MacroTileMetadata"] = state["MacroTileA"]
state["WaveSeparateGlobalReadMetadata"] = state["WaveSeparateGlobalReadA"]
state["ProblemType"]["MirrorDimsMetadata"] = list(state["ProblemType"]["MirrorDimsA"])
state["VectorWidthMetadata"] = state["VectorWidthA"]
if state["EnableMatrixInstruction"]:
state["MIWaveTileMetadata"] = state["MIWaveTileA"]
if state["DirectToLdsMetadata"] and not state["DirectToLdsA"]:
state["DirectToLdsMetadata"] = 0
elif not state["ProblemType"]["Sparse"]:
state["DirectToVgprSparseMetadata"] = False
state["DirectToLdsMetadata"] = 0
state["MIWaveTileMetadata"] = 0
if state["NonTemporal"] != -1:
state["NonTemporalA"] = state["NonTemporal"]
state["NonTemporalB"] = state["NonTemporal"]
state["NonTemporalC"] = state["NonTemporal"]
state["NonTemporalD"] = state["NonTemporal"]
state["NonTemporalGate"] = state["NonTemporal"]
state["NonTemporalMetadata"] = state["NonTemporal"]
if isaInfoMap[isa].asmCaps.get("HasTHModifier", False):
droppedNT = sorted(
"NonTemporal%s"%ch for ch in ("",) + _cacheHintTensors
if state["NonTemporal%s"%ch] >= 4
)
if droppedNT:
printWarning(
"gfx1250 ignores the NonTemporal nt bit (0x4) on %s; use TemporalHint*=1 (TH_NT) for non-temporal hints."
% ", ".join(droppedNT)
)
reservedLoadHints = [
"TemporalHint%s"%ch for ch in _cacheHintLoadTensors
if state["TemporalHint%s"%ch] == 7
]
if reservedLoadHints:
reject(
state,
printRejectionReason,
"TemporalHint=7 is reserved for loads (%s)" % ", ".join(reservedLoadHints),
)
return
# TODO: Remove this reject once amdclang++ supports SCOPE_SYS with LU/WB hints.
invalidScopeParams = [
"NonTemporal%s"%ch for ch in _cacheHintTensors
if state["TemporalHint%s"%ch] == 3 and state["NonTemporal%s"%ch] & 0x3 == 0x3
]
if invalidScopeParams:
reject(
state,
printRejectionReason,
"TemporalHint=3 maps to LU/WB and is not valid with SCOPE_SYS (%s)"
% ", ".join(invalidScopeParams),
)
return
# Init vars early since there are early-exit return statements below
# tentative init for UseGeneralizedNLCOneA/B
# set True for DTL
state["UseGeneralizedNLCOneA"] = state["DirectToLdsA"]
state["UseGeneralizedNLCOneB"] = state["DirectToLdsB"]
state["UseGeneralizedNLCOneMetadata"] = state["ProblemType"]["Sparse"] and state["DirectToLdsMetadata"]
state["UseGeneralizedNLCOneMXSA"] = False
state["UseGeneralizedNLCOneMXSB"] = False
state["LocalWriteUseSgprA"] = False
state["LocalWriteUseSgprB"] = False
state["LocalWriteUseSgprMetadata"] = False
state["StoreSwapAddr"] = False
if state["WorkGroupMappingXCC"] == -1:
if state["StreamK"] == 0:
reject(state, printRejectionReason, "Can only use auto WGMXCC with StreamK.")
return False
if state["StreamKXCCMapping"] != 0:
reject(state, printRejectionReason, "Cannot use auto WGMXCC with SKXCC.")
return False
if state["WorkGroupMapping"] == 0:
if state["WorkGroupMappingXCC"] == -1:
if state["StreamK"] == 0:
reject(state, printRejectionReason, "Can only use auto WGM with StreamK.")
return False
problemType = state["ProblemType"]
for (tc,batchMask) in (('A', 0x1), ('B', 0x2)):
freeDims = [i for i in problemType["IndexAssignments%s"%tc] if i in problemType["IndicesFree"]]
if not freeDims:
reject(state, printRejectionReason, "tensor%s contains no free indices.")
return False
# Determine which indices will be packed together as this impacts several different parms (sizes, magic numbers, etc)
# The order in PackedC*Indices also determines the order that dimensions are packed - the first elements in
# the list are the fastest-moving elements.
# The store code optimizes for C0 being the coalesced dimension and C1 the perp dimension.
# C0/C1 indices can come from IndexAssignmentsA or IndexAssignmentsB
# grid size [0,1]
state["PackedC0IdxChars"] = []
state["PackedC0IndicesX"] = []
indexChars = INDEX_CHARS
# Pack all the dimensions (free) of A into grid[0]
if problemType["Index0"] in problemType["IndexAssignmentsA"]:
tc0 = 'A'
tc1 = 'B'
else:
tc0 = 'B'
tc1 = 'A'
assert(isPackedIndex(state, problemType["Index01A"]))
assert(isPackedIndex(state, problemType["Index01B"]))
# Pack all the dimensions (batch and free) of A into grid[0]
for idx in problemType["IndexAssignments%s"%tc0]:
if isPackedIndex(state, idx):
assert (idx < problemType["NumIndicesC"])
state["PackedC0IdxChars"].append("%s" % indexChars[idx])
state["PackedC0IndicesX"].append(idx)
state["PackedC1IdxChars"] = []
state["PackedC1IndicesX"] = []
for idx in problemType["IndexAssignments%s"%tc1]:
if isPackedIndex(state, idx):
assert (idx < problemType["NumIndicesC"])
state["PackedC1IdxChars"].append("%s" % indexChars[idx])
state["PackedC1IndicesX"].append(idx)
# If dims are packed, then need to ensure a global vector load isn't split by a tensor dim
# (since this could result in non-contiguous addresses)
# Current implementation ensures that the vector load is not partial across the Free* boundary:
# GlobalReadVectorWidth=1 will always meet this requirement.
# (TODO - could make this more sophisticated if dims use default strides and are thus contiguous)
packedC0 = len(state["PackedC0IdxChars"])>1
packedC1 = len(state["PackedC1IdxChars"])>1
# MX scale layout + transport are validated by the MXLoadInst /
# MXScaleFormat derivation below (search for "MX scale layout & transport
# derivation").
bufferLoad = state["BufferLoad"] and state["KernelLanguage"] == "Assembly"
if not bufferLoad:
state["DirectToLds"] = 0
state["DirectToLdsA"] = False
state["DirectToLdsB"] = False
state["_UseSgprForGRO"] = False
# StaggerU only works with source kernels, or with BufferLoad.
state["StaggerU"] = 0
if state["PrefetchGlobalRead"] >= 2:
reject(state, printRejectionReason, "BufferLoad=0 does not support PrefetchGlobalRead>=2")
return
if state["DirectToVgprA"]:
reject(state, printRejectionReason, "DirectToVgprA requires BufferLoad")
return
if state["DirectToVgprB"]:
reject(state, printRejectionReason, "DirectToVgprB requires BufferLoad")
return
if problemType["UseBias"]:
reject(state, printRejectionReason, "BufferLoad=0 does not support UseBias due to no suppress no load.")
return
if problemType["Sparse"]:
reject(state, printRejectionReason, "BufferLoad=0 does not support Sparse due to VGPR pressure from non-SRD addressing")
return
if problemType["GroupedGemm"]:
reject(state, printRejectionReason, "BufferLoad=0 does not support GroupedGemm")
return
# Sub-dword types (fp8, int8, fp16, bf16) have correctness issues with flat
# addressing: tail loop reload uses globalread_gpr_record not populated for
# flat path, and edge/tail global reads produce wrong results.
if problemType["DataType"].numBytes() < 4:
reject(state, printRejectionReason, "BufferLoad=0 does not support sub-dword data types (flat addressing not validated)")
return
if state["TDMInst"]:
reject(state, printRejectionReason, "BufferLoad=0 does not support TDMInst")
return
#These modes only work under certain conditions, apply them here:
# - The "NoLoad" loop is only generated if PrefetchGlobalRead>0
# - And Suppress does not work if GSU>1 for some reason
if state["SuppressNoLoadLoop"]:
if not (bufferLoad and state["PrefetchGlobalRead"] == 1 and (state["GlobalSplitU"]==1 or state["GlobalSplitU"]==-1)):
state["SuppressNoLoadLoop"] = False
#print("PackedC0IdxChars", state["PackedC0IdxChars"])
#print("PackedC1IdxChars", state["PackedC1IdxChars"])
#
# UnrollMajorLDS{A,B}
# 0: means M/N is contiguous in LDS
# 1: means K is contiguous in LDS
#
# TLU{A,B}
# 1: means M/N is contiguous in global memory
# 0: means K is contiguous in global memory
#
if state["EnableMatrixInstruction"]:
if state["TransposeLDS"] == -1:
if state["ProblemType"]["TLUA"] and state["ProblemType"]["TLUB"]:
state["TransposeLDS"] = 0
else:
state["TransposeLDS"] = 1
if state["TransposeLDS"] == 0:
state["UnrollMajorLDSA"] = 0
state["UnrollMajorLDSB"] = 0
elif state["TransposeLDS"] == 1:
state["UnrollMajorLDSA"] = not state["ProblemType"]["TLUA"]
state["UnrollMajorLDSB"] = not state["ProblemType"]["TLUB"]
elif state["TransposeLDS"] == 2:
state["UnrollMajorLDSA"] = 1
state["UnrollMajorLDSB"] = 1
else: # mac instruction
if state["UseDotInstruction"]:
# dot2: force TLDS = 2
if state["TransposeLDS"] not in [-1,2]:
reject(state, "dot2 kernel does not support TransposeLDS != 2")
state["TransposeLDS"] = 2
state["UnrollMajorLDSA"] = True
state["UnrollMajorLDSB"] = True
else:
state["TransposeLDS"] = 0
state["UnrollMajorLDSA"] = False
state["UnrollMajorLDSB"] = False
def findSparseVectorWidth(steps, optVW):
if steps == 0:
return optVW
optVW_new = max(optVW // 2, 1)
if optVW_new == 1:
return optVW
else:
return findSparseVectorWidth(steps-1, optVW_new)
# Save whether VW was auto-derived (-1) for TDM VW fallback in depthUIteration
state.setdefault("_inputVWA_was_auto", state["VectorWidthA"] == -1)
state.setdefault("_inputVWB_was_auto", state["VectorWidthB"] == -1)
if state["VectorWidthA"] == -1:
if state["EnableMatrixInstruction"]:
regPerElem = state["ProblemType"]["MacDataTypeA"].numRegisters()
optVW = int(4 // regPerElem)
while 1:
if state["MIWaveTile"][0] % optVW == 0:
state["VectorWidthA"] = optVW
break
else:
optVW //= 2
if state["ProblemType"]["Sparse"]:
# sparse gfx1250: Currently disable autoVectorWidthA. VectorWidthA still can be set manually in yaml file.
# if isaInfoMap[isa].asmCaps["HasSWMMAC_gfx1250"] and state["ProblemType"]["Sparse"] == 1:
# state["VectorWidthA"] = max(findSparseVectorWidth(2, state["VectorWidthA"]), 1)
# else:
state["VectorWidthA"] = 1
else:
state["VectorWidthA"] = 1
if state["VectorWidthB"] == -1:
if state["EnableMatrixInstruction"]:
regPerElem = state["ProblemType"]["MacDataTypeB"].numRegisters()
optVW = int(4 // regPerElem)
while 1:
if state["MIWaveTile"][1] % optVW == 0:
state["VectorWidthB"] = optVW
break
else:
optVW //= 2
if state["ProblemType"]["Sparse"]:
# sparse gfx1250: Currently disable autoVectorWidthB. VectorWidthB still can be set manually in yaml file.
# if isaInfoMap[isa].asmCaps["HasSWMMAC_gfx1250"] and state["ProblemType"]["Sparse"] == 2:
# state["VectorWidthB"] = max(findSparseVectorWidth(2, state["VectorWidthB"]), 1)
# else:
state["VectorWidthB"] = 1
else:
state["VectorWidthB"] = 1
def isLDSTrEnabled(asmCaps: Dict, hasLDSTrans: bool, unrollMajorLDS: bool, dtv: bool, numBytes: int):
if unrollMajorLDS:
return False
if not hasLDSTrans:
return False
if numBytes == 0.5:
return asmCaps["HasLDSTrB64B4"]
elif numBytes == 0.75:
return asmCaps["HasLDSTrB96B6"]
elif numBytes == 1:
return asmCaps["HasLDSTrB64B8"]
elif numBytes == 2:
return asmCaps["HasLDSTrB64B16"] or asmCaps["HasLDSTrB128B16"]
return False
numBytesA = state["ProblemType"]["MacDataTypeA"].numBytes()
numBytesB = state["ProblemType"]["MacDataTypeB"].numBytes()
isa = tuple(state["ISA"])
# state["enableLDSTrA"] = state["LDSTrInst"] and isaInfoMap[isa].asmCaps["HasLDSTr"] and numBytes == 2 \
# and not state["UnrollMajorLDSA"] and not state["DirectToVgprA"]
# state["enableLDSTrB"] = state["LDSTrInst"] and isaInfoMap[isa].asmCaps["HasLDSTr"] and numBytes == 2 \
# and not state["UnrollMajorLDSB"] and not state["DirectToVgprB"]
# TODO- Is it possible for devices with asmCaps["HasLDSTr"], we automatically use it when UnrollMajorLDS=0
# Supporting manually transpose load when having "HasLDSTr" is not worthy.
state["enableLDSTrA"] = isLDSTrEnabled(isaInfoMap[isa].asmCaps, state["LDSTrInst"], state["UnrollMajorLDSA"], state["DirectToVgprA"], numBytesA)
state["enableLDSTrMXSA"] = False
state["enableLDSTrB"] = isLDSTrEnabled(isaInfoMap[isa].asmCaps, state["LDSTrInst"], state["UnrollMajorLDSB"], state["DirectToVgprB"], numBytesB)
state["enableLDSTrMXSB"] = False
# This reject kernels in 950 logic yaml, temporarily comment it out.
# finalLDSTrInst = state["enableLDSTrA"] or state["enableLDSTrB"]
# if state["LDSTrInst"] != finalLDSTrInst:
# # This means LDSTrInst=True, but none of A/B can be enabled (False)
# reject(state, printRejectionReason, "LDSTrInst is True but none of A/B can be enabled")
# return
state["enableGLTrA"] = state["DirectToVgprA"] and state["ProblemType"]["TLUA"] \
and ((numBytesA == 1 and isaInfoMap[isa].asmCaps["HasGLTr8B64"]) \
or (numBytesA == 2 and isaInfoMap[isa].asmCaps["HasGLTr16B128"]) \
)
state["enableGLTrB"] = state["DirectToVgprB"] and state["ProblemType"]["TLUB"] \
and ((numBytesB == 1 and isaInfoMap[isa].asmCaps["HasGLTr8B64"]) \
or (numBytesB == 2 and isaInfoMap[isa].asmCaps["HasGLTr16B128"]) \
)
if state["enableLDSTrA"] or state["enableGLTrA"]:
state["VectorWidthA"] = 1
if state["enableLDSTrB"] or state["enableGLTrB"]:
state["VectorWidthB"] = 1
if state["_ScheduleIterAlg"] == 2:
state["ExpandPointerSwap"] = True
print2("\nSet SIA=2, force ExpandPointerSwap=1")
if state["ExpandPointerSwap"]:
# Pointer swap only used if PGR==1 or (PGR>1 and double/double complex) - so set ExpandPointerSwap=0 here
# So far, EPS=1 and PGR>1 works only with double/double complex.
#if not (bufferLoad and state["PrefetchGlobalRead"] == 1):
if not (bufferLoad and ( state["PrefetchGlobalRead"] == 1 \
or (state["PrefetchGlobalRead"] > 1 and \
(state["ProblemType"]["DataType"].isDouble() or state["ProblemType"]["DataType"].isDoubleComplex()))
or (state["ProblemType"]["Sparse"] and state["PrefetchGlobalRead"] > 0))):
state["ExpandPointerSwap"] = False
#################################################################
# ForceUnrollSubIter requirements
# - Needs PGR > 0, double buffer
# - MIWaveTile must be even and larger than 2
# - TLU{A,B} cases only supported if using LdsTR or if VPerm not needed (size{A,B} >= 4)
# - Not supported for mixed precision cases currently
# - For non-CMS F32X kernels, SubIter is disabled after CMS check because
# the default scheduler's sub-tiling conflicts with F32X pack code.
TLUA = state["ProblemType"]["TLUA"]
TLUB = state["ProblemType"]["TLUB"]
def _canEnableSubIter():
sizeDataTypeA = state["ProblemType"]["DataTypeA"].numBytes()
sizeDataTypeB = state["ProblemType"]["DataTypeB"].numBytes()
sizeDataType = state["ProblemType"]["DataType"].numBytes()
return (
state["EnableMatrixInstruction"] and not state["ExpandPointerSwap"]
and state["DepthU"] == state["MatrixInstK"] and state["PrefetchGlobalRead"] and not state["1LDSBuffer"]
and (state["MIWaveTile"][0] > 2 and state["MIWaveTile"][1] > 2)
and (state["MIWaveTile"][0] % 2 == 0 and state["MIWaveTile"][1] % 2 == 0)
and (sizeDataTypeA == sizeDataType) and (sizeDataTypeB == sizeDataType)
and ((TLUA == False or state["enableLDSTrA"] or sizeDataTypeA >= 4) and (TLUB == False or state["enableLDSTrB"] or sizeDataTypeB >= 4) )
and (not state["ProblemType"]["Sparse"] or state["TransposeLDSMetadata"])
and not state["TDMInst"]
)
def _applySubIterSetting(enable):
if enable:
state["ForceUnrollSubIter"] = True
state["numSubTiles"] = 2
else:
state["ForceUnrollSubIter"] = False
state["numSubTiles"] = 1
_applySubIterSetting(_canEnableSubIter())
if state["ForceUnrollSubIter"]:
state["ClusterLocalRead"] = 1
state["TailloopInNll"] = False
if state["VectorWidthA"] == -1:
if state["EnableMatrixInstruction"]:
regPerElem = state["ProblemType"]["MacDataTypeA"].numRegisters()
optVW = int(4 // regPerElem)
while 1:
if state["MIWaveTile"][0] % optVW == 0:
state["VectorWidthA"] = optVW
break
else:
optVW //= 2
if state["ProblemType"]["Sparse"]:
state["VectorWidthA"] = 1
else:
state["VectorWidthA"] = 1
# VW adjustment for F32XEmu + TLU + SubIter
# -> this logic does not work. Reject the kernel so far.
if state["UseF32XEmulation"]:
if TLUA and state["numSubTiles"] > 1 and state["MIWaveTile"][0] % (state["VectorWidthA"] * state["numSubTiles"]) != 0:
# reject(state, printRejectionReason, "F32XEmulation + SubTiles requires MIWaveTile[0](%u) is multiple of VectorWidthA(%u) * 2"%(state["MIWaveTile"][0], state["VectorWidthA"]))
# return
# Many existing kernels are rejected with this condition.
# So far, continue with VectorWidthA //=state["numSubTiles"]
while state["MIWaveTile"][0] % (state["VectorWidthA"] * state["numSubTiles"]) != 0:
state["VectorWidthA"] //= state["numSubTiles"]
if state["SourceSwap"] and state["StoreVectorWidth"] > state["VectorWidthA"]:
# need to adjust StoreVectorWidth in SourceSwap case
state["StoreVectorWidth"] = state["VectorWidthA"]
if state["VectorWidthB"] == -1:
if state["EnableMatrixInstruction"]:
regPerElem = state["ProblemType"]["MacDataTypeB"].numRegisters()
optVW = int(4 // regPerElem)
while 1:
if state["MIWaveTile"][1] % optVW == 0:
state["VectorWidthB"] = optVW
break
else:
optVW //= 2
if state["ProblemType"]["Sparse"]:
state["VectorWidthB"] = 1
else:
state["VectorWidthB"] = 1
# VW adjustment for F32XEmu + TLU + SubIter
# -> this logic does not work. Reject the kernel so far.
if state["UseF32XEmulation"]:
if TLUB and state["numSubTiles"] > 1 and state["MIWaveTile"][1] % (state["VectorWidthB"] * state["numSubTiles"]) != 0:
# reject(state, printRejectionReason, "F32XEmulation + SubTiles requires MIWaveTile[1](%u) is multiple of VectorWidthB(%u) * 2"%(state["MIWaveTile"][1],state["VectorWidthB"]))
# return
# Many existing kernels are rejected with this condition.
# So far, continue with VectorWidthB //=state["numSubTiles"]
while state["MIWaveTile"][1] % (state["VectorWidthB"] * state["numSubTiles"]) != 0:
state["VectorWidthB"] //= state["numSubTiles"]
if state["ProblemType"]["Sparse"]:
if not state["DirectToVgprSparseMetadata"]:
state["VectorWidthMetadata"] = state["VectorWidthA"] if state["ProblemType"]["Sparse"] == 1 else state["VectorWidthB"]
# ON/OFF the sourceswap according to the sparse type automatically
state["SourceSwap"] = False if state["ProblemType"]["Sparse"] == 1 else True
# The real value of "1LDSBuffer" will be determined later (when it is -1), not here
# if state["EnableMatrixInstruction"] and not state["SourceSwap"] and (state["VectorWidthA"] > 1 or state["VectorWidthB"] > 1):
# reject(state, printRejectionReason, "not implement VectorWidth without SourceSwap")
# TT0,1 both must be multiples of VW, b/c of rC, rA, rB
if state["EnableMatrixInstruction"]:
if (state["MIWaveTile"][0] % state["VectorWidthA"]) != 0:
reject(state, printRejectionReason, "MIWaveTile0(%u) should be multiple of VectorWidthA(%u)" % (state["MIWaveTile"][0], state["VectorWidthA"]))
return
if (state["MIWaveTile"][1] % state["VectorWidthB"]) != 0:
reject(state, printRejectionReason, "MIWaveTile0(%u) should be multiple of VectorWidthB(%u)" % (state["MIWaveTile"][1], state["VectorWidthB"]))
return
if len(problemType["IndicesSummation"]) > 1:
# not supported with multiple summations, bug is maybe something with
# how stagger iteration is wrapped when unroll loop exits
state["StaggerU"] = 0
if state["ProblemType"]["MXBlockA"]:
state["DirectToVgprMXSA"] = state["DirectToVgprA"]
state["ThreadTileMXSA"] = state["ThreadTileA"]
state["SubGroupMXSA"] = state["SubGroupA"]
state["MacroTileMXSA"] = state["MacroTileA"]
state["WaveSeparateGlobalReadMXSA"] = state["WaveSeparateGlobalReadA"]
state["NumLoadsCoalescedMXSA"] = state["NumLoadsCoalescedA"]
Solution.checkAndAssignWaveSeparateGlobalRead(state, 'MXSA', printRejectionReason)
state["DirectToLdsMXSA"] = state["DirectToLdsA"]
state["LocalWriteUseSgprMXSA"] = state["DirectToLdsMXSA"]
state["ProblemType"]["MirrorDimsMXSA"] = list(state["ProblemType"]["MirrorDimsA"])
state["VectorWidthMXSA"] = state["VectorWidthA"]
state["MIWaveTileMXSA"] = state["MIWaveTileA"]
state["UnrollMajorLDSMXSA"] = state["UnrollMajorLDSA"]
if state["ProblemType"]["MXBlockB"]:
state["DirectToVgprMXSB"] = state["DirectToVgprB"]
state["ThreadTileMXSB"] = state["ThreadTileB"]
state["SubGroupMXSB"] = state["SubGroupB"]
state["MacroTileMXSB"] = state["MacroTileB"]
state["WaveSeparateGlobalReadMXSB"] = state["WaveSeparateGlobalReadB"]
state["NumLoadsCoalescedMXSB"] = state["NumLoadsCoalescedB"]
Solution.checkAndAssignWaveSeparateGlobalRead(state, 'MXSB', printRejectionReason)
state["DirectToLdsMXSB"] = state["DirectToLdsB"]
state["LocalWriteUseSgprMXSB"] = state["DirectToLdsMXSB"]
state["ProblemType"]["MirrorDimsMXSB"] = list(state["ProblemType"]["MirrorDimsB"])
state["VectorWidthMXSB"] = state["VectorWidthB"]
state["MIWaveTileMXSB"] = state["MIWaveTileB"]
state["UnrollMajorLDSMXSB"] = state["UnrollMajorLDSB"]
if state["ProblemType"]["MXBlockA"] or state["ProblemType"]["MXBlockB"]:
if state["ProblemType"]["MXBlockA"]:
state["LocalReadVectorWidthMXS"] = state["MIInputPerThreadMXSA"]
elif state["ProblemType"]["MXBlockB"]:
state["LocalReadVectorWidthMXS"] = state["MIInputPerThreadMXSB"]
# Some restrictions for half:
if state["KernelLanguage"] == "Assembly" \
and state["ProblemType"]["DataType"].isHalf():
if isaInfoMap[state["ISA"]].archCaps["HasEccHalf"]:
if not state["ProblemType"]["HighPrecisionAccumulate"] and state["AssertFree0ElementMultiple"] % 2 != 0:
# beta-on-edge has AF0EM requirement except for HPA kernels
reject(state, printRejectionReason, "Archs with HasEccHalf require AF0EM%2==0 except for HPA kernels")
return
if state["ConvertAfterDS"]:
if (state["ProblemType"]["DataType"].isHalf() == False) and (state["ProblemType"]["DataType"].isBFloat16() == False):
reject(state, printRejectionReason, "ConvertAfterDS only support DataType half")
return
if (state["ProblemType"]["DataTypeA"].isAnyFloat8() == False) and (state["ProblemType"]["DataTypeB"].isAnyFloat8() == False) \
and not (state["ProblemType"]["DataTypeA"].isSingle() and state["ProblemType"]["DataTypeB"].isSingle()):
reject(state, printRejectionReason, "one of DataTypeA or DataTypeB need to be float8/float8_fnuz or both are fp32")
return
if state["ProblemType"]["DataType"].isBFloat16() \
and (state["ProblemType"]["DataTypeA"].isSingle() and state["ProblemType"]["DataTypeB"].isSingle()):
reject(state, printRejectionReason, "ConvertAfterDS doesn't support SS_BSS type")
return
# Complex datatype restrictions.
if state["ProblemType"]["DataType"].isComplex():
if state["MIArchVgpr"] and state["StreamK"] != 0:
reject(state, printRejectionReason, "Complex datatype kernel does not support StreamK with MIArchVgpr yet.")
return
if state["TDMInst"]:
if not isaInfoMap[isa].asmCaps["HasTDM"]:
reject(state, printRejectionReason, "This arch does not support TDM")
return
if state["CompactLoopStore"]:
if not isaInfoMap[isa].asmCaps["HasMovRelsD2B32"]:
reject(state, printRejectionReason, "This arch does not support CompactLoopStore (no v_movrelsd_2_b32)")
return
# MX scale layout + transport derivation and validation. See
# _deriveAndValidateMXScaleLayoutAndTransport for the full set of rules.
if not _deriveAndValidateMXScaleLayoutAndTransport(
state,
isaInfoMap[isa].asmCaps,
isaInfoMap[isa].archCaps,
printRejectionReason):
return
# MX scale-format combination validation. Rejects candidates whose joint
# (A type, A scale, B type, B scale) tuple is not legal on gfx1250's
# WMMA_V3 MX path (see `Validators.MXScaleFormat.validateMXScaleFormatCombination`
# / ROCm/llvm-project#2634). Gated on HasWMMA_V3 so non-gfx1250 arches
# are unaffected.
if not validateMXScaleFormatCombination(
state,
isaInfoMap[isa].asmCaps,
printRejectionReason):
return
tdmInst: int = state["TDMInst"]
state["enableTDMA"] = bool(tdmInst & 0x01)
state["enableTDMB"] = bool(tdmInst & 0x02)
state["enableTDMMetadata"] = bool(state["ProblemType"]["Sparse"] and not state["DirectToVgprSparseMetadata"] and tdmInst > 0)
if tdmInst not in (0, 3):
reject(state, printRejectionReason, "Currently TDMA and TDMB must be enabled simultaneously")
return
if state.get("PrefetchAcrossPersistent", 0) and (state["enableTDMA"] or state["enableTDMB"]):
if not (state["enableTDMA"] and state["enableTDMB"]):
reject(state, printRejectionReason, "TDM + PrefetchAcrossPersistent requires TDMInst == 3 (enableTDMA and enableTDMB)")
return
if state["StreamK"] != 3:
reject(state, printRejectionReason, "TDM + PrefetchAcrossPersistent requires StreamK == 3")
return
if state["TDMInst"] == 3 and state["StaggerU"] != 0:
reject(state, printRejectionReason, "TDM + PrefetchAcrossPersistent with StaggerU is not implemented")
return
if (state["ProblemType"]["MXBlockA"] or state["ProblemType"]["MXBlockB"]) \
and (state["ProblemType"]["TransposeA"], state["ProblemType"]["TransposeB"]) != (True, False):
reject(state, printRejectionReason, "TDM + PrefetchAcrossPersistent with MX supports TN only")
return
if math.prod(state["MIWaveGroup"]) <= 1:
reject(state, printRejectionReason, "TDM + PrefetchAcrossPersistent requires wave-separated mode (prod(MIWaveGroup) > 1)")
return
# Multi-wave TDMSplit recomputes split increments transiently instead of
# persisting SGPRs, so MX-scaled SK+PAP tiles are allowed here; the
# SGPR-overflow check still drops any tile that overflows.
# Wave-separated TDM splits waves by parity (even=A, odd=B) and requires
# numComp = numWaves//2 to be a power of two; equivalently, numWaves
# itself must be a power of two (>= 2).
if state["enableTDMA"] and state["enableTDMB"]:
numWaves: int = state["NumWaves"]
if numWaves > 1 and (numWaves & (numWaves - 1)) != 0:
reject(state, printRejectionReason, f"Wave-separated TDM requires NumWaves={numWaves} to be a power of two")
return
# DepthU == -1?
if state["DepthU"] == -1:
depthuList = [1024,512,256,128,64,32,16]
else:
depthuList = [state["DepthU"]]
index = [0]
backupValues = []
for key, value in state.items():
if isinstance(value, int) and value < 0:
backupValues.append([key, value])
# Skip this check for subtile impl?
# TODO: Add this check back
# Save auto-derived VW for restoration across DepthU loop iterations.
# TDM VW fallback in depthUIteration may reduce VW for a specific DepthU;
# each DepthU attempt should start from the original auto-derived VW.
_savedVWA = state["VectorWidthA"]
_savedVWB = state["VectorWidthB"]
while True:
for backup in backupValues:
state[backup[0]] = backup[1]
state["VectorWidthA"] = _savedVWA
state["VectorWidthB"] = _savedVWB
state["ValidDepthU"] = True
state["DepthU"] = depthuList[index[0]]
Solution.depthUIteration(
state,
index,
depthuList,
problemType,
isa,
bufferLoad,
packedC0,
packedC1,
printRejectionReason,
isaInfoMap,
rocmVersion
)
if state["Valid"] or (state["ValidDepthU"] and (not state["Valid"])):
break
index[0] += 1
if index[0] >= len(depthuList):
break
if "ValidDepthU" in state:
del state["ValidDepthU"]
halfPLR: int = state["HalfPLR"]
state["HalfPLRA"] = bool(halfPLR & 0x01)
state["HalfPLRB"] = bool(halfPLR & 0x02)
if state["HalfPLR"]:
state["ClusterLocalRead"] = 0
state["SuppressNoLoadLoop"] = True
# Order-independent reject on the raw PAP flag: HalfPLR forces
# SuppressNoLoadLoop=True, which disables the PAP no-load-loop handoff
# (isPrefetchAcrossPersistentEnabled returns False). The PAP guard runs
# before this block, so reject here to avoid PAP silently no-oping while
# its raw-flag sites still fire.
if state.get("PrefetchAcrossPersistent", 0):
reject(state, printRejectionReason, "HalfPLR is incompatible with PrefetchAcrossPersistent (HalfPLR forces SuppressNoLoadLoop, which disables the PAP NLL handoff)")
return
# The subtile main loop ignores SuppressNoLoadLoop, which HalfPLR forces;
# the HalfPLR tail fixup lives only in the legacy calculateLoopNumIter path.
if state["UseSubtileImpl"]:
reject(state, printRejectionReason, "HalfPLR is not supported with UseSubtileImpl (subtile main loop does not honor SuppressNoLoadLoop)")
return
if state["PrefetchLocalRead"] != 1:
reject(state, printRejectionReason, "Need to set PrefetchLocalRead = 1 as it shares some common logic with HalfPLR")
return
if state["PrefetchGlobalRead"] == 0:
reject(state, printRejectionReason, "HalfPLR only supports PGR > 0")
return
if state["LoopIters"] == 1:
reject(state, printRejectionReason, "HalfPLR only supports LoopIters > 1")
return
if not (state["enableTDMA"] and state["enableTDMB"]):
reject(state, printRejectionReason, "HalfPLR only supports TDMInst")
return
if (state["HalfPLRA"] and (state["MIWaveTileA"] % 2 != 0)) or \
(state["HalfPLRB"] and (state["MIWaveTileB"] % 2 != 0)):
reject(state, printRejectionReason, "HalfPLR does not support odd WaveTile")
return
if not state["EnableMatrixInstruction"]:
reject(state, printRejectionReason, "Currently HalfPLR only supports MatrixInstruction")
return
if state["_ScheduleIterAlg"] != 0:
reject(state, printRejectionReason, "Currently HalfPLR only supports SIA = 0 or 4")
return
# SIA=4 is remapped to _ScheduleIterAlg=0 + _StinkyTofuOptLevel=3, so both
# SIA=0 and SIA=4 pass the check above. On StreamK, HalfPLR requires the
# StinkyTofu backend (SIA=4); plain SIA=0 is only allowed for non-StreamK.
if state["StreamK"] != 0 and state["ScheduleIterAlg"] != 4:
reject(state, printRejectionReason, "HalfPLR on StreamK requires ScheduleIterAlg = 4 (StinkyTofu)")
return
if (state["HalfPLRA"] and not (state["UnrollMajorLDSA"] or state["enableLDSTrA"])) or \
(state["HalfPLRB"] and not (state["UnrollMajorLDSB"] or state["enableLDSTrB"])):
reject(state, printRejectionReason, "Currently HalfPLR does not support packing (need UnrollMajorLDS or use LDSTrInst)")
return
if state["InnerUnroll"] != 1:
reject(state, printRejectionReason, "Currently HalfPLR only supports InnerUnroll = 1")
return
if state["ConvertAfterDS"]:
reject(state, printRejectionReason, "Currently HalfPLR does not support ConvertAfterDS")
return
if state["UseF32XEmulation"]:
reject(state, printRejectionReason, "Currently HalfPLR does not support UseF32XEmulation")
return
if state["UseDirect32XEmulation"] == True:
# Turn off Direct32X for the following kernels:
# Cijk_Ailk_Bjlk_S_MX_B_Bias_HA_S_SAV_UserArgs_MT16x16x512_MI16x16x1
if (state["MacroTile0"] == 16 and state["MacroTile1"] == 16 and state["DepthU"] == 512):
state["UseDirect32XEmulation"] = False
# backup UsePLRPack from yaml before calling hasCustomSchedule
backup_UsePLRPack = state["UsePLRPack"]
# Check if CMS is available for this solution
if state["UseCustomMainLoopSchedule"] in [-1, 1]:
# initialize CMS related config parameters (for CMS only)
state["SwapGlobalReadOrder"] = 0
state["UsePLRPack"] = 0
hasCMS,_ = hasCustomSchedule(state)
if state["UseCustomMainLoopSchedule"] == 1 and not hasCMS:
reject(state, printRejectionReason, "UseCustomMainLoopSchedule=1 but CMS is not supported")
state["UseCustomMainLoopSchedule"] = 1 if hasCMS else 0
# reject CMS + TailloopInNll
if state["TailloopInNll"] and state["UseCustomMainLoopSchedule"] == 1:
reject(state, printRejectionReason, "UseCustomMainLoopSchedule=1 is incompatible with TailloopInNll=True")
return
# UseSubtileImpl has its own main loop scheduler; CMS is not compatible.
if state["UseSubtileImpl"] and state["UseCustomMainLoopSchedule"] == 1:
reject(state, printRejectionReason, "UseCustomMainLoopSchedule=1 is incompatible with UseSubtileImpl")
if state.get("PrefetchAcrossPersistent", 0) and state["UseCustomMainLoopSchedule"] == 1:
reject(state, printRejectionReason, "PrefetchAcrossPersistent NLL path not supported with custom main-loop scheduling")
# additional setting for non CMS
if state["UseCustomMainLoopSchedule"] == 0:
# Non-CMS, non-MFMA F32X (e.g. gfx1250 WMMA): disable SubIter because
# the default scheduler's sub-tiling conflicts with F32X pack code's
# destructive in-place VGPR conversion.
# MFMA-based XF32 (gfx950) and CMS kernels are exempt.
if state["UseF32XEmulation"] and not state["UseMFMAF32XEmulation"] and state["ForceUnrollSubIter"]:
_applySubIterSetting(False)
if state["UseMFMAF32XEmulation"]:
state["MfmaInitCVgprs"] = True
# usePLRPack check
# adjust setting only for non CMS (keep original setting for CMS)
if backup_UsePLRPack:
state["UsePLRPack"] = 1
# MatrixInstruction only
if not state["EnableMatrixInstruction"]:
state["UsePLRPack"] = 0
# F32X emulation only
if not state["UseF32XEmulation"]:
state["UsePLRPack"] = 0
# SIA3 only
if state["_ScheduleIterAlg"] != 3:
state["UsePLRPack"] = 0
# enable UsePLRPack for SubIter only
if not state["ForceUnrollSubIter"]:
state["UsePLRPack"] = 0
# DirectToLds (both A and B) only
if state["DirectToLds"] != 1:
state["UsePLRPack"] = 0
# PGR and PLR should be non 0
if state["PrefetchGlobalRead"] == 0 or state["PrefetchLocalRead"] == 0:
state["UsePLRPack"] = 0
# disable SwapGlobalReadOrder if grmode(normal/DTL/DTV) is different between A and B
# GRA and GRB need to be equivalent to swap the order
# keep original setting in CMS case
def getGrMode(tc):
grmode = 0
if state["DirectToLds%s"%tc]:
grmode = 1
elif state["DirectToVgpr%s"%tc]:
grmode = 2
return grmode
if state["UseCustomMainLoopSchedule"] == 0:
if getGrMode("A") != getGrMode("B"):
state["SwapGlobalReadOrder"] = 0
# SwapGlobalReadOrder does not work with UnrollLoopSwapGlobalReadOrder
if state["UnrollLoopSwapGlobalReadOrder"]:
state["SwapGlobalReadOrder"] = 0
# 0: Normal mode. Hardware applies all of the normal data dependency checks
# 1: Full expert mode (not suppoeted yet). Disable hardware checks against: VA_VDST, VA_SDST, VA_SSRC, VA_VCC, VM_VSRC and SA_SDST.
# 2: Disable only VA_VDST and VM_VSRC checks.
def evaluateExpertSchedulingMode() -> Literal[0, 1, 2]:
# Check if the current parameters is supported by the ExpertSchedulingMode
if not isaInfoMap[isa].archCaps["HasSchedMode"]: return 0
if state["ProblemType"]["Sparse"]: return 0
if state["ProblemType"]["DataType"].isSingle(): return 0
# parameters not tested yet:
supportedParameters = {
"EnableMatrixInstruction": state["EnableMatrixInstruction"],
"_ScheduleIterAlg": state["_ScheduleIterAlg"] == 3,
"WavefrontSize": state["WavefrontSize"] == 32,
"UnrollLoopSwapGlobalReadOrder": not state["UnrollLoopSwapGlobalReadOrder"],
"SuppressNoLoadLoop": not state["SuppressNoLoadLoop"],
"ScheduleGlobalRead": state["ScheduleGlobalRead"] == 1,
"ScheduleLocalWrite": state["ScheduleLocalWrite"] == 1,
"GlobalReadPerMfma": state["GlobalReadPerMfma"] == 1,
"InterleaveAlpha": not state["InterleaveAlpha"],
"DirectToLds": not state["DirectToLds"],
"UseSgprForGRO": state["UseSgprForGRO"] == -1,
"UseInstOffsetForGRO": not state["UseInstOffsetForGRO"],
"Use64bShadowLimit": state["Use64bShadowLimit"] == 1,
"StorePriorityOpt": not state["StorePriorityOpt"],
"StoreSyncOpt": not state["StoreSyncOpt"],
"GroupLoadStore": not state["GroupLoadStore"],
"StreamK": not state["StreamK"],
"StreamKAtomic": not state["StreamKAtomic"],
"StreamKWorkStealing": not state["StreamKWorkStealing"],
"StreamKXCCMapping": not state["StreamKXCCMapping"],
"StreamKFixupTreeReduction": not state["StreamKFixupTreeReduction"],
"DebugStreamK": not state["DebugStreamK"],
"WorkGroupReduction": not state["WorkGroupReduction"],
"ConvertAfterDS": not state["ConvertAfterDS"],
"ForceDisableShadowInit": not state["ForceDisableShadowInit"],
}
for key, supported in supportedParameters.items():
if supported:
continue
print2(f"ExpertSchedulingMode not supported with {key}={state[key]}")
return 0
# Currently, only the mode that disables VA_VDST and VM_VSRC checks is supported.
return 2
# StinkyTofu expert scheduling mode2 (EnableStinkyTofuESM2) — independent of the rocisa ExpertSchedulingMode rules.
def evaluateStinkyTofuESM2() -> bool:
if not isaInfoMap[isa].archCaps["HasSchedMode"]: return False
if state["ProblemType"]["Sparse"]: return False
# stinkytofu does not yet support f64 (double / double-complex) datatypes
if state["ProblemType"]["MacDataTypeA"].isDouble() or state["ProblemType"]["MacDataTypeA"].isDoubleComplex(): return False
if state["ProblemType"]["MacDataTypeB"].isDouble() or state["ProblemType"]["MacDataTypeB"].isDoubleComplex(): return False
if state["ProblemType"]["ComputeDataType"].isDouble() or state["ProblemType"]["ComputeDataType"].isDoubleComplex(): return False
return True
state["ExpertSchedulingMode"] = evaluateExpertSchedulingMode()
state["EnableStinkyTofuESM2"] = evaluateStinkyTofuESM2()
state["ESMRuntimeGate"] = tuple(state["ISA"])[:2] == (12, 0)
# Some restrictions for float4 and 6bitFloat:
# TODO: remove this if edge and tail are supported for fp4/fp6/bf6
isFloat4 = state["ProblemType"]["DataTypeA"].isFloat4() or state["ProblemType"]["DataTypeB"].isFloat4()
isFloat6 = state["ProblemType"]["DataTypeA"].is6bitFloat() or state["ProblemType"]["DataTypeB"].is6bitFloat()
if isa[:2] == (12, 5) and state["KernelLanguage"] == "Assembly" and (isFloat4 or isFloat6):
if state["ProblemType"]["MacDataTypeA"].isFloat4() or state["ProblemType"]["MacDataTypeB"].isFloat4():
if not state["enableLDSTrA"] and not state["UnrollMajorLDSA"]:
reject(state, printRejectionReason, "Currently FP4 requires LDSTrInst == True for UnrolledMajorLDSA == False")
return
if not state["enableLDSTrB"] and not state["UnrollMajorLDSB"]:
reject(state, printRejectionReason, "Currently FP4 requires LDSTrInst == True for UnrolledMajorLDSB == False")
return
# Currently we only support fp4 edge with AssertFree0(1)ElementMultiple = 2.
# TODO: Enalbe edge with arbitrary number
state["AssertFree0ElementMultiple"] = 2
state["AssertFree1ElementMultiple"] = 2
# Currently we only support fp4 tail-loop with AssertSummationElementMultiple = 8.
# TODO: Enalbe tail-loop with arbitrary number
state["AssertSummationElementMultiple"] = 8
if state["ProblemType"]["MacDataTypeA"].is6bitFloat() or state["ProblemType"]["MacDataTypeB"].is6bitFloat():
state["AssertFree0ElementMultiple"] = 1
state["AssertFree1ElementMultiple"] = 1
state["AssertSummationElementMultiple"] = 16
if not state["ProblemType"]["TransposeA"]:
state["AssertFree0ElementMultiple"] = 16
if state["ProblemType"]["TransposeB"]:
state["AssertFree1ElementMultiple"] = 16
state["AssertSummationElementMultiple"] = max(state["ProblemType"]["MXBlockA"], state["AssertSummationElementMultiple"])
state["AssertSummationElementMultiple"] = max(state["ProblemType"]["MXBlockB"], state["AssertSummationElementMultiple"])
# We have the real "1LDSBuffer" value now, so we have to test the rejection condition here
# TODO-
# On gfx1250, i8, f8, it seem working for 1LDSBuffer=0 "BUT EPS=0", haven't checked for other archs/types, so we still reject by 1LDSBuffer only
# if (state["enableLDSTrA"] or state["enableLDSTrB"]) and (not state["1LDSBuffer"] and state["ExpandPointerSwap"]):
if (state["enableLDSTrA"] or state["enableLDSTrB"]) and (not state["1LDSBuffer"] and state["ExpandPointerSwap"]):
reject(state, printRejectionReason, "Current LDSTrInst implementation does not support 1LDSBuffer=0")
return
@staticmethod
def depthUIteration(
state,
index,
depthuList,
problemType,
isa,
bufferLoad,
packedC0,
packedC1,
printRejectionReason: bool,
isaInfoMap: Dict[IsaVersion, IsaInfo],
rocmVersion: SemanticVersion
):
########################################
# Auto search for DepthU starts here
# Activates when DepthU == -1
########################################
wmmaV3 = isaInfoMap[isa].asmCaps["HasWMMA_V3"]
resetStaggerUStride = state["StaggerUStride"]
resetLocalReadVectorWidth = state["LocalReadVectorWidth"]
resetLocalReadVectorWidthA = state["LocalReadVectorWidthA"]
resetLocalReadVectorWidthB = state["LocalReadVectorWidthB"]
resetGlobalReadVectorWidthA = state["GlobalReadVectorWidthA"]
resetGlobalReadVectorWidthB = state["GlobalReadVectorWidthB"]
tuning = (state.get("SolutionIndex", -1) == -1)
while True:
userDepthU = depthuList[index[0]]
state["StaggerUStride"] = resetStaggerUStride
state["LocalReadVectorWidth"] = resetLocalReadVectorWidth
state["LocalReadVectorWidthA"] = resetLocalReadVectorWidthA
state["LocalReadVectorWidthB"] = resetLocalReadVectorWidthB
state["GlobalReadVectorWidthA"] = resetGlobalReadVectorWidthA
state["GlobalReadVectorWidthB"] = resetGlobalReadVectorWidthB
state["Valid"] = True
########################################
# Initial DepthU
########################################
depthU = userDepthU
depthUA = depthUB = depthUM = depthU
if state["ProblemType"]["Sparse"]:
if state["ProblemType"]["Sparse"] == 2:
depthUB = depthUB // 2
depthUM = depthUB if state["DirectToVgprSparseMetadata"] else depthUB // 4
else:
depthUA = depthUA // 2
depthUM = depthUA if state["DirectToVgprSparseMetadata"] else depthUA // 4
if state["MXScaleFormat"] == "HostPreSwizzle":
mxBlock = state["ProblemType"]["MXBlockA"] or state["ProblemType"]["MXBlockB"]
# MXFP8 subtile (AB_B8): 256B K swizzle tiles → dataDU=128 < DepthU=256.
# MXFP4/BF16 subtile (AB_B4/B16) use a different data layout; keep full DepthU.
enableMultiDu = (
state.get("UseSubtileImpl")
and state.get("_ABTilePairA") == "AB_B8"
and state.get("_ABTilePairB") == "AB_B8"
)
if mxBlock and enableMultiDu:
swizzleSize1 = 256
dataDU = depthU * state["MatrixInstK"] // swizzleSize1
if dataDU < depthU and max(state["MacroTileA"], state["MacroTileB"]) > dataDU:
depthUA = dataDU
depthUB = dataDU
if state["PrefetchGlobalRead"] > 1:
reject(state, printRejectionReason,
f"Multi-DU (DepthU={depthU}, dataDU={dataDU}) is incompatible "
f"with PrefetchGlobalRead={state['PrefetchGlobalRead']}")
state["_DepthU"] = depthU
state["DepthU"] = depthU
state["_DepthUA"] = depthUA# internal — data SRD advance
if state["ProblemType"]["MXBlockA"]:
state["_DepthUMXSA"] = depthU // state["ProblemType"]["MXBlockA"]
state["_DepthUB"] = depthUB# internal — data SRD advance
if state["ProblemType"]["MXBlockB"]:
state["_DepthUMXSB"] = depthU // state["ProblemType"]["MXBlockB"]
state["_DepthUMetadata"] = depthUM# internal
# Runs here (not earlier) because it needs MacroTileA/B and _DepthUA/B,
# which TileInfo reads and which are only set by this point.
if not _validateSubtileGRKPartition(state, printRejectionReason):
return
# fp6 doesn't support LDS padding yet.
for tc in ["A", "B"]:
if state["ProblemType"]["MacDataType%s" % tc].is6bitFloat():
if state["LdsPad%s" % tc] == -1:
state["LdsPad%s" % tc] = 0
if state["LdsBlockSizePerPad%s" % tc] == -1:
state["LdsBlockSizePerPad%s" % tc] = 0
if state["LdsPad%s" % tc] != 0 or state["LdsBlockSizePerPad%s" % tc] != 0:
reject(state, printRejectionReason,
f"fp6 MacDataType{tc}: LdsPad{tc} and LdsBlockSizePerPad{tc} must be 0")
return
iterModeMask = state["TDMIterateMode"]
if state["TDMInst"] and state["EnableMatrixInstruction"] and not state["ProblemType"]["Sparse"]:
# Stage 1: decide iterate-mode per tensor.
if iterModeMask == -1:
state.pop("_TDMIterateModeA", None)
state.pop("_TDMIterateModeB", None)
for tc in ["A", "B"]:
if not state["UnrollMajorLDS%s" % tc]:
continue
vw = state["VectorWidth%s" % tc]
bpe_tc = state["ProblemType"]["MacDataType%s" % tc].numBytes()
lbspp = roundUpToNearestMultiple(int(state["_DepthU%s" % tc] * bpe_tc * vw), 256)
if lbspp > 1024:
state["_TDMIterateMode%s" % tc] = True
# Resolve -1 into the concrete mask for kernel naming. backupValues
# restores -1 before each DepthU candidate, so this overwrite is safe.
state["TDMIterateMode"] = (1 if state.get("_TDMIterateModeA", False) else 0) | \
(2 if state.get("_TDMIterateModeB", False) else 0)
else:
if iterModeMask & 1:
if not state["UnrollMajorLDSA"]:
reject(state, printRejectionReason, "TDMIterateMode bit for A set but UnrollMajorLDSA is False")
return
state["_TDMIterateModeA"] = True
if iterModeMask & 2:
if not state["UnrollMajorLDSB"]:
reject(state, printRejectionReason, "TDMIterateMode bit for B set but UnrollMajorLDSB is False")
return
state["_TDMIterateModeB"] = True
# Stage 2: for non-iterate tensors, halve auto-derived VW until LBSPP
# fits the pad_interval 1024 B limit.
multiple = 256
for tc in ["A", "B"]:
if state.get("_TDMIterateMode%s" % tc, False):
continue
if not state.get("_inputVW%s_was_auto" % tc, False):
continue
if not state["UnrollMajorLDS%s" % tc]:
continue
vw = state["VectorWidth%s" % tc]
if vw <= 1:
continue
tmpBpe = state["ProblemType"]["MacDataType%s" % tc].numBytes()
depthU_tc = state["_DepthU%s" % tc]
origVw = vw
while vw > 1:
candidate = roundUpToNearestMultiple(int(depthU_tc * tmpBpe * vw), multiple)
dwords = candidate // 4
if dwords > 0 and (dwords & (dwords - 1)) == 0:
pad_interval = int(math.log2(dwords)) - 1
if pad_interval <= 7:
break # current VW fits pad_interval encoding
vw //= 2
if vw != origVw:
state["VectorWidth%s" % tc] = vw
# Update derived values that were set from VW before depthUIteration
if state["ProblemType"].get("MXBlock%s" % tc) and not state.get("DirectToVgpr%s" % tc):
state["VectorWidthMXS%s" % tc] = vw
Solution.checkAndAssignWaveSeparateGlobalRead(state, 'A', printRejectionReason)
Solution.checkAndAssignWaveSeparateGlobalRead(state, 'B', printRejectionReason)
if state["ProblemType"]["Sparse"]:
if state["ProblemType"]["Sparse"] == 2:
if not state["DirectToVgprSparseMetadata"]:
Solution.checkAndAssignWaveSeparateGlobalRead(state, 'Metadata', printRejectionReason)
else:
if not state["DirectToVgprSparseMetadata"]:
Solution.checkAndAssignWaveSeparateGlobalRead(state, 'Metadata', printRejectionReason)
# Set up stagger shift:
bpeAB = state["ProblemType"]["DataType"].numBytes()
# (1<<staggerStrideShift) is number of loop iterations to traverse the stride
if state["StaggerU"] == 0:
state["StaggerUMapping"] = 0
state["StaggerUStride"] = 0
if state["StaggerUStride"] == -1 or state["StaggerUStride"] < (state["DepthU"] * bpeAB):
# (StaggerUStride) shoud be greater than or equal to (DepthU * bpeAB)
target_stagger_stride = state["DepthU"] * bpeAB
# Assert that it is an int or a float with no fractional part
is_integer = isinstance(target_stagger_stride, int) \
or (isinstance(target_stagger_stride, float) and target_stagger_stride.is_integer())
assert is_integer, "StaggerUStride must be an int or a float with no fractional part"
state["StaggerUStride"] = int(target_stagger_stride)
state["_staggerStrideShift"] = 0
else:
state["_staggerStrideShift"] = (int)(math.ceil(math.log(state["StaggerUStride"] / (state["DepthU"] * bpeAB), 2)))
def calcLdsPad(isaInfoMap: Dict[str, IsaInfo]) -> Tuple[int, int, int, int, int]:
# SubtileImpl: LDS padding is disabled.
# gfx950 subtile uses software swizzle+rotation for bank conflict avoidance instead.
# gfx1250 subtile (TDM) currently has no bank conflict mitigation -- TDM hardware
# padding could be enabled here in the future by computing non-zero LdsPad values.
if state["UseSubtileImpl"]:
return 0, 0, 0, 0, 0
numBytesA = state["ProblemType"]["MacDataTypeA"].numBytes()
numBytesB = state["ProblemType"]["MacDataTypeB"].numBytes()
lrvwA = state["LocalReadVectorWidthA"]
lrvwB = state["LocalReadVectorWidthB"]
ldsPadA = state["LdsPadA"]
ldsPadB = state["LdsPadB"]
ldsPadM = state["LdsPadMetadata"]
optPadA = lrvwA
optPadB = lrvwB
readRegsA = int(lrvwA * state["ProblemType"]["MacDataTypeA"].numBytes() // 4)
readRegsB = int(lrvwB * state["ProblemType"]["MacDataTypeB"].numBytes() // 4)
if state["ProblemType"]["Sparse"]:
if state["ProblemType"]["Sparse"] == 2:
optPadB //= 2
readRegsB //= 2
else:
optPadA //= 2
readRegsA //= 2
if (not isaInfoMap[isa].asmCaps['HasWMMA']) and (readRegsA > 6 or readRegsB > 6):
reject(state, "LocalReadVectorWidth results in attempting to read LDS larger than b192, reject")
return ldsPadA, ldsPadB, ldsPadM, 0, 0
if state["EnableMatrixInstruction"]:
# MI16x16xNx1 needs double padding to avoid bank conflict:
# WMMA_V3 doubles when readRegs == 2; other ISAs double when readRegs in {1, 4}.
if state["MatrixInstB"] == 1 and state["MatrixInstM"] == 16:
if wmmaV3:
if readRegsA == 2: optPadA *= 2
if readRegsB == 2: optPadB *= 2
else:
if readRegsA in (1, 4): optPadA *= 2
if readRegsB in (1, 4): optPadB *= 2
def calcLdsPadPerOperand(tc: str, idx: int, numBytes: float, lrvw: int, optPad: int) -> int:
"""Auto-resolve LdsPad{tc} (tc in {A, B}) when the user left it on -1.
Order of overrides is intentional: later writes can clobber earlier
ones (e.g. the gfx1250 solver path can replace the MatrixInstM=16
formula, and the DirectToLds branch overrides everything that came
before).
"""
mt = state[f"MacroTile{idx}"]
vw = state[f"VectorWidth{tc}"]
grvw = state[f"GlobalReadVectorWidth{tc}"]
ldstr = state.get(f"enableLDSTr{tc}", False)
macDtype = state["ProblemType"][f"MacDataType{tc}"]
tlu = state["ProblemType"][f"TLU{tc}"]
ldsPad = 0
if not state[f"UnrollMajorLDS{tc}"]:
if state["EnableMatrixInstruction"]:
if state["MatrixInstB"] == 1 and state["MatrixInstM"] == 16:
ldsPad = int(((16 * vw * numBytes + mt * numBytes * lrvw) % 128) // numBytes)
if grvw * numBytes == 32 and ldsPad == 0:
ldsPad = int(16 // numBytes)
if wmmaV3:
# MIWaveTile / MIWaveGroup are only populated for MFMA/WMMA
# kernels; reading them outside this guard breaks non-MI dtypes
# (e.g. FP64) where the keys are absent.
miwt = state["MIWaveTile"][idx]
miwg = state["MIWaveGroup"][idx]
if macDtype.numBytes() == 0.5 and ldstr:
ldsPad = get_fp4_mt_config(mt, "pad", miwt, miwg)
elif macDtype.is8bitFloat() and ldstr:
ldsPad = get_fp8_mt_config(mt, "pad", miwt, miwg)
elif macDtype.numBytes() == 2 and ldstr:
ldsPad = get_fp16_mt_config(mt, "pad", miwg,
miInputPerThUnroll=state["MIInputPerThread"],
lrvw=state[f"LocalReadVectorWidth{tc}"],
miWaveTile=miwt,
vw=vw)
elif macDtype.numBytes() == 4:
ldsPad = get_fp32_mt_config(mt, "pad",
vw, state[f"LocalReadVectorWidth{tc}"], miwg,
miInputPerThread=state["MIInputPerThread"],
miWaveTile=miwt,
xf32EmuPack=state.get("UseF32XEmulation", False))
if state[f"DirectToLds{tc}"]:
# TODO: Check if there are cases which benefit from padding, currently set to zero by default
ldsPad = state["MatrixInstM"] if ldstr else 0
else: # mac instruction
ldsPad = 0 if tlu else vw
else:
if state[f"DirectToLds{tc}"]:
if not tlu:
bpe = state["ProblemType"][f"DataType{tc}"].numBytes()
LdsStride = vw * bpe * state["DepthU"]
MinLdsBlockSizePerPad = grvw * bpe * state["WavefrontSize"]
isM0PadEnough = LdsStride >= MinLdsBlockSizePerPad
ldsPad = state["MatrixInstK"] if bpe == 2 and not isM0PadEnough else 2 * lrvw
else:
ldsPad = 0
else:
ldsPad = max(grvw, optPad)
assert ldsPad >= 0
return ldsPad
if ldsPadA == -1:
ldsPadA = calcLdsPadPerOperand("A", 0, numBytesA, lrvwA, optPadA)
if ldsPadB == -1:
ldsPadB = calcLdsPadPerOperand("B", 1, numBytesB, lrvwB, optPadB)
if state["ProblemType"]["Sparse"] and not state["DirectToVgprSparseMetadata"]:
optPadM = (optPadB if state["ProblemType"]["Sparse"] == 2 else optPadA) // 4
grvwM = (state["GlobalReadVectorWidthB"] if state["ProblemType"]["Sparse"] == 2 else state["GlobalReadVectorWidthA"]) // 4
vwM = (state["VectorWidthB"] if state["ProblemType"]["Sparse"] == 2 else state["VectorWidthA"]) // 4
if ldsPadM == -1:
ldsPadM = 0
if not state["ProblemType"]["TLUMetadata"]:
if state["EnableMatrixInstruction"] and state["TransposeLDSMetadata"]:
ldsPadM = max(grvwM, optPadM)
else:
ldsPadM = vwM
## turn-off padding for directToLds
if state["EnableMatrixInstruction"] and state["TransposeLDSMetadata"] and state["DirectToLdsMetadata"]:
ldsPadM = 0
assert(ldsPadM >= 0)
def removeLdsPadLogicForDTL(tc, ldsPad):
ret = ldsPad
miwt = state["MIWaveTile%s"%tc]
# If TLU = 1 and not using LDSTR, lds read is contiguous so no padding needed
if state["ProblemType"]["TLU%s"%tc] and (not state["enableLDSTr%s"%tc]):
ret = 0
if state["ProblemType"]["TLU%s"%tc] and state["enableLDSTr%s"%tc] \
and (miwt & (miwt-1)) != 0 and state["UseGeneralizedNLCOne%s"%tc]:
ret = 0
return ret
if state["DirectToLdsA"]:
ldsPadA = removeLdsPadLogicForDTL('A', ldsPadA)
if state["DirectToLdsB"]:
ldsPadB = removeLdsPadLogicForDTL('B', ldsPadB)
# set ldsPadA,B=0 for DirectToVgpr
if state["DirectToVgprA"]:
ldsPadA = 0
if state["DirectToVgprB"]:
ldsPadB = 0
def calcLdsPadPerMxOperand(tc: str) -> int:
"""Auto-resolve LdsPad{tc} (tc in {MXSA, MXSB}) when user left it on -1."""
if state[f"LdsPad{tc}"] != -1:
return state[f"LdsPad{tc}"]
sub = tc[-1] # "A" or "B"
if not state["ProblemType"][f"MXBlock{sub}"]:
return 0
if wmmaV3:
return get_mxs_mt_config(state["MatrixInstK"],
state["ProblemType"][f"MXBlock{sub}"],
state[f"VectorWidth{sub}"], "pad")
return 0
ldsPadMXSA = calcLdsPadPerMxOperand("MXSA")
ldsPadMXSB = calcLdsPadPerMxOperand("MXSB")
if state["TDMInst"]:
pads = {"A": ldsPadA * state["ProblemType"]["MacDataTypeA"].numBytes(), "B": ldsPadB * state["ProblemType"]["MacDataTypeB"].numBytes(), "MXSA": ldsPadMXSA, "MXSB": ldsPadMXSB}
for tc, val in pads.items():
if val == 0: continue
pad_amount = TensorDataMoverLoad.calPadAmount(val)
if pad_amount > 127:
reject(state, printRejectionReason, f"pad_amount=(ldsPad//4-1)={pad_amount} should be smaller than or equal to 127 for ldsPad{tc}={val}")
return ldsPadA, ldsPadB, ldsPadM, ldsPadMXSA, ldsPadMXSB
def checkLdsBlockSizePerPadForTDM(ldsBlockSizePerPadA: int, ldsBlockSizePerPadB: int, ldsBlockSizePerPadMXSA: int, ldsBlockSizePerPadMXSB: int):
if state["TDMInst"]:
pads = {"A": ldsBlockSizePerPadA, "B": ldsBlockSizePerPadB, "MXSA": ldsBlockSizePerPadMXSA, "MXSB": ldsBlockSizePerPadMXSB}
for tc, val in pads.items():
# A/B in iterate-mode bypass the pad_interval encoding; skip their
# check. MXSA/MXSB do not support iterate-mode, so their LBSPP
# must still satisfy the pad_interval constraints.
if tc in ("A", "B") and state.get("_TDMIterateMode%s" % tc, False):
if val == 0:
reject(state, printRejectionReason,
f"TDMIterateMode set for {tc} but LdsBlockSizePerPad{tc}=0; "
f"iterate-mode needs a non-zero pad block.")
return
continue
if val == 0: continue
dwords = val // 4
if dwords == 0 or (dwords & (dwords - 1)) != 0:
reject(state, printRejectionReason, f"LdsBlockSizePerPad{tc}={val}: val//4={dwords} must be a positive power of 2 for TDM hardware encoding")
return
pad_interval = TensorDataMoverLoad.calPadInterval(val)
if pad_interval > 7:
reject(state, printRejectionReason,
f"LdsBlockSizePerPad{tc}={val} exceeds TDM padding (1024B). "
f"Set TDMIterateMode or reduce DepthU / VectorWidth.")
def calcMXSLdsBlockSizePerPad(tc: str, lrvw: int) -> int:
LdsBlockSizePerPad = state["LdsBlockSizePerPad%s"%tc]
if wmmaV3:
if LdsBlockSizePerPad != -1:
return LdsBlockSizePerPad
subTc = tc[3]
mxBlock = state["ProblemType"]["MXBlock%s"%subTc]
if not mxBlock:
return 0
return get_mxs_mt_config(state["MatrixInstK"], mxBlock,
state["VectorWidth%s"%subTc], "perBlock")
return 0
def getLdsBpe(tc: str) -> float:
return state["ProblemType"]["DataType%s"%tc].numBytes() if state["ConvertAfterDS"] else state["ProblemType"]["MacDataType%s"%tc].numBytes()
def calcLdsBlockSizePerPad(tc: str, lrvw: int) -> int:
if "MXS" in tc:
return calcMXSLdsBlockSizePerPad(tc, lrvw)
mt = state["MacroTile0"] if ("A" in tc) else state["MacroTile1"]
LdsBlockSizePerPad = state["LdsBlockSizePerPad%s"%tc]
tmpBpe = getLdsBpe(tc)
multiple = 256 if wmmaV3 else 128
if LdsBlockSizePerPad == -1:
if state["EnableMatrixInstruction"] and tmpBpe != 0.75:
if state["UnrollMajorLDS%s"%tc]:
LdsBlockSizePerPad = roundUpToNearestMultiple(int(state["_DepthU%s"%tc] * tmpBpe), multiple)
if state["_DepthU%s"%tc] * tmpBpe * state["VectorWidth%s"%tc] > multiple:
LdsBlockSizePerPad = roundUpToNearestMultiple(int(state["_DepthU%s"%tc] * tmpBpe * state["VectorWidth%s"%tc]), multiple)
else:
if state["MatrixInstB"] == 1 and state["MatrixInstM"] == 16:
LdsBlockSizePerPad = int(mt * tmpBpe * lrvw)
if wmmaV3:
miWaveTileIdx = 0 if "A" in tc else 1
ldsType = state["ProblemType"]["DataType%s"%tc] if state["ConvertAfterDS"] else state["ProblemType"]["MacDataType%s"%tc]
miwt = state["MIWaveTile"][miWaveTileIdx]
miwg = state["MIWaveGroup"][miWaveTileIdx]
if tmpBpe == 0.5 and state.get("enableLDSTr%s"%tc, False):
LdsBlockSizePerPad = get_fp4_mt_config(mt, "perBlock", miwt, miwg)
elif tmpBpe == 1 and ldsType.is8bitFloat() and state.get("enableLDSTr%s"%tc, False):
LdsBlockSizePerPad = get_fp8_mt_config(mt, "perBlock", miwt, miwg)
elif tmpBpe == 2 and state.get("enableLDSTr%s"%tc, False):
LdsBlockSizePerPad = get_fp16_mt_config(mt, "perBlock", miwg,
miInputPerThUnroll=state["MIInputPerThread"],
lrvw=lrvw,
miWaveTile=miwt,
vw=state[f"VectorWidth{tc}"])
elif tmpBpe == 4:
LdsBlockSizePerPad = get_fp32_mt_config(mt, "perBlock",
state[f"VectorWidth{tc}"], lrvw, miwg,
miInputPerThread=state["MIInputPerThread"],
miWaveTile=miwt,
xf32EmuPack=state.get("UseF32XEmulation", False))
else:
LdsBlockSizePerPad = 0
else:
LdsBlockSizePerPad = 0
# set LdsBlockSizePerPad,B=0 for DirectToVgpr
if state["DirectToVgpr%s"%tc]:
LdsBlockSizePerPad = 0
if state["DirectToLds%s"%tc]:
bpeA = state["ProblemType"]["DataType%s"%tc].numBytes()
# For DTL lds padding must be a multiple of the instruction load size (in bytes)
# Also, LBSPP must be less than or equal to GRVW*bpe*NumThreads (cannot handle LBSPP larger than m0 inc value)
MinLdsBlockSizePerPad = int((state[f"GlobalReadVectorWidth%s"%tc] * bpeA) * state["WavefrontSize"])
MaxLdsBlockSizePerPad = int((state[f"GlobalReadVectorWidth%s"%tc] * bpeA) * state["NumThreads"])
if state["UseGeneralizedNLCOne%s"%tc]:
LdsBlockSizePerPad = MinLdsBlockSizePerPad
else:
LdsBlockSizePerPad = max(LdsBlockSizePerPad, MinLdsBlockSizePerPad)
LdsBlockSizePerPad = min(LdsBlockSizePerPad, MaxLdsBlockSizePerPad)
LdsBlockSizePerPad = roundUpToNearestMultiple(LdsBlockSizePerPad, MinLdsBlockSizePerPad)
return int(LdsBlockSizePerPad)
def calcLdsNumBytesAB(mxTc: str, ldsPad: int, LdsBlockSizePerPad: int):
tc = mxTc.replace("MXS", "")
if ("MXS" in mxTc) and (state["ProblemType"]["MXBlock%s"%tc] == 0):
return (0, 0)
if state["DirectToVgpr%s"%mxTc]:
return (0, 0)
bpe = state["ProblemType"]["DataType%s"%tc].numBytes() if state["ConvertAfterDS"] else state["ProblemType"]["MacDataType%s"%tc].numBytes()
bpe = 1 if ("MXS" in mxTc) else bpe
ldsAlign = int(64 / state["ProblemType"]["MacDataType%s"%tc].numRegisters())
ldsAlign = 64 if state["ProblemType"]["MacDataType%s"%tc].is6bitFloat() else ldsAlign
if state["UnrollMajorLDS%s"%mxTc]:
ldsNumBytes = int((state["_DepthU%s"%mxTc] + ldsPad) * state["MacroTile%s"%mxTc] * bpe)
else:
ldsNumBytes = int(state["_DepthU%s"%mxTc] * (state["MacroTile%s"%mxTc] + ldsPad) * bpe)
padInterval = LdsBlockSizePerPad
if padInterval != 0:
ldsNumBytes = int((state["_DepthU%s"%mxTc] * state["MacroTile%s"%mxTc] * bpe) / padInterval * (padInterval + ldsPad * bpe))
ldsNumBytesAligned = roundUpToNearestMultiple(ldsNumBytes, ldsAlign)
if state["DirectToVgpr%s"%mxTc]:
ldsNumBytes = 0
ldsNumBytesAligned = 0
return (ldsNumBytes, ldsNumBytesAligned)
def calcLdsNumBytesM():
if state["ProblemType"]["Sparse"] and not state["DirectToVgprSparseMetadata"]:
ldsAlign = int(64 / state["ProblemType"]["DataType"].numRegisters())
ldsAlign = 64 if state["ProblemType"]["DataType"].is6bitFloat() else ldsAlign
bpeAB = state["ProblemType"]["DataType"].numBytes()
if state["UnrollMajorLDSMetadata"]:
ldsNumBytesMetadata = (state["_DepthUMetadata"] + state["LdsPadMetadata"]) * state["MacroTileMetadata"]
else:
ldsNumBytesMetadata = state["_DepthUMetadata"] * (state["MacroTileMetadata"] + state["LdsPadMetadata"])
ldsNumBytesMetadata = roundUp(ldsNumBytesMetadata / bpeAB) # metadata is in byte type. so divide ldsNumBytesMetadata by A,B's bpe
padInterval = state["LdsBlockSizePerPadMetadata"]
if padInterval != 0:
ldsNumBytesMetadata = int(roundUp(state["_DepthUMetadata"] * state["MacroTileMetadata"] / bpeAB) / padInterval * (padInterval + state["LdsPadMetadata"]))
ldsNumBytesAlignedMetadata = int(roundUpToNearestMultiple(ldsNumBytesMetadata, ldsAlign) * bpeAB)
ldsNumBytesMetadata = int(ldsNumBytesMetadata * bpeAB)
else:
ldsNumBytesMetadata = 0
ldsNumBytesAlignedMetadata = 0
return ldsNumBytesMetadata, ldsNumBytesAlignedMetadata
if state["LocalReadVectorWidthA"] == -1:
state["LocalReadVectorWidthA"] = state["LocalReadVectorWidth"]
if (not state["enableLDSTrA"]) and (state["ProblemType"]["Sparse"] == 1):
state["LocalReadVectorWidthA"] = min(state["LocalReadVectorWidthA"], state["MIInputPerThreadA"])
if state["LocalReadVectorWidthB"] == -1:
state["LocalReadVectorWidthB"] = state["LocalReadVectorWidth"]
if (not state["enableLDSTrB"]) and (state["ProblemType"]["Sparse"] == 2):
state["LocalReadVectorWidthB"] = min(state["LocalReadVectorWidthB"], state["MIInputPerThreadB"])
def calLRVW():
# Default LocalReadVectorWidth
if state["EnableMatrixInstruction"]:
# Default LocalReadVectorWidth
autoLRVWA = False
maxNumDsLoadBytesA = Solution.MAX_NUM_DS_LOAD_BYTES
maxLRVWA = int(maxNumDsLoadBytesA // state["ProblemType"]["MacDataTypeA"].numBytes())
# Set maxLRVW to 32 for 6 bits float: use two load instructions b128(4 vgpr) and b64(2 vgpr) to mimic b192
if isaInfoMap[isa].asmCaps["HasWMMA_f8f6f4"] and state["ProblemType"]["MacDataTypeA"].numBytes() == 0.75:
maxLRVWA = 32
if state["LocalReadVectorWidthA"] == -1:
autoLRVWA = True
if state["TransposeLDS"] or (state["MIInputPerThread"] * state["ProblemType"]["MacDataTypeA"].numBytes() > maxNumDsLoadBytesA):
state["LocalReadVectorWidthA"] = maxLRVWA
else:
state["LocalReadVectorWidthA"] = min(state["MIInputPerThreadA"], maxLRVWA)
if state["LocalReadVectorWidthA"] > maxLRVWA:
raise RuntimeError("LocalReadVectorWidthA (%d) exceeds max %d (# bytes of lrvw > 32)" \
% (state["LocalReadVectorWidthA"], maxLRVWA))
else:
if isaInfoMap[isa].asmCaps["HasWMMA_V3"]:
if state["LocalReadVectorWidthA"] != maxLRVWA and state["TransposeLDS"]:
reject(state, printRejectionReason, f"gfx1250 requires lrvwA == {maxLRVWA} for datatype {state['ProblemType']['MacDataTypeA']}, actual value: {state['LocalReadVectorWidthA']}")
if state["ProblemType"]["Sparse"]:
if state["LocalReadVectorWidthA"] * state["ProblemType"]["MacDataTypeA"].numBytes() > maxNumDsLoadBytesA:
reject(state, printRejectionReason, "LocalReadVectorWidthA(%d) * BytePerMacDataTypeA(%s) > %d bytes." % (state["LocalReadVectorWidthA"], state["ProblemType"]["MacDataTypeA"].numBytes(), maxNumDsLoadBytesA))
elif not state["ProblemType"]["Sparse"] and not state["UseF32XEmulation"] and not(state["ProblemType"]["MacDataTypeA"].is8bitFloat() and (state["MatrixInstK"] in [64, 128,])):
if state["LocalReadVectorWidthA"] < state["MIInputPerThread"] and not state["LDSTrInst"] and not isaInfoMap[isa].asmCaps["HasWMMA_V3"]:
reject(state, printRejectionReason, "LocalReadVectorWidthA < %u" %(state["MIInputPerThread"])) # << Rejected here
if state["LocalReadVectorWidthA"] > state["MIInputPerThread"] and not state["TransposeLDS"]:
reject(state, printRejectionReason, "LocalReadVectorWidth require Transpose LDS")
if autoLRVWA:
if state["LocalReadVectorWidthA"] // state["MIInputPerThreadA"] > 1:
if (state["DepthU"] // state["MatrixInstK"] <= state["LocalReadVectorWidthA"] // state["MIInputPerThreadA"]):
# if only have 1 iteration with wider local read, reduce LRVW to have better scheduling (at least 2 iterations)
state["LocalReadVectorWidthA"] //= 2
# Default LocalReadVectorWidth
autoLRVWB = False
maxNumDsLoadBytesB = Solution.MAX_NUM_DS_LOAD_BYTES
maxLRVWB = int(maxNumDsLoadBytesB // state["ProblemType"]["MacDataTypeB"].numBytes())
# Set maxLRVW to 32 for 6 bits float: use two load instructions b128(4 vgpr) and b64(2 vgpr) to mimic b192
if isaInfoMap[isa].asmCaps["HasWMMA_f8f6f4"] and state["ProblemType"]["MacDataTypeB"].numBytes() == 0.75:
maxLRVWB = 32
if state["LocalReadVectorWidthB"] == -1:
autoLRVWB = True
if state["TransposeLDS"] or (state["MIInputPerThread"] * state["ProblemType"]["MacDataTypeB"].numBytes() > maxNumDsLoadBytesB):
state["LocalReadVectorWidthB"] = maxLRVWB
else:
state["LocalReadVectorWidthB"] = min(state["MIInputPerThreadB"], maxLRVWB)
if state["LocalReadVectorWidthB"] > maxLRVWB:
raise RuntimeError("LocalReadVectorWidthB (%d) exceeds max %d (# bytes of lrvw > 32)" \
% (state["LocalReadVectorWidthB"], maxLRVWB))
else:
if isaInfoMap[isa].asmCaps["HasWMMA_V3"]:
if state["LocalReadVectorWidthB"] != maxLRVWB and state["TransposeLDS"]:
reject(state, printRejectionReason, f"gfx1250 requires lrvwB == {maxLRVWB} for MacDataTypeB {state['ProblemType']['MacDataTypeB']}, actual value: {state['LocalReadVectorWidthB']}")
# TODO: Find better conditons to filter gfx1250 solutions
if state["ProblemType"]["Sparse"]:
if state["LocalReadVectorWidthB"] * state["ProblemType"]["MacDataTypeB"].numBytes() > maxNumDsLoadBytesB:
reject(state, printRejectionReason, "LocalReadVectorWidthB(%d) * BytePerMacDataTypeB(%s) > %d bytes." % (state["LocalReadVectorWidthB"], state["ProblemType"]["MacDataTypeB"].numBytes(), maxNumDsLoadBytesB))
elif not state["ProblemType"]["Sparse"] and not state["UseF32XEmulation"] and not(state["ProblemType"]["MacDataTypeB"].is8bitFloat() and (state["MatrixInstK"] in [64, 128,])):
if state["LocalReadVectorWidthB"] < state["MIInputPerThread"] and not state["LDSTrInst"] and not isaInfoMap[isa].asmCaps["HasWMMA_V3"]:
reject(state, printRejectionReason, "LocalReadVectorWidthB < %u" %(state["MIInputPerThread"]))
if state["LocalReadVectorWidthB"] > state["MIInputPerThread"] and not state["TransposeLDS"]:
reject(state, printRejectionReason, "LocalReadVectorWidthB require Transpose LDS")
if autoLRVWB:
if state["LocalReadVectorWidthB"] // state["MIInputPerThreadB"] > 1:
if (state["DepthU"] // state["MatrixInstK"] <= state["LocalReadVectorWidthB"] // state["MIInputPerThreadB"]):
# if only have 1 iteration with wider local read, reduce LRVW to have better scheduling (at least 2 iterations)
state["LocalReadVectorWidthB"] //= 2
if autoLRVWA or autoLRVWB:
wlrA = max(state["LocalReadVectorWidthA"] // state["MIInputPerThread"], 1)
wlrB = max(state["LocalReadVectorWidthB"] // state["MIInputPerThread"], 1)
if (wlrA > 1) or (wlrB > 1):
padA, padB, padM, padMXSA, padMXSB = calcLdsPad(isaInfoMap)
ldsBlockSizePerPadA = calcLdsBlockSizePerPad("A", state["LocalReadVectorWidthA"])
ldsBlockSizePerPadB = calcLdsBlockSizePerPad("B", state["LocalReadVectorWidthB"])
ldsBlockSizePerPadMXSA = calcLdsBlockSizePerPad("MXSA", state["LocalReadVectorWidthMXS"]) if state["ProblemType"]["MXBlockA"] else 0
ldsBlockSizePerPadMXSB = calcLdsBlockSizePerPad("MXSB", state["LocalReadVectorWidthMXS"]) if state["ProblemType"]["MXBlockB"] else 0
ldsBlockSizePerPadA = 0 if padA == 0 else ldsBlockSizePerPadA
ldsBlockSizePerPadB = 0 if padB == 0 else ldsBlockSizePerPadB
ldsBlockSizePerPadMXSA = 0 if padMXSA == 0 else ldsBlockSizePerPadMXSA
ldsBlockSizePerPadMXSB = 0 if padMXSB == 0 else ldsBlockSizePerPadMXSB
checkLdsBlockSizePerPadForTDM(ldsBlockSizePerPadA, ldsBlockSizePerPadB, ldsBlockSizePerPadMXSA, ldsBlockSizePerPadMXSB)
(ldsNumBytesA, ldsNumBytesAlignedA) = calcLdsNumBytesAB("A", padA, ldsBlockSizePerPadA)
(ldsNumBytesB, ldsNumBytesAlignedB) = calcLdsNumBytesAB("B", padB, ldsBlockSizePerPadB)
(ldsNumBytesMXSA, ldsNumBytesAlignedMXSA) = calcLdsNumBytesAB("MXSA", padMXSA, ldsBlockSizePerPadMXSA) if state["ProblemType"]["MXBlockA"] else (0, 0)
(ldsNumBytesMXSB, ldsNumBytesAlignedMXSB) = calcLdsNumBytesAB("MXSB", padMXSB, ldsBlockSizePerPadMXSB) if state["ProblemType"]["MXBlockB"] else (0, 0)
(ldsNumBytesMetadata, ldsNumBytesAlignedMetadata) = calcLdsNumBytesM()
if (ldsNumBytesAlignedA + ldsNumBytesAlignedB) > state["MaxLDS"]:
if wlrA > 1:
state["LocalReadVectorWidthA"] //= 2
if wlrB > 1:
state["LocalReadVectorWidthB"] //= 2
wlrA = max(state["LocalReadVectorWidthA"] // state["MIInputPerThreadA"], 1)
wlrB = max(state["LocalReadVectorWidthB"] // state["MIInputPerThreadB"], 1)
if wlrA > wlrB:
state["LocalReadVectorWidthA"] = wlrB * state["MIInputPerThreadA"]
if wlrA < wlrB:
state["LocalReadVectorWidthB"] = wlrA * state["MIInputPerThreadB"]
if state["ProblemType"]["Sparse"] == 1:
state["LocalReadVectorWidthMetadata"] = state["LocalReadVectorWidthA"]
elif state["ProblemType"]["Sparse"] == 2:
state["LocalReadVectorWidthMetadata"] = state["LocalReadVectorWidthB"]
if state["enableGLTrA"] or state["enableGLTrB"]:
state["LocalReadVectorWidth"] = 8
state["LocalReadVectorWidthA"] = 8
state["LocalReadVectorWidthB"] = 8
else:
if state["UseDotInstruction"]:
# dot2: LRVW should be equal to NumDotElements * InnerUnroll
if state["LocalReadVectorWidth"] not in [-1, state["NumDotElements"] * state["InnerUnroll"]]:
reject(state, printRejectionReason, "dot kernel requires LocalReadVectorWidth = NumDotElements(%u) * InnerUnroll(%u)" \
% (state["NumDotElements"], state["InnerUnroll"]))
return
state["LocalReadVectorWidth"] = state["NumDotElements"] * state["InnerUnroll"]
state["LocalReadVectorWidthA"] = state["NumDotElements"] * state["InnerUnroll"]
state["LocalReadVectorWidthB"] = state["NumDotElements"] * state["InnerUnroll"]
else: # mac
if state["LocalReadVectorWidth"] == -1:
state["LocalReadVectorWidth"] = state["VectorWidthA"]
if state["LocalReadVectorWidth"] != state["VectorWidthA"] or \
state["LocalReadVectorWidth"] != state["VectorWidthB"]:
reject(state, printRejectionReason, "LocalReadVectorWidth must equal VectorWidthA/B for MAC kernels")
if state["LocalReadVectorWidthA"] == -1:
state["LocalReadVectorWidthA"] = state["VectorWidthA"]
if state["LocalReadVectorWidthB"] == -1:
state["LocalReadVectorWidthB"] = state["VectorWidthB"]
def calLRVWFor950MX():
# Determine if we need to infer LRVW. Returns True if,
# - state["LocalReadVectorWidth{tc}"] is -1, and,
# - state["ProblemType"]["MacDataType{tc}"] is not 6-bit float
# If the LRVW is set by the user, validate the configuration and rejects if,
# - state["LocalReadVectorWidth{tc}"] * state["ProblemType"]["MacDataType{tc}"].numRegisters() < 1 if not sparse
# - state["LocalReadVectorWidth{tc}"] // 2 * state["ProblemType"]["MacDataType{tc}"].numRegisters() < 1 is sparse
# - state["LocalReadVectorWidth{tc}"] > state["MIInputPerThread"] and LDS is not transposed
def isAutoLRVW(tc) -> bool:
autoLRVW = False
if state[f"LocalReadVectorWidth{tc}"] != -1:
tmplrvw = (state[f"LocalReadVectorWidth{tc}"] // 2) if state["ProblemType"]["Sparse"] else state[f"LocalReadVectorWidth{tc}"]
if tmplrvw * state["ProblemType"][f"MacDataType{tc}"].numRegisters() < 1:
reject(state, "LocalReadVectorWidth * dataRegister < 1")
if state[f"LocalReadVectorWidth{tc}"] > state["MIInputPerThread"] and not state["TransposeLDS"]:
reject(state, "LocalReadVectorWidth require Transpose LDS")
else:
if state["ProblemType"][f"MacDataType{tc}"].is6bitFloat():
state[f"LocalReadVectorWidth{tc}"] = 32 if state[f"UnrollMajorLDS{tc}"] else 16
else:
autoLRVW = True
if state["TransposeLDS"] and (not state[f"DirectToLds{tc}"]):
state[f"LocalReadVectorWidth{tc}"] = int(16 // state["ProblemType"][f"MacDataType{tc}"].numBytes())
else:
if state["ProblemType"]["Sparse"] and state["MIInputPerThread"] * state["ProblemType"][f"MacDataType{tc}"].numBytes() > 16:
state[f"LocalReadVectorWidth{tc}"] = int(16 // state["ProblemType"][f"MacDataType{tc}"].numBytes())
else:
state[f"LocalReadVectorWidth{tc}"] = state["MIInputPerThread"]
if state[f"LocalReadVectorWidth{tc}"] // state["MIInputPerThread"] > 1:
if (state["DepthU"] // state["MatrixInstK"] <= state[f"LocalReadVectorWidth{tc}"] // state["MIInputPerThread"]):
# if only have 1 iteration with wider local read, reduce LRVW to have better scheduling (at least 2 iterations)
state[f"LocalReadVectorWidth{tc}"] //= 2
return autoLRVW
if state["EnableMatrixInstruction"]:
autoLRVWA = isAutoLRVW("A")
autoLRVWB = isAutoLRVW("B")
if autoLRVWA or autoLRVWB:
wlrA = max(state["LocalReadVectorWidthA"] // state["MIInputPerThread"], 1)
wlrB = max(state["LocalReadVectorWidthB"] // state["MIInputPerThread"], 1)
if (wlrA > 1) or (wlrB > 1):
padA, padB, padM = calcLdsPad(state["LocalReadVectorWidth"], isaInfoMap)
ldsBlockSizePerPadA, ldsBlockSizePerPadB = calcLdsBlockSizePerPad(state["LocalReadVectorWidth"])
ldsNumBytesA, ldsNumBytesAlignedA, ldsNumBytesB, ldsNumBytesAlignedB, ldsNumBytesMetadata, ldsNumBytesAlignedMetadata, \
ldsNumBytesMXSA, ldsNumBytesAlignedMXSA, ldsNumBytesMXSB, ldsNumBytesAlignedMXSB \
= calcLdsNumBytes(padA, ldsBlockSizePerPadA, padB, ldsBlockSizePerPadB)
ldsNumBytes = ldsNumBytesAlignedA + ldsNumBytesAlignedB + \
ldsNumBytesAlignedMXSA + ldsNumBytesAlignedMXSB + \
ldsNumBytesAlignedMetadata
if ldsNumBytes > state["MaxLDS"]:
if wlrA > 1:
state["LocalReadVectorWidthA"] //= 2
if wlrB > 1:
state["LocalReadVectorWidthB"] //= 2
wlrA = max(state["LocalReadVectorWidthA"] // state["MIInputPerThreadA"], 1)
wlrB = max(state["LocalReadVectorWidthB"] // state["MIInputPerThreadB"], 1)
if wlrA > wlrB:
state["LocalReadVectorWidthA"] = wlrB * state["MIInputPerThreadA"]
if wlrA < wlrB:
state["LocalReadVectorWidthB"] = wlrA * state["MIInputPerThreadB"]
if state["ProblemType"]["Sparse"] == 1:
state["LocalReadVectorWidthMetadata"] = state["LocalReadVectorWidthA"]
elif state["ProblemType"]["Sparse"] == 2:
state["LocalReadVectorWidthMetadata"] = state["LocalReadVectorWidthB"]
if state["ProblemType"]["MXBlockA"]:
state["LocalReadVectorWidthMXSA"] = 1 # TODO: check if need to fomulization
if state["ProblemType"]["MXBlockB"]:
state["LocalReadVectorWidthMXSB"] = 1 # TODO: check if need to fomulization
else:
reject(state, printRejectionReason, "expecting MFMA for MX datatypes")
return
# Default LocalReadVectorWidth
if state["ISA"] == IsaVersion(9,5,0) and (state["ProblemType"]["MXBlockA"] or state["ProblemType"]["MXBlockB"]):
calLRVWFor950MX()
else:
calLRVW()
def calcOptGRVW(lrvw: int, unrollMajorLDS: bool, datatype: DataType) -> int:
# with UnrollMajorLDS, GRVW need to less or equal than LRVW to have conflict free LDS read with padding.
optGRVW = lrvw if unrollMajorLDS else 4 / datatype.numRegisters()
if optGRVW * datatype.numBytes() > 16 and not(isaInfoMap[isa].asmCaps["HasWMMA_f8f6f4"] and datatype.numBytes() == 0.75):
optGRVW = int(16 // datatype.numBytes())
return optGRVW
def calSwizzlePackK(state, tc):
return 16 // state[f"MIInputPerThread{tc}"] // int(state["ProblemType"][f"DataType{tc}"].numBytes())
genGRVWA = False
genGRVWB = False
# Default GlobalReadVectorWidthA
if state["EnableMatrixInstruction"]:
if state["GlobalReadVectorWidthA"] < 0:
genGRVWA = True
if state["GlobalReadVectorWidthA"] == -2:
if state["MatrixInstBM"] == 1 and state["MIWaveTile"][0] == 1 and state["MIWaveGroup"][0] == 1 and state["ProblemType"]["TLUA"]:
state["GlobalReadVectorWidthA"] = 1
else:
reject(state, printRejectionReason, "GRVWA=-2 is set for skinny MT")
elif state["GlobalReadVectorWidthA"] == -1:
if state["ProblemType"]["SwizzleTensorA"]:
state["GlobalReadVectorWidthA"] = state["MIInputPerThreadA"] * calSwizzlePackK(state, "A")
elif state["ProblemType"]["DataTypeA"].is6bitFloat():
state["GlobalReadVectorWidthA"] = 32
elif state["enableGLTrA"]:
state["GlobalReadVectorWidthA"] = 8
else:
optGRVW = calcOptGRVW(state["LocalReadVectorWidthA"], state["UnrollMajorLDSA"], state["ProblemType"]["DataTypeA"])
curGRVW = 1
state["GlobalReadVectorWidthA"] = int(curGRVW)
while (curGRVW <= optGRVW):
if (state["MacroTile0"]*state["_DepthUA"]//state["NumThreads"]) % curGRVW == 0:
state["GlobalReadVectorWidthA"] = int(curGRVW)
curGRVW *= 2
if state["ProblemType"]["MXBlockA"]:
state["GlobalReadVectorWidthMXSA"] = max(state["MacroTile0"] * state["_DepthUMXSA"] // state["NumThreads"], 1)
# workaround for DTL
# use 32bit load for DTL if GlobalReadVectorWidthMXS is 8
if state["DirectToLdsMXSA"] and state["GlobalReadVectorWidthMXSA"] == 8:
state["GlobalReadVectorWidthMXSA"] = 4
else:
# dot2
if state["UseDotInstruction"]:
coalescedA = state["MacroTile0"] if state["ProblemType"]["TLUA"] else state["DepthU"]
elementPerThread = state["MacroTile0"] * state["DepthU"] // state["NumThreads"]
if state["GlobalReadVectorWidthA"] == -1:
for tmpGRVW in [8,4,2,1]:
if coalescedA % tmpGRVW == 0 and elementPerThread % tmpGRVW == 0:
state["GlobalReadVectorWidthA"] = tmpGRVW
break
if coalescedA % state["GlobalReadVectorWidthA"] != 0 or elementPerThread % state["GlobalReadVectorWidthA"] != 0:
reject(state, "dot2: non-valid GRVWA")
return
# TODO: support edge shiftptr to release this constraint.
if state["ProblemType"]["TLUA"]:
state["AssertFree0ElementMultiple"] = max(state["AssertFree0ElementMultiple"], state["GlobalReadVectorWidthA"])
# Default GlobalReadVectorWidthB
if state["EnableMatrixInstruction"]:
if state["GlobalReadVectorWidthB"] < 0:
genGRVWB = True
if state["GlobalReadVectorWidthB"] == -2:
if state["MatrixInstBN"] == 1 and state["MIWaveTile"][1] == 1 and state["MIWaveGroup"][1] == 1 and state["ProblemType"]["TLUB"]:
state["GlobalReadVectorWidthB"] = 1
else:
reject(state, printRejectionReason, "GRVWB=-2 is set for skinny MT")
elif state["GlobalReadVectorWidthB"] == -1:
if state["ProblemType"]["SwizzleTensorB"]:
state["GlobalReadVectorWidthB"] = state["MIInputPerThreadB"] * calSwizzlePackK(state, "B")
elif state["ProblemType"]["DataTypeB"].is6bitFloat():
state["GlobalReadVectorWidthB"] = 32
elif state["enableGLTrB"]:
state["GlobalReadVectorWidthB"] = 8
else:
optGRVW = calcOptGRVW(state["LocalReadVectorWidthB"], state["UnrollMajorLDSB"], state["ProblemType"]["DataTypeB"])
curGRVW = 1
state["GlobalReadVectorWidthB"] = int(curGRVW)
while (curGRVW <= optGRVW):
if (state["MacroTile1"]*state["_DepthUB"]//state["NumThreads"]) % curGRVW == 0:
state["GlobalReadVectorWidthB"] = int(curGRVW)
curGRVW *= 2
if state["ProblemType"]["MXBlockB"]:
state["GlobalReadVectorWidthMXSB"] = max(state["MacroTile1"] * state["_DepthUMXSB"] // state["NumThreads"], 1)
# workaround for DTL
# use 32bit load for DTL if GlobalReadVectorWidthMXS is 8
if state["DirectToLdsMXSB"] and state["GlobalReadVectorWidthMXSB"] == 8:
state["GlobalReadVectorWidthMXSB"] = 4
else:
# dot2
if state["UseDotInstruction"]:
coalescedB = state["MacroTile1"] if state["ProblemType"]["TLUB"] else state["DepthU"]
elementPerThread = state["MacroTile1"] * state["DepthU"] // state["NumThreads"]
if state["GlobalReadVectorWidthB"] == -1:
for tmpGRVW in [8,4,2,1]:
if coalescedB % tmpGRVW == 0 and elementPerThread % tmpGRVW == 0:
state["GlobalReadVectorWidthB"] = tmpGRVW
break
if coalescedB % state["GlobalReadVectorWidthB"] != 0 or elementPerThread % state["GlobalReadVectorWidthB"] != 0:
reject(state, "dot2: non-valid GRVWB")
return
# TODO: support edge shiftptr to release this constraint.
if state["ProblemType"]["TLUB"]:
state["AssertFree1ElementMultiple"] = max(state["AssertFree1ElementMultiple"], state["GlobalReadVectorWidthB"])
if state["EnableMatrixInstruction"]:
if state["ProblemType"]["MXBlockA"]:
state["GlobalReadVectorWidthMXSA"] = max(state["MacroTile0"] * state["_DepthUMXSA"] // state["NumThreads"], 1)
if state["ProblemType"]["TLUMXSA"]:
state["GlobalReadVectorWidthMXSA"] = min(state["GlobalReadVectorWidthMXSA"], state["MacroTile0"])
else:
state["GlobalReadVectorWidthMXSA"] = min(state["GlobalReadVectorWidthMXSA"], state["_DepthUMXSA"])
state["GlobalReadVectorWidthMXSA"] = min(state["GlobalReadVectorWidthMXSA"], 16)
if state["ProblemType"]["MXBlockB"]:
state["GlobalReadVectorWidthMXSB"] = max(state["MacroTile1"] * state["_DepthUMXSB"] // state["NumThreads"], 1)
if state["ProblemType"]["TLUMXSB"]:
state["GlobalReadVectorWidthMXSB"] = min(state["GlobalReadVectorWidthMXSB"], state["MacroTile1"])
else:
state["GlobalReadVectorWidthMXSB"] = min(state["GlobalReadVectorWidthMXSB"], state["_DepthUMXSB"])
state["GlobalReadVectorWidthMXSB"] = min(state["GlobalReadVectorWidthMXSB"], 16)
#for tensor swizzling, we calculate pack-k to achieve buffer_load_dwordx4
for tc in ("A", "B",):
if state["ProblemType"][f"SwizzleTensor{tc}"]:
if not state["EnableMatrixInstruction"]:
reject(state, printRejectionReason, f"Tensor {tc} swizzling supports MI only")
# Print rejection reason instead of force set
# 16 means bytes of buffer_load_dwordx4
SwizzlePackK = calSwizzlePackK(state, tc)
if state[f"GlobalReadVectorWidth{tc}"] != state[f"MIInputPerThread{tc}"] * SwizzlePackK:
GRVW_TC = state[f"GlobalReadVectorWidth{tc}"]
MIInPerThread = state[f"MIInputPerThread{tc}"]
reject(state, printRejectionReason, f"SwizzleTensor{tc} doesn't support GRVW{tc} ({GRVW_TC}) != MIInputPerThread{tc} ({MIInPerThread}) * {SwizzlePackK}")
if state["ProblemType"]["SwizzleTensorA"]:
if not state["DirectToVgprA"]:
reject(state, printRejectionReason, f"Tensor A swizzling requires DirectToVgprA")
if not state["ProblemType"]["TransposeA"]:
reject(state, printRejectionReason, f"Tensor A swizzling supports TN or TT only")
if state["ProblemType"]["SwizzleTensorB"]:
if not state["DirectToVgprB"]:
reject(state, printRejectionReason, f"Tensor B swizzling requires DirectToVgprB")
if state["ProblemType"]["TransposeB"]:
reject(state, printRejectionReason, f"Tensor B swizzling supports TN or NN only")
# TODO- NN fails validation due to DTVB + Tail-Loop is not working correctly
if not (state["ProblemType"]["TransposeA"] and not state["ProblemType"]["TransposeB"]):
reject(state, printRejectionReason, f"Tensor B swizzling supports TN only")
# Force GRVW the same when UnrollLoopSwapGlobalReadOrder = 1.
if genGRVWA and state["UnrollLoopSwapGlobalReadOrder"] == 1:
state["GlobalReadVectorWidthA"] = min(state["GlobalReadVectorWidthA"], state["GlobalReadVectorWidthB"])
if genGRVWB and state["UnrollLoopSwapGlobalReadOrder"] == 1:
state["GlobalReadVectorWidthB"] = min(state["GlobalReadVectorWidthA"], state["GlobalReadVectorWidthB"])
# reject - VW too big
if (state["VectorWidthA"] * state["ProblemType"]["MacDataTypeA"].numBytes()) > 16:
reject(state, printRejectionReason, "VWA * MacDataTypeA.numBytes() > 16")
if (state["VectorWidthB"] * state["ProblemType"]["MacDataTypeB"].numBytes()) > 16:
reject(state, printRejectionReason, "VWB * MacDataTypeB.numBytes() > 16")
# reject - GRVW too big
if (state["GlobalReadVectorWidthA"] * state["ProblemType"]["DataTypeA"].numBytes()) > 24:
reject(state, printRejectionReason, "GRVWA * DataTypeA.numBytes() > 24")
if (state["GlobalReadVectorWidthB"] * state["ProblemType"]["DataTypeB"].numBytes()) > 24:
reject(state, printRejectionReason, "GRVWB * DataTypeB.numBytes() > 24")
disableGNLC = False # Set to true to disable GNLC if needed
if state["UseSgprForGRO"] == 1:
disableGNLC = True
isMixedPrec = (state["ProblemType"]["DataTypeA"].numBytes() != state["ProblemType"]["DataTypeB"].numBytes())
if state["DirectToLds"] and state["LocalSplitU"] == 1 \
and not isMixedPrec and not state["ProblemType"]["Sparse"] \
and state["MatrixInstB"] == 1 \
and not disableGNLC:
for tc in ['A', 'B']:
if state["DirectToLds%s"%tc]:
# Check if we are requesting b64 loads for A/B - these are not compatible with DTL
grwidth = state["GlobalReadVectorWidth%s"%tc] * state["ProblemType"]["DataType%s"%tc].numBytes()
# Check that GR layout is the same as LDS layout for A/B
sameLayout = state["ProblemType"]["TLU%s"%tc] != state["UnrollMajorLDS%s"%tc]
state["UseGeneralizedNLCOne%s"%tc] = grwidth != 8 and sameLayout \
and state["WaveSeparateGlobalRead%s"%tc] == 0 and not state["DirectToVgpr%s"%tc]
# workaround: disable UseGeneralizedNLCOne for MXBlock (TODO: enable it for MXBlock)
if state["ProblemType"]["MXBlock%s"%tc]:
state["UseGeneralizedNLCOne%s"%tc] = False
else:
state["UseGeneralizedNLCOne%s"%tc] = False
state["UseGeneralizedNLCOneMetadata"] = False
state["_UseSgprForGRO"] = 0
else:
state["UseGeneralizedNLCOneA"] = False
state["UseGeneralizedNLCOneB"] = False
state["UseGeneralizedNLCOneMetadata"] = False
########################################
# Search DepthU
# Inputs:
# - depthU, userDepthU, state["LocalSplitU"], state["InnerUnroll"], state["MacroTile0/1"], state["GlobalReadVectorWidth"]
# - state["MatrixInstK"], ...
# Outputs:
# - totalVectorsCoalescedA, totalVectorsCoalescedB, totalElementsPerpA, totalElementsPerpB, state["DepthU"]
#######################################
while True: # exit criteria at end
validDepthU = True
# handle global read vector width A
if state["ProblemType"]["TLUA"]: # NT/NN
totalElementsCoalescedA = state["MacroTileA"]
totalElementsPerpA = state["_DepthUA"]
if state["DirectToVgprA"]:
totalElementsCoalescedA *= state["MIWaveGroup"][1]
else: # TN/TT
totalElementsCoalescedA = state["_DepthUA"]
totalElementsPerpA = state["MacroTileA"]
if state["DirectToVgprA"]:
totalElementsPerpA *= state["MIWaveGroup"][1]
if state["GlobalReadVectorWidthA"] > totalElementsCoalescedA:
reject(state, printRejectionReason, f"GRVWA({state['GlobalReadVectorWidthA']}) > Coaleased({totalElementsCoalescedA})")
totalElementsA = totalElementsCoalescedA * totalElementsPerpA
tva = totalElementsA // state["GlobalReadVectorWidthA"]
if not Solution.setGlobalReadVectorWidth(state, "A", tva, state["GlobalReadVectorWidthA"], printRejectionReason):
validDepthU = False
if state["EnableMatrixInstruction"] and state["GlobalReadVectorWidthA"]:
partialA = state["ProblemType"]["TLUA"] and (state["AssertFree0ElementMultiple"] % state["GlobalReadVectorWidthA"] != 0)
if partialA and not state["UseGeneralizedNLCOneA"]:
limitBytes = 24 if state["ProblemType"]["DataType"].is6bitFloat else 16
glvwAlimit = int(limitBytes / state["ProblemType"]["MacDataTypeA"].numBytes())
if state["SourceSwap"]:
matrixInstM = (state["MatrixInstM"] * state["MatrixInstBM"]) if (state["MatrixInstM"] == 4) else state["MatrixInstM"]
glvwAlimit = matrixInstM * state["VectorWidthA"]
else:
matrixInstN = (state["MatrixInstN"] * state["MatrixInstBN"]) if (state["MatrixInstN"] == 4) else state["MatrixInstN"]
glvwAlimit = state["MIOutputVectorWidth"] * (state["WavefrontSize"] // matrixInstN)
if state["ProblemType"]["MacDataTypeA"].numRegisters() == 0.25:
glvwAlimit = max(glvwAlimit, 4)
# reduce GLVA if GLVA larger than MIOVW
if state["GlobalReadVectorWidthA"] > glvwAlimit:
tva = totalElementsA // glvwAlimit
if not Solution.setGlobalReadVectorWidth(state, "A", tva, glvwAlimit, printRejectionReason):
validDepthU = False
GlobalReadVectorWidthA = state["GlobalReadVectorWidthA"]
totalVectorsCoalescedA = totalElementsCoalescedA // GlobalReadVectorWidthA
# handle global read vector width MXSA
if state["ProblemType"]["MXBlockA"] and not state["UseSubtileImpl"]:
if state["ProblemType"]["TLUMXSA"]: # NT/NN
totalElementsCoalescedMXSA = state["MacroTileMXSA"]
totalElementsPerpMXSA = state["_DepthUMXSA"]
if state["DirectToVgprMXSA"]:
totalElementsCoalescedMXSA *= state["MIWaveGroup"][1]
else: # TN/TT
totalElementsCoalescedMXSA = state["_DepthUMXSA"]
totalElementsPerpMXSA = state["MacroTileMXSA"]
if state["DirectToVgprMXSA"]:
totalElementsPerpMXSA *= state["MIWaveGroup"][1]
totalElementsMXSA = totalElementsCoalescedMXSA * totalElementsPerpMXSA
tvmxsa = totalElementsMXSA // state["GlobalReadVectorWidthMXSA"]
if not Solution.setGlobalReadVectorWidth(state, "MXSA", tvmxsa, state["GlobalReadVectorWidthMXSA"], printRejectionReason):
validDepthU = False
GlobalReadVectorWidthMXSA = state["GlobalReadVectorWidthMXSA"]
totalVectorsCoalescedMXSA = totalElementsCoalescedMXSA // GlobalReadVectorWidthMXSA
# handle global read vector width B
if state["ProblemType"]["TLUB"]: # NT/TT
totalElementsCoalescedB = state["MacroTileB"]
totalElementsPerpB = depthUB
if state["DirectToVgprB"]:
totalElementsCoalescedB *= state["MIWaveGroup"][0]
else: # TN/NN
totalElementsCoalescedB = depthUB
totalElementsPerpB = state["MacroTileB"]
if state["DirectToVgprB"]:
totalElementsPerpB *= state["MIWaveGroup"][0]
if state["GlobalReadVectorWidthB"] > totalElementsCoalescedB:
reject(state, printRejectionReason, f"GRVWB({state['GlobalReadVectorWidthB']}) > Coaleased({totalElementsCoalescedB})")
totalElementsB = totalElementsCoalescedB * totalElementsPerpB
# handle global read vector width M
if state["ProblemType"]["Sparse"] and not state["DirectToVgprSparseMetadata"]:
if state["ProblemType"]["TLUMetadata"]:
totalElementsCoalescedM = state["MacroTileMetadata"]
totalElementsPerpM = depthUM
else:
totalElementsCoalescedM = depthUM
totalElementsPerpM = state["MacroTileMetadata"]
totalElementsM = totalElementsCoalescedM * totalElementsPerpM
tvb = totalElementsB // state["GlobalReadVectorWidthB"]
if not Solution.setGlobalReadVectorWidth(state, "B", tvb, state["GlobalReadVectorWidthB"], printRejectionReason):
validDepthU = False
if state["EnableMatrixInstruction"] and state["GlobalReadVectorWidthB"]:
partialB = state["ProblemType"]["TLUB"] and (state["AssertFree1ElementMultiple"] % state["GlobalReadVectorWidthB"] != 0)
if partialB and not state["UseGeneralizedNLCOneB"]:
limitBytes = 24 if state["ProblemType"]["DataType"].is6bitFloat else 16
glvwBlimit = int(limitBytes / state["ProblemType"]["MacDataTypeB"].numBytes())
if state["SourceSwap"]:
matrixInstM = (state["MatrixInstM"] * state["MatrixInstBM"]) if (state["MatrixInstM"] == 4) else state["MatrixInstM"]
glvwBlimit = state["MIOutputVectorWidth"] * (state["WavefrontSize"] // matrixInstM)
else: # use origin shiftptr for B
matrixInstN = (state["MatrixInstN"] * state["MatrixInstBN"]) if (state["MatrixInstN"] == 4) else state["MatrixInstN"]
glvwBlimit = matrixInstN * state["VectorWidthB"]
if state["ProblemType"]["MacDataTypeB"].numRegisters() == 0.25:
glvwBlimit = max(glvwBlimit, 4)
# reduce GLVB if GLVB larger than MIOVW
if state["GlobalReadVectorWidthB"] > glvwBlimit:
tvb = totalElementsB // glvwBlimit
if not Solution.setGlobalReadVectorWidth(state, "B", tvb, glvwBlimit, printRejectionReason):
validDepthU = False
GlobalReadVectorWidthB = state["GlobalReadVectorWidthB"]
totalVectorsCoalescedB = totalElementsCoalescedB // GlobalReadVectorWidthB
# handle global read vector width MXSB
if state["ProblemType"]["MXBlockB"] and not state["UseSubtileImpl"]:
if state["ProblemType"]["TLUMXSB"]: # NT/NN
totalElementsCoalescedMXSB = state["MacroTileMXSB"]
totalElementsPerpMXSB = state["_DepthUMXSB"]
if state["DirectToVgprMXSB"]:
totalElementsCoalescedMXSB *= state["MIWaveGroup"][1]
else: # TN/TT
totalElementsCoalescedMXSB = state["_DepthUMXSB"]
totalElementsPerpMXSB = state["MacroTileMXSB"]
if state["DirectToVgprMXSB"]:
totalElementsPerpMXSB *= state["MIWaveGroup"][1]
totalElementsMXSB = totalElementsCoalescedMXSB * totalElementsPerpMXSB
tvmxsa = totalElementsMXSB // state["GlobalReadVectorWidthMXSB"]
if not Solution.setGlobalReadVectorWidth(state, "MXSB", tvmxsa, state["GlobalReadVectorWidthMXSB"], printRejectionReason):
validDepthU = False
GlobalReadVectorWidthMXSB = state["GlobalReadVectorWidthMXSB"]
totalVectorsCoalescedMXSB = totalElementsCoalescedMXSB // GlobalReadVectorWidthMXSB
# handle global read vector width Metadata
if state["ProblemType"]["Sparse"] and not state["DirectToVgprSparseMetadata"]:
grvw = 1
vw = 1
if state["ProblemType"]["Sparse"] == 2:
grvw = math.ceil(state["GlobalReadVectorWidthB"] / 4)
vw = math.ceil(state["VectorWidthB"] / 4)
if state["GlobalReadVectorWidthB"] % 4 != 0:
reject(state, printRejectionReason, "Sparse B requires GRVWB %% 4 == 0, current GRVWB is %u"%state["GlobalReadVectorWidthB"])
break
else:
grvw = math.ceil(state["GlobalReadVectorWidthA"] / 4)
vw = math.ceil(state["VectorWidthA"] / 4)
if state["GlobalReadVectorWidthA"] % 4 != 0:
reject(state, printRejectionReason, "Sparse A requires GRVWA %% 4 == 0, current GRVWA is %u"%state["GlobalReadVectorWidthA"])
break
tvm = totalElementsM // grvw
if tuning and (not Solution.setGlobalReadVectorWidth(state, "Metadata", tvm, grvw, printRejectionReason)):
validDepthU = False
if state["EnableMatrixInstruction"] and state["GlobalReadVectorWidthMetadata"]:
partialM = True
if state["ProblemType"]["Sparse"] == 2:
partialM = state["ProblemType"]["TLUMetadata"] and (state["AssertFree1ElementMultiple"] % state["GlobalReadVectorWidthB"] != 0)
else:
partialM = state["ProblemType"]["TLUMetadata"] and (state["AssertFree0ElementMultiple"] % state["GlobalReadVectorWidthA"] != 0)
if partialM:
glvwMlimit = 16
if state["SourceSwap"]:
matrixInstM = (state["MatrixInstM"] * state["MatrixInstBM"]) if (state["MatrixInstM"] == 4) else state["MatrixInstM"]
glvwMlimit = matrixInstM * vw
else:
if state["ProblemType"]["Sparse"] == 1:
matrixInstN = (state["MatrixInstN"] * state["MatrixInstBN"]) if (state["MatrixInstN"] == 4) else state["MatrixInstN"]
glvwMlimit = state["MIOutputVectorWidth"] * (state["WavefrontSize"] // matrixInstN)
# reduce GLVMetadata if GLVMetadata larger than MIOVW
if tuning and (state["GlobalReadVectorWidthMetadata"] > glvwMlimit):
tvm = totalElementsM // glvwMlimit
if not Solution.setGlobalReadVectorWidth(state, "Metadata", tvm, glvwMlimit, printRejectionReason):
validDepthU = False
if validDepthU and state["KernelLanguage"] == "Assembly":
if isaInfoMap[state["ISA"]].archCaps["HasEccHalf"]:
if state["ProblemType"]["DataType"].numRegisters() == 0.5 and (not state["ProblemType"]["HighPrecisionAccumulate"]) \
and (not state["ProblemType"]["DataTypeA"].isSingle()) and (not state["ProblemType"]["DataTypeB"].isSingle()):
if state["GlobalReadVectorWidthA"] == 1 or state["GlobalReadVectorWidthB"] == 1:
reject(state, printRejectionReason, "HalfEcc requires HPA if glvw = 1")
break
if state["ProblemType"]["Sparse"] and state["DirectToVgprSparseMetadata"]:
if state["VectorWidthA"] > 1 or state["VectorWidthB"] > 1 :
reject(state, printRejectionReason, "Not implement DTVSM with VW>1")
break
extraComment = ""
if validDepthU:
if not state["ProblemType"]["TLUA"]:
if depthUA < state["GlobalReadVectorWidthA"]:
validDepthU = False
if not state["ProblemType"]["TLUB"]:
if depthUB < state["GlobalReadVectorWidthB"]:
validDepthU = False
if state["ProblemType"]["Sparse"] and (not state["DirectToVgprSparseMetadata"]):
if not state["ProblemType"]["TLUMetadata"]:
if depthUM < state["GlobalReadVectorWidthMetadata"]:
validDepthU = False
# swizzle
if state["LocalSplitU"] > 1:
if state["ProblemType"]["SwizzleTensorA"]:
SwizzlePackK = calSwizzlePackK(state, "A")
if depthUA < state["MatrixInstK"] * SwizzlePackK * state["LocalSplitU"]:
validDepthU = False
extraComment = ": DepthU(%u) < Min-DU for swizzleA + LSU(%u)"%(depthUA, state["LocalSplitU"])
if state["ProblemType"]["SwizzleTensorB"]:
SwizzlePackK = calSwizzlePackK(state, "B")
if depthUB < state["MatrixInstK"] * SwizzlePackK * state["LocalSplitU"]:
validDepthU = False
extraComment = ": DepthU(%u) < Min-DU for swizzleB + LSU(%u)"%(depthUB, state["LocalSplitU"])
# TDM pad_interval (padIntervalBytes = DepthU * bpeGR) max allowed value is
# 1024 bytes (256 DWORDs).
if state["UseSubtileImpl"]:
for tc in ["A", "B"]:
if not state["enableTDM%s" % tc]:
continue
padIntervalBytes = depthU * state["ProblemType"]["DataType%s" % tc].numBytes()
if padIntervalBytes > 1024:
validDepthU = False
extraComment = ": DepthU(%u)*bpeGR%s = %u exceeds TDM pad_interval limit of 1024 bytes" \
% (depthU, tc, padIntervalBytes)
# this depthU is valid, done unless user wants to double (for TN)
if validDepthU:
state["DepthU"] = depthU
break
# this depthU not valid
else:
reject(state, printRejectionReason, "No valid DepthU found%s"%(extraComment))
state["ValidDepthU"] = False
break
########################################
# end DepthU loop
########################################
if state["Valid"]:
break
index[0] += 1
if index[0] >= len(depthuList):
break
if not state["Valid"]:
return
assert(state["DepthU"]> 0)
if state["_ScheduleIterAlg"] == 2:
state["InnerUnroll"] = state["DepthU"] // state["MatrixInstK"]
state["PrefetchLocalRead"] = 1
#state["ExpandPointerSwap"] = 1 # EPS is adjusted in advance
state["1LDSBuffer"] = 1
print2("\nSet SIA=2, force PrefetchLocalRead=1, 1LDSBuffer=1")
# Default GlobalStoreVectorWidth
if state["StoreVectorWidth"] == -1:
if state["SourceSwap"]:
state["StoreVectorWidth"] = state["VectorWidthA"]
else:
if state["EnableMatrixInstruction"]:
# Adjusting StoreVectorWidth for larger CGEMM register count
if state["ProblemType"]["DestDataType"].isSingleComplex():
state["StoreVectorWidth"] = 4 // state["ProblemType"]["DestDataType"].numRegisters()
else:
state["StoreVectorWidth"] = state["MIOutputVectorWidth"]
if state["VectorWidthA"] * state["MIOutputVectorWidth"] <= 4 / state["ProblemType"]["DestDataType"].numRegisters():
state["StoreVectorWidth"] = state["VectorWidthA"] * state["MIOutputVectorWidth"]
if state["LocalSplitU"] > 1:
state["StoreVectorWidth"] = state["VectorWidthA"]
else:
state["StoreVectorWidth"] = state["VectorWidthA"]
if state["EnableMatrixInstruction"] and not state["UseSubtileImpl"]:
if state["SourceSwap"]:
if ((state["VectorWidthA"] % state["StoreVectorWidth"]) != 0):
reject(state, printRejectionReason, "MFMA SourceSwap mode doesn't support vwA(%u) with svw(%u)" % (state["VectorWidthA"], state["StoreVectorWidth"]))
return
else:
if (((state["VectorWidthA"] * state["MIOutputVectorWidth"]) % state["StoreVectorWidth"]) != 0):
reject(state, printRejectionReason, "MFMA non-SourceSwap mode doesn't support miovw(%u) with svw(%u)" % (state["VectorWidthA"]*state["MIOutputVectorWidth"], state["StoreVectorWidth"]))
return
# LocalSplitU too large?
# dot2: every NumWaveSplitK threads compute the same element.
numElementsPerWorkGroup = state["MacroTile0"]*state["MacroTile1"]*state["NumWaveSplitK"]
if numElementsPerWorkGroup < state["NumThreads"]:
reject(state, printRejectionReason, "NumElementsPerWorkGroup %u < NumThreads %u; reduce LocalSplitU" \
% (numElementsPerWorkGroup, state["NumThreads"]))
return
state["NumElementsPerThread"] = numElementsPerWorkGroup // state["NumThreads"]
state["GlobalWriteVectorWidth"] = min(state["VectorWidthA"], state["NumElementsPerThread"] )
if state["NumElementsPerThread"] % state["GlobalWriteVectorWidth"] != 0:
reject(state, printRejectionReason, "LSU NumElementsPerThread %u not divisible into GWVW %u" \
% (state["NumElementsPerThread"], state["GlobalWriteVectorWidth"]))
return
state["NumGlobalWriteVectorsPerThread"] = state["NumElementsPerThread"] \
// state["GlobalWriteVectorWidth"]
# NumWaveSplitK requires BufferStore for thread masking in emitLdChange
if state["NumWaveSplitK"] > 1:
if not state["BufferStore"]:
reject(state, printRejectionReason, "NumWaveSplitK > 1 requires BufferStore")
return
# SingleBuffer GSU workspace addressing not yet implemented for flat stores
if not state["BufferStore"] and state["_GlobalAccumulation"] == "SingleBuffer":
reject(state, printRejectionReason, "GlobalAccumulation SingleBuffer requires BufferStore")
return
# LocalSplitU but can't NumThreads%MacroTile doesn't support sideways store
if state["LocalSplitU"] > 1:
if not state["BufferStore"]:
reject(state, printRejectionReason, "LocalSplitU > 1 requires BufferStore")
return
if not state["SourceSwap"] and state["StoreVectorWidth"] > state["VectorWidthA"]:
reject(state, printRejectionReason, "LSU and non-SourceSwap doesn't support StoreVectorWidth(%u)>VWA(%u)." \
% (state["StoreVectorWidth"], state["VectorWidthA"]))
return
if not (state["ProblemType"]["ComputeDataType"].isSingle() or state["ProblemType"]["ComputeDataType"].isInt32() or state["ProblemType"]["ComputeDataType"].isComplex()):
reject(state, printRejectionReason, "TODO: LSU doesn't support ComputeDataType!=(single, Int32 or complex).")
return
if state["StoreRemapVectorWidth"] > 0:
reject(state, printRejectionReason, "TODO: LSU doesn't support StoreRemapVectorWidth>0.")
return
if state["NumThreads"] % state["MacroTile0"] != 0:
reject(state, printRejectionReason, "LocalSplitU but NumThreads=%u not divisible by MT0=%u for sideways store" \
% (state["NumThreads"], state["MacroTile0"]))
return
if state["MacroTile0"]*state["MacroTile1"] % state["NumThreads"] != 0:
reject(state, printRejectionReason, "LocalSplitU but MT0*MT1=%u elements doesn't divide into NumThreads=%u" \
% (state["MacroTile0"]*state["MacroTile1"], state["NumThreads"]))
return
# GlobalSplitU doesn't work with some other things:
if state["GlobalSplitU"] > 1 or state["GlobalSplitU"] == -1:
# added GSU support for DGEMM
supported = \
(state["ProblemType"]["DataType"].isSingle()) or \
(state["ProblemType"]["DataType"].isDouble() and state["BufferStore"]) or \
(state["ProblemType"]["DestDataType"].isInt32()) or \
(state["KernelLanguage"] == "Assembly" and
(state["ProblemType"]["DataType"].isHalf() and not state["ProblemType"]["HighPrecisionAccumulate"]) or
(state["_GlobalAccumulation"])
)
if not supported:
reject(state, printRejectionReason, "GlobalSplitU only compatible with single or asm and (half or mixed) precision")
return
if state["ProblemType"]["DataType"].isHalf() and state["KernelLanguage"] == "Assembly":
if (state["GlobalSplitU"] > 1 or state["GlobalSplitU"] == -1) and (not state["_GlobalAccumulation"]):
if state["AssertFree0ElementMultiple"] < 2:
reject(state, printRejectionReason, "Assembly GSU half requires AF0EM>=2 (for atomics on edge tiles)")
if state["EnableMatrixInstruction"] and isaInfoMap[isa].asmCaps['HasWMMA']:
reject(state, printRejectionReason, "Half WMMA doesn't support single buffer GSU")
return
# dot2 limitation
if state["UseDotInstruction"]:
if state["EnableMatrixInstruction"]:
reject(state, "dot inst is for mac kernel!")
if not bufferLoad:
reject(state, "dot2 kernel only support bufferLoad!")
if not ((isaInfoMap[isa].asmCaps['v_dot2_f32_f16'] and state["ProblemType"]["DataType"].isHalf()) \
or (isaInfoMap[isa].asmCaps['v_dot2_f32_bf16'] and state["ProblemType"]["DataType"].isBFloat16())) \
and state["ProblemType"]["HighPrecisionAccumulate"]:
reject(state, "dot2 kernel only support DataType fp16 or bf16 with HPA")
if state["InnerUnroll"] not in [1,2,4]:
reject(state, "dot2 kernel requires InnerUnroll = 1,2 or 4")
if state["NumWaveSplitK"] not in [1,2,4,8,16,32,64]:
reject(state, "Unsupported NumWaveSplitK value. Need to be power of 2 and does not exceed 64.")
if state["_DepthUA"] % (state["LocalReadVectorWidthA"] * state["NumWaveSplitK"]) != 0:
reject(state, "Non-valid DepthU for dot2 kernel, need to be multiple of (LocalReadVectorWidth * InnerUnroll * NumWaveSplitK) for atomics")
if state["_DepthUB"] % (state["LocalReadVectorWidthB"] * state["NumWaveSplitK"]) != 0:
reject(state, "Non-valid DepthU for dot2 kernel, need to be multiple of (LocalReadVectorWidth * InnerUnroll * NumWaveSplitK) for atomics")
# TODO: Need to consider VectorWidth in LraTileAssignmentVALU
if state["VectorWidthA"] != 1 or state["VectorWidthB"] != 1:
reject(state, "dot2 kernel requires VectorWidth = 1")
# TODO: Need to remap VGPR index
if (state["ThreadTile0"] != 1 or state["ThreadTile1"] != 1) and state["InnerUnroll"] > 1:
reject(state, "dot2 kernel does not support wider local read with ThreadTile > 1")
if state["ScheduleLocalWrite"] != 1:
reject(state, "dot2 kernel requires ScheduleLocalWrite = 1")
if state["LocalSplitU"] != 1:
reject(state, "dot2 kernel requires LocalSplitU = 1")
if state["ProblemType"]["Sparse"]:
reject(state, "dot2 kernel does not support sparse gemm")
# TODO: Need to fix WS address calculation of MT<16x16 cases
if state["GlobalSplitUAlgorithm"] == "MultipleBufferSingleKernel":
reject(state, "dot2 kernel does not support MBSK")
if state["ProblemType"]["Sparse"] and not state["DirectToVgprSparseMetadata"]:
state["NumLoadsCoalescedMetadata"] = 1
if not Solution.setGlobalLoadTileDimClassic(state, "A", state["NumLoadsA"], \
totalVectorsCoalescedA, totalElementsPerpA, state["_DepthUA"], printRejectionReason):
return
if state["ProblemType"]["MXBlockA"] and not state["UseSubtileImpl"]:
if not Solution.setGlobalLoadTileDimClassic(state, "MXSA", state["NumLoadsMXSA"], \
totalVectorsCoalescedMXSA, totalElementsPerpMXSA, state["_DepthUMXSA"], printRejectionReason):
return
if not Solution.setGlobalLoadTileDimClassic(state, "B", state["NumLoadsB"], \
totalVectorsCoalescedB, totalElementsPerpB, state["_DepthUB"], printRejectionReason):
return
if state["ProblemType"]["MXBlockB"] and not state["UseSubtileImpl"]:
if not Solution.setGlobalLoadTileDimClassic(state, "MXSB", state["NumLoadsMXSB"], \
totalVectorsCoalescedMXSB, totalElementsPerpMXSB, state["_DepthUMXSB"], printRejectionReason):
return
if state["ProblemType"]["Sparse"] and not state["DirectToVgprSparseMetadata"]:
if state["ProblemType"]["TLUMetadata"]:
totalElementsCoalescedM = state["MacroTileMetadata"]
totalElementsPerpM = depthUM
else:
totalElementsCoalescedM = depthUM
totalElementsPerpM = state["MacroTileMetadata"]
totalElementsM = totalElementsCoalescedM * totalElementsPerpM
GlobalReadVectorWidthMetadata = state["GlobalReadVectorWidthMetadata"]
totalVectorsCoalescedM = totalElementsCoalescedM // GlobalReadVectorWidthMetadata
if tuning:
# Try to enlarge GLVW for metadata
bGlobalReadVectorWidthMetadata = state["GlobalReadVectorWidthMetadata"]
glvwMlimit = 16
if state["GlobalReadVectorWidthMetadata"] < glvwMlimit:
if state["ProblemType"]["Sparse"] == 2:
GlobalReadVectorWidth = min(state["GlobalReadVectorWidthMetadata"] * state["NumLoadsPerpendicularB"], depthUM, glvwMlimit) #sum all need read
tvm = totalElementsM // GlobalReadVectorWidth
if not Solution.setGlobalReadVectorWidth(state, "Metadata", tvm, GlobalReadVectorWidth, printRejectionReason):
#fallback
tvm = totalElementsM // bGlobalReadVectorWidthMetadata
Solution.setGlobalReadVectorWidth(state, "Metadata", tvm, bGlobalReadVectorWidthMetadata, printRejectionReason)
else:
GlobalReadVectorWidth = min(state["GlobalReadVectorWidthMetadata"] * state["NumLoadsPerpendicularA"], depthUM, glvwMlimit) #sum all need read
tvm = totalElementsM // GlobalReadVectorWidth
if not Solution.setGlobalReadVectorWidth(state, "Metadata", tvm, GlobalReadVectorWidth, printRejectionReason):
#fallback
tvm = totalElementsM // bGlobalReadVectorWidthMetadata
Solution.setGlobalReadVectorWidth(state, "Metadata", tvm, bGlobalReadVectorWidthMetadata, printRejectionReason)
GlobalReadVectorWidthMetadata = state["GlobalReadVectorWidthMetadata"]
if GlobalReadVectorWidthMetadata == 0:
GlobalReadVectorWidthMetadata = 1
totalVectorsCoalescedM = totalElementsCoalescedM // GlobalReadVectorWidthMetadata
if not Solution.setGlobalLoadTileDimClassic(state, "Metadata", state["NumLoadsMetadata"], \
totalVectorsCoalescedM, totalElementsPerpM, depthUM, printRejectionReason):
return
# TODO
if (0 and state["LSCA"] % state["GlobalReadVectorWidthA"] != 0):
reject(state, printRejectionReason, "lsca % grvw != 0")
return
if (0 and state["LSPA"] % state["GlobalReadVectorWidthA"] != 0):
reject(state, printRejectionReason, "lspa % grvw != 0")
return
if (0 and state["LSCB"] % state["GlobalReadVectorWidthB"] != 0):
reject(state, printRejectionReason, "lscb % grvw != 0")
return
if (0 and state["LSPB"] % state["GlobalReadVectorWidthB"] != 0):
reject(state, printRejectionReason, "lspb % grvw != 0")
return
state["LVCA"] = roundupRatio(state["LSCA"] , state["GlobalReadVectorWidthA"])
state["LVPA"] = roundupRatio(state["LSPA"] , state["GlobalReadVectorWidthA"])
if state["ProblemType"]["MXBlockA"]:
state["LVCMXSA"] = roundupRatio(state["LSCMXSA"] , state["GlobalReadVectorWidthMXSA"])
state["LVPMXSA"] = roundupRatio(state["LSPMXSA"] , state["GlobalReadVectorWidthMXSA"])
state["LVCB"] = roundupRatio(state["LSCB"] , state["GlobalReadVectorWidthB"])
state["LVPB"] = roundupRatio(state["LSPB"] , state["GlobalReadVectorWidthB"])
if state["ProblemType"]["MXBlockB"]:
state["LVCMXSB"] = roundupRatio(state["LSCMXSB"] , state["GlobalReadVectorWidthMXSB"])
state["LVPMXSB"] = roundupRatio(state["LSPMXSB"] , state["GlobalReadVectorWidthMXSB"])
if state["ProblemType"]["Sparse"] and not state["DirectToVgprSparseMetadata"]:
state["LVCMetadata"] = roundupRatio(state["LSCMetadata"] , state["GlobalReadVectorWidthMetadata"])
state["LVPMetadata"] = roundupRatio(state["LSPMetadata"] , state["GlobalReadVectorWidthMetadata"])
for tc in ('A','B'):
if problemType["TLU%s"%tc]:
pos = problemType["IndexAssignments%s"%tc].index(problemType["Index01%s"%tc])
else:
pos = problemType["IndexAssignments%s"%tc].index(problemType["IndexUnroll"])
# Some of these might become 0?
if 0:
print("info: ", pvar(state, "LVCA"), pvar(state, "LVPA"), \
pvar(state, "LVCB"), pvar(state, "LVPB"))
# lds buffer size for A, B
if state["KernelLanguage"] == "Source" and \
state["LdsPadA"] != state["LdsPadB"]:
reject(state, printRejectionReason, "Source KernelLanguage only supports LdsPadA == LdsPadB")
return
# NoTailLoop parameter initialization.
# If ASEM is multiple of DepthU TailLoop will not be used.
state["NoTailLoop"] = False
if state["AssertSummationElementMultiple"] % state["DepthU"] == 0:
state["NoTailLoop"] = True
# TailloopInNll optimization check
if state["TailloopInNll"]:
# Disable TailloopInNll
# - (not MFMA) or WMMA
# - PrefetchGlobalRead is 0
# - NoTailLoop
# - DepthU is not power of 2
# - LocalSplitU > 1
# - MX case
if ((not state["EnableMatrixInstruction"]) or isaInfoMap[isa].asmCaps["HasWMMA"]) or \
(state["PrefetchGlobalRead"] == 0) or \
state["NoTailLoop"] or \
(state["DepthU"] <=1 or (state["DepthU"] & (state["DepthU"] - 1) != 0)) or \
state["LocalSplitU"] > 1 or \
state["ProblemType"]["MXBlockA"] or state["ProblemType"]["MXBlockB"] or \
not bufferLoad:
state["TailloopInNll"] = False
# need restrictions for TailloopInNll
if state["TailloopInNll"]:
# need to disable SuppressNoLoadLoop
state["SuppressNoLoadLoop"] = False
# disable StaggerUcode
# - TailloopInNll
# - MX + StreamK (not enough sgpr)
if state["TailloopInNll"] or \
(state["StreamK"] and (state["ProblemType"]["MXBlockA"] or state["ProblemType"]["MXBlockB"])):
_disableRuntimeStaggerU(state)
_disableUnsupportedRuntimeStaggerU(state)
# Determine if we can load directly-to-Vgpr
# need to check after state["LocalReadVectorWidth"] = -1 is resolved
if state["DirectToVgprA"]:
if not Solution.isDirectToVgprDoable(state, 'A', printRejectionReason, isaInfoMap):
return # rejected
if state["DirectToVgprB"]:
if not Solution.isDirectToVgprDoable(state, 'B', printRejectionReason, isaInfoMap):
return # rejected
########################################
# LDS
########################################
if state["ProblemType"]["Sparse"]:
transposeLDSMetadata = int(state["TransposeLDSMetadata"])
if transposeLDSMetadata == -1:
state["TransposeLDSMetadata"] = int(not state["ProblemType"]["TLUMetadata"])
else:
state["TransposeLDSMetadata"] = int(transposeLDSMetadata)
state["UnrollMajorLDSMetadata"] = False if state["ProblemType"]["Sparse"] == 2 else True
if not state["DirectToVgprSparseMetadata"]:
state["UnrollMajorLDSMetadata"] = state["TransposeLDSMetadata"]
# Determine if we can load directly-to-LDS.
# Transpose requires a trip through registers to perform the transpose so can't use DirectToLdsA
# LDS loads always write 4 bytes apart so can use only 4-byte operations
# TODO - for doubles we need to add something special here?
# The matrix must not require transposing since that is done by reading to VGPR and writing in different order
# The LSC (load size coalesced) must load some multiple of 256 bytes since that is what each DirectToLds load provides
# Note for these matrices LSC is same as MacroTile dim
# MatrixInstruction rules:
# DirectToLDS is supported for TLU=0 (make sure transposeLDS=1)
# LDS (load size coalesced) * LSPA must load some multiple of 256 bytes.
# No longer support loadX2/loadx4 .
for tc in ['A', 'B']:
tcmx = "MXS%s"%tc
if state["UseSubtileImpl"] and state["ProblemType"]["MXBlock%s"%tc]:
state["DirectToLds%s"%tcmx] = False
if state["DirectToLds%s"%tc]:
isDtlDoable = Solution.isDirectToLdsDoable(state, tc, isaInfoMap, printRejectionReason)
if (not state["DirectToVgpr%s"%tc]) and isDtlDoable:
state["DirectToLds%s"%tc] = True
state["LocalWriteUseSgpr%s"%tc] = True
# MX case
if state["ProblemType"]["MXBlock%s"%tc]:
isDtlMxDoable = Solution.isDirectToLdsDoable(state, tcmx, isaInfoMap, printRejectionReason)
state["DirectToLds%s"%tcmx] = isDtlMxDoable
else:
state["DirectToLds%s"%tc] = False
state["LocalWriteUseSgpr%s"%tc] = False
# MX case
if state["ProblemType"]["MXBlock%s"%tc]:
state["DirectToLds%s"%tcmx] = False
if not isDtlDoable:
if state["UseGeneralizedNLCOne%s"%tc]:
reject(state, printRejectionReason, "DirectToLds%s not doable, but GNLC%s enabled, rejecting"%(tc, tc))
if state["ProblemType"]["Sparse"] and state["DirectToLdsMetadata"]:
sparseTc = 'B' if state["ProblemType"]["Sparse"] == 2 else 'A'
# DirectToLdsMetadata requires GRVWMetadata ∈ {4, 16} (dword/dwordx4), similar to isDirectToLdsDoable
grvwm = state["GlobalReadVectorWidthMetadata"]
grvwmCheck = (grvwm == 4) or (grvwm == 16 and isaInfoMap[state["ISA"]].asmCaps["HasDirectToLdsx4"])
if state["DirectToLds%s"%sparseTc] and (not state["DirectToVgprSparseMetadata"]) and grvwmCheck:
state["DirectToLdsMetadata"] = 1
state["LocalWriteUseSgprMetadata"] = True
else:
state["DirectToLdsMetadata"] = 0
state["LocalWriteUseSgprMetadata"] = False
# Update parent variable so kernel display is accurate
if state["DirectToLdsA"] and state["DirectToLdsB"]:
state["DirectToLds"] = 1
elif state["DirectToLdsA"]:
state["DirectToLds"] = 2
elif state["DirectToLdsB"]:
state["DirectToLds"] = 3
else:
state["DirectToLds"] = 0
if state["1LDSBuffer"] == -1 and state["DirectToLds"]:
#1LDS buffer must be 0 for DirectToLdsA
state["1LDSBuffer"] = 0
# MX case
if (state["ProblemType"]["MXBlockA"] or state["ProblemType"]["MXBlockB"]):
if state["DirectToLdsA"] != state["DirectToLdsMXSA"] or state["DirectToLdsB"] != state["DirectToLdsMXSB"]:
reject(state, printRejectionReason, "DirectToLdsA/B and DirectToLdsMXSA/B should match")
if state["DirectToLdsA"] != state["DirectToLdsB"]:
reject(state, printRejectionReason, "DirectToLdsA and DirectToLdsB should match")
# does not work with UnrollLoopSwapGlobalReadOrder
if (state["DirectToLds"] == 2 or state["DirectToLds"] == 3) and state["UnrollLoopSwapGlobalReadOrder"]:
reject(state, printRejectionReason, "DirectToLdsA or B only does not supports UnrollLoopSwapGlobalReadOrder")
return False
# Re-check DTV + WaveGroup after DTL is confirmed
if state["DirectToLds"]:
if state["DirectToVgprA"] and state['MIWaveGroup'][1] > 1:
reject(state, printRejectionReason, "DirectToLds + (DirectToVgprA + WaveGroups along N-Dim) is not supported yet")
return False
if state["DirectToVgprB"] and state['MIWaveGroup'][0] > 1:
reject(state, printRejectionReason, "DirectToLds + (DirectToVgprB + WaveGroups along M-Dim) is not supported yet")
return False
state["enableLDSTrMetadata"] = isaInfoMap[isa].asmCaps["HasLDSTrB64B8"] and state["ProblemType"]["MetadataLayout"]
if state["enableLDSTrMetadata"]:
state["VectorWidthMetadata"] = 1
# the VetorWidth of the sparse matrix and metadta need to be the same.
if state["ProblemType"]["Sparse"] == 1:
state["VectorWidthA"] = 1
else:
state["VectorWidthB"] = 1
wmmaV3 = isaInfoMap[isa].asmCaps["HasWMMA_V3"]
if wmmaV3:
# Todo: f64 needs to verify bank conflict.
ldsPadChecks = {
"MXSA": (bool(state["ProblemType"]["MXBlockA"]), True),
"MXSB": (bool(state["ProblemType"]["MXBlockB"]), True),
"A": (True, state["enableTDMA"] or (state["EnableMatrixInstruction"] and not state["UnrollMajorLDSA"])),
"B": (True, state["enableTDMB"] or (state["EnableMatrixInstruction"] and not state["UnrollMajorLDSB"])),
}
for tc, (check, mustHaveBlk) in ldsPadChecks.items():
if not check: continue
pad = state[f"LdsPad{tc}"]
blk = state[f"LdsBlockSizePerPad{tc}"]
if pad != 0 and (pad == -1) != (blk == -1):
reject(state, printRejectionReason,
f"Require LdsPad{tc} and LdsBlockSizePerPad{tc} "
f"to both be -1 (auto) or both explicit when LdsPad{tc} != 0; "
f"got LdsPad{tc}={pad}, LdsBlockSizePerPad{tc}={blk}.")
return False
if mustHaveBlk and pad != 0 and blk == 0:
reject(state, printRejectionReason,
f"LdsPad{tc}={pad} with LdsBlockSizePerPad{tc}=0 is not "
f"supported here; set LdsBlockSizePerPad{tc} explicitly.")
return False
auto_LdsPadA = (state["LdsPadA"] == -1)
auto_LdsPadB = (state["LdsPadB"] == -1)
auto_LdsBlockSizePerPadA = (state["LdsBlockSizePerPadA"] == -1)
auto_LdsBlockSizePerPadB = (state["LdsBlockSizePerPadB"] == -1)
state["LdsBlockSizePerPadA"] = calcLdsBlockSizePerPad("A", state["LocalReadVectorWidthA"])
state["LdsBlockSizePerPadB"] = calcLdsBlockSizePerPad("B", state["LocalReadVectorWidthB"])
state["LdsBlockSizePerPadMXSA"] = calcLdsBlockSizePerPad("MXSA", state["LocalReadVectorWidthMXS"]) if state["ProblemType"]["MXBlockA"] else 0
state["LdsBlockSizePerPadMXSB"] = calcLdsBlockSizePerPad("MXSB", state["LocalReadVectorWidthMXS"]) if state["ProblemType"]["MXBlockB"] else 0
checkLdsBlockSizePerPadForTDM(state["LdsBlockSizePerPadA"], state["LdsBlockSizePerPadB"], state["LdsBlockSizePerPadMXSA"], state["LdsBlockSizePerPadMXSB"])
if state["LdsBlockSizePerPadMetadata"] == -1:
state["LdsBlockSizePerPadMetadata"] = state["LdsBlockSizePerPadA"]
if state["EnableMatrixInstruction"]:
if state["LdsBlockSizePerPadA"] and not state["UseGeneralizedNLCOneA"]:
if state["UnrollMajorLDSA"]:
if state["LdsBlockSizePerPadA"] % int(state["_DepthUA"] * getLdsBpe("A")) != 0:
reject(state, printRejectionReason, "reject: LdsBlockSizePerPadA %u %% depthU %u x bpeA != 0" % (state["LdsBlockSizePerPadA"],state["_DepthUA"]))
if (state["LdsBlockSizePerPadA"] // (state["_DepthUA"] * state["ProblemType"]["MacDataTypeA"].numBytes())) % state["LSPA"] != 0 and \
state["LSPA"] % (state["LdsBlockSizePerPadA"] // (state["_DepthUA"] * state["ProblemType"]["MacDataTypeA"].numBytes())) != 0:
reject(state, printRejectionReason, "can't pad by addrVgpr or instOffset")
if state["LdsBlockSizePerPadB"] and not state["UseGeneralizedNLCOneB"]:
if state["UnrollMajorLDSB"]:
if state["LdsBlockSizePerPadB"] % int(state["_DepthUB"] * getLdsBpe("B")) != 0:
reject(state, printRejectionReason, "reject: LdsBlockSizePerPadB %u %% depthU %u x bpeB != 0" % (state["LdsBlockSizePerPadB"],state["_DepthUB"]))
if (state["LdsBlockSizePerPadB"] // (state["_DepthUB"] * state["ProblemType"]["MacDataTypeB"].numBytes())) % state["LSPB"] != 0 and \
state["LSPB"] % (state["LdsBlockSizePerPadB"] // (state["_DepthUB"] * state["ProblemType"]["MacDataTypeB"].numBytes())) != 0:
reject(state, printRejectionReason, "can't pad by addrVgpr or instOffset")
else:
if not state["UseDotInstruction"] and (state["UnrollMajorLDSA"] or state["UnrollMajorLDSB"]):
reject(state, printRejectionReason, "didn't support UnrollMajorLDS in VALU mode yet (except for dot2 kernel)")
if state["LdsBlockSizePerPadA"] != 0 or state["LdsBlockSizePerPadB"] != 0:
reject(state, printRejectionReason, "didn't support LdsBlockSizePerPad in VALU mode yet")
def checkLdsBlockSizePerPad(tc):
"""
Simulated to calculate the local write address and local write offset, to check the pad will be added correctly or not.
Expected: (address + offset) // lbspp == (address // lbspp + offset // lbspp)
# offset is an array, the amount is 'NLP x NLC x (grvw / width of ds write instruction)'
# Assumed Wave ID is zero
refer KernelWriterAssembly.lwaFirstOffset and KernelWriterAssembly.lwaTileAssignment to come out the address (local write address).
refer KereelWriterAssembly.localWriteDo and KereelWriteAssembly.calculateLdsWriteOffset to come out the offset (local write offset).
"""
def caculateLdsWriteOffset(perp, para, sPerp, sPara, tlu, uMLds, bpe, tc, idx):
mask = 0
#print "tc ", tc, " perp ", perp, " para ", para, " sPerp ", sPerp, " sPara ", sPara
lscaOffset = para * state["LSC%s"%tc]
perp_masked = perp
perp_rem = 0
lspaOffset = perp_masked * state["LSP%s"%tc]
rem = 0
# Add component offset to interleave from different regs
# and compute mysterious "i"
assert(sPerp==0 or sPara==0)
if tlu != uMLds:
lspaOffset += sPerp & mask
lscaOffset += sPara
rem = (sPerp & ~mask)
else:
lscaOffset += sPara
lspaOffset += sPerp
rem = 0
lds_stride = state["_DepthU%s"%tc] if uMLds else state["MacroTile%d"%idx]
if tlu != uMLds:
lspaOffset *= lds_stride
lspaOffset += rem + perp_rem
else:
lscaOffset *= lds_stride
lscaOffset += rem
offsetElements = (lspaOffset + lscaOffset)
offsetBytes = offsetElements * bpe
return offsetBytes
def caculateLdsWriteAddress(tc, idx, serial, tlu, uMLds, grvw, bpe):
id = serial
if state["WaveSeparateGlobalRead%s"%tc]:
id = id % state["WavefrontSize"]
q = id // state["LVC%s"%tc]
r = id % state["LVC%s"%tc]
#assumed wave id = 0
if state["WaveSeparateGlobalRead%s"%tc] == 2:
q *= state["NumLoadsPerpendicular%s"%tc]*state["NumThreads"]//state["WavefrontSize"]
if tlu:
t = r
u = q
t *= grvw
else:
t = q
u = r
u *= grvw
address = 0
if uMLds:
address = (state["_DepthU%s"%tc] * t + u) * bpe
else:
address = (state["MacroTile%s"%tc] * u + t) * bpe
return address
def findValidWriteBlockWidth(nwcv, bpe, bpr):
localWriteWidth = nwcv * bpe // bpr
if localWriteWidth < 1:
localWriteWidth = (1.0* nwcv * bpe )/bpr
blockWidth = 0
for bw in [8, 4, 2, 1, 0.5, 0.25]:
if localWriteWidth >= bw:
blockWidth = bw
break
if blockWidth == 0:
reject(state, printRejectionReason,
"invalid local write block width "
"(nwcv=%s, bpe=%s, bpr=%s, localWriteWidth=%s). "
"This typically means TransposeLDS=0 is incompatible with the selected "
"DataType and MatrixInstruction (e.g. FP4 + [16,16,128] requires TransposeLDS=1)."
% (nwcv, bpe, bpr, localWriteWidth))
return blockWidth
def subCheckLdsBlockSizePerPad(tc, idx):
lbspp = state["LdsBlockSizePerPad%s"%tc]
bpe = state["ProblemType"]["MacDataType%s"%tc].numBytes()
bpr = 4
vw = state["GlobalReadVectorWidth%s"%tc]
tlu = state["ProblemType"]["TLU%s"%tc]
uMLds = state["UnrollMajorLDS%s"%tc]
if tlu != uMLds: # NT no transpose
wtc = False # Vector
# writeCoal indicates writes should be done in the coal dim or else perp
nwcv = vw
nwpv = 1
else: # TN yes transpose
wtc = True
nwcv = 1
nwpv = vw
blockWidth = findValidWriteBlockWidth(nwcv, bpe, bpr)
if blockWidth == 0:
# Solution already rejected in findValidWriteBlockWidth; bail out to
# avoid ZeroDivisionError at "vw // nwcvpi" below.
return False
nwcvpi = int(blockWidth * bpr / bpe)
serials = []
if tlu != uMLds:
serials = range(0, state["LVC%s"%tc])
else:
serials = [state["LVC%s"%tc] * q for q in range(0, max(1, state["NumThreads"] // state["LVC%s"%tc]))]
for serial in serials:
address = caculateLdsWriteAddress(tc, idx, serial, tlu, uMLds, vw, bpe)
for perp in range(0, state["NumLoadsPerpendicular%s"%tc]):
for para in range(0, state["NumLoadsCoalesced%s"%tc]):
sPerp = 0
sPara = 0
for s in range(0, vw // nwcvpi):
if tlu != uMLds:
if wtc:
sPerp = s
else:
if wtc:
sPara = s
offset = caculateLdsWriteOffset(perp, para, sPerp, sPara, tlu, uMLds, bpe, tc, idx)
lLdsBlocks = (address + offset) // lbspp
rLdsBlocks = address // lbspp + offset // lbspp
if 0: #Debug
pad = state["LdsPad%s"%tc]
print(tc, serial, state["UnrollMajorLDS%s"%tc], perp, para, bpe, lbspp, address, offset, address // lbspp * pad * bpe, offset // lbspp * pad * bpe, lLdsBlocks, rLdsBlocks, address + offset + lLdsBlocks * pad * bpe, address + offset + rLdsBlocks * pad * bpe)
if lLdsBlocks != rLdsBlocks:
return False
return True
if state["LdsBlockSizePerPad%s"%tc] != 0 and state["LdsPad%s"%tc] != 0:
idx = 0 if tc == "A" else 1
auto_LdsBlockSizePerPad = auto_LdsBlockSizePerPadA if tc == "A" else auto_LdsBlockSizePerPadB
if not subCheckLdsBlockSizePerPad(tc, idx) and not state["UseGeneralizedNLCOne%s"%tc]:
if auto_LdsBlockSizePerPad:
printWarning("Padded address is inconsistent, set LdsBlockSizePerPad%s=0."%tc)
state["LdsBlockSizePerPad%s"%tc] = 0
else:
reject(state, printRejectionReason, "%s's padded address is inconsistent"%tc)
if(not (state["CustomKernelName"] and state["CustomKernelName"] != "")): #don't check the custom kernel.
checkLdsBlockSizePerPad("A")
checkLdsBlockSizePerPad("B")
# set NoLdsWriteCode if (DirectToVgpr or DirectToLds)A+B is enabled
state["NoLdsWriteCode"] = False
if (state["DirectToVgprA"] or state["DirectToLdsA"]) and (state["DirectToVgprB"] or state["DirectToLdsB"]) \
and (not state["ProblemType"]["Sparse"] or state["DirectToLdsMetadata"] or state["DirectToVgprSparseMetadata"]):
state["NoLdsWriteCode"] = True
# MX case
if state["ProblemType"]["MXBlockA"]:
if not (state["DirectToVgprMXSA"] or state["DirectToLdsMXSA"]):
state["NoLdsWriteCode"] = False
if state["ProblemType"]["MXBlockB"]:
if not (state["DirectToVgprMXSB"] or state["DirectToLdsMXSB"]):
state["NoLdsWriteCode"] = False
elif state["enableTDMA"] and state["enableTDMB"]:
state["NoLdsWriteCode"] = True
# Use64bShadowLimitMX for MX case only
if not (state["ProblemType"]["MXBlockA"] or state["ProblemType"]["MXBlockB"]):
state["Use64bShadowLimitMX"] = False
# enable scheduling GR (in LWcode for PGR2) over barrier sync
if not state["EnableMatrixInstruction"]:
# not valid for non MFMA case
state["ScheduleGROverBarrier"] = 0
else:
numMFMA = state["MIWaveTile"][0] * state["MIWaveTile"][1] * (state["DepthU"] // (state["MatrixInstK"] * state["LocalSplitU"]))
if state["ScheduleGROverBarrier"] == -1:
# auto enable ScheduleGROverBarrier for PGR>=3
# PGR2+tripleLDS case, manual enablement only
state["ScheduleGROverBarrier"] = 0
if state["PrefetchGlobalRead"] >= 3:
# better to avoid applying this logic for smaller MT sizes
# Set threshold as
# PGR3: MT128x64x64 with MT16x16x32x1 (with 4 waves)
# threshold //= 2 for MI32
thresholdMFMA = 128*64*64 / (16*16*32*4) # MI16x16x32x1 4waves
if state["MatrixInstM"] == 32:
thresholdMFMA //= 2
if numMFMA >= thresholdMFMA:
state["ScheduleGROverBarrier"] = 1
if state["ScheduleGROverBarrier"]:
# enable ScheduleGROverBarrier only for
# - NoLdsWriteCode and not DTV, and
# - PGR>=2, and
# - PLR>=1
# - small MT (detail below)
if (not state["NoLdsWriteCode"]) or \
(state["DirectToVgprA"] or state["DirectToVgprB"]) or \
state["PrefetchGlobalRead"] < 2:
state["ScheduleGROverBarrier"] = 0
# disable this logic for very small MT sizes
# Set threshold as MT64x64x64 with MT16x16x32x1 (with 4 waves)
# threshold //= 2 for MI32
thresholdMFMA = 64*64*64 / (16*16*32*4) # MI16x16x32x1 4waves
if state["MatrixInstM"] == 32:
thresholdMFMA //= 2
if numMFMA < thresholdMFMA:
state["ScheduleGROverBarrier"] = 0
# number of minimum GR inc inst per MFMA
# default 1
# Set at least 2 for gfx950 + MI16 + smaller MT case
# Set 3 for MX (number of GRInc is doubled)
if state["MinGRIncPerMfma"] == -1:
state["MinGRIncPerMfma"] = 1
if isa == (9, 5, 0):
if state["EnableMatrixInstruction"] and state["MatrixInstM"] == 16 and state["MatrixInstB"] == 1:
if numMFMA<=16 or (state["ProblemType"]["MXBlockA"] or state["ProblemType"]["MXBlockB"]):
state["MinGRIncPerMfma"] = 3
elif numMFMA<=32:
state["MinGRIncPerMfma"] = 2
# calculate ldsPad
state["LdsPadA"], state["LdsPadB"], state["LdsPadMetadata"], state["LdsPadMXSA"], state["LdsPadMXSB"] = calcLdsPad(isaInfoMap)
if state["GlobalReadVectorWidthA"] * state["ProblemType"]["MacDataTypeA"].numBytes() == 32 and state["LdsPadA"] == 16 // state["ProblemType"]["MacDataTypeA"].numBytes():
if auto_LdsBlockSizePerPadA:
state["LdsBlockSizePerPadA"] = 128
assert(state["LdsPadA"] >= 0)
if state["GlobalReadVectorWidthB"] * state["ProblemType"]["MacDataTypeB"].numBytes() == 32 and state["LdsPadB"] == 16 // state["ProblemType"]["MacDataTypeB"].numBytes():
if auto_LdsBlockSizePerPadB:
state["LdsBlockSizePerPadB"] = 128
assert(state["LdsPadB"] >= 0)
# set ldsbspp = 0 for ldspad = 0
for tc in ['A', 'B']:
if state["LdsPad%s"%tc] == 0:
state["LdsBlockSizePerPad%s"%tc] = 0
if state["TDMInst"]:
for tc in ['A', 'B']:
if state["LdsPad%s"%tc] > 0 and state["LdsBlockSizePerPad%s"%tc] == 0:
reject(state, printRejectionReason, f"TDM requires LdsBlockSizePerPad{tc} != 0 when LdsPad{tc} != 0")
return
# Normalize lds block-size-per-pad fields to native Python int.
assert(int(state["LdsBlockSizePerPadA"]) == state["LdsBlockSizePerPadA"])
assert(int(state["LdsBlockSizePerPadB"]) == state["LdsBlockSizePerPadB"])
assert(int(state["LdsBlockSizePerPadMetadata"]) == state["LdsBlockSizePerPadMetadata"])
state["LdsBlockSizePerPadA"] = int(state["LdsBlockSizePerPadA"])
state["LdsBlockSizePerPadB"] = int(state["LdsBlockSizePerPadB"])
state["LdsBlockSizePerPadMetadata"] = int(state["LdsBlockSizePerPadMetadata"])
if (state["UnrollMajorLDSA"] or state["UnrollMajorLDSB"]) and (not state["EnableMatrixInstruction"]) and (not state["UseDotInstruction"]):
reject(state, printRejectionReason, "UnrollMajorLDS Supports only in EnableMatrixInstruction=1 or dot2 kernel")
(ldsNumBytesA, ldsNumBytesAlignedA) = calcLdsNumBytesAB("A", state["LdsPadA"], state["LdsBlockSizePerPadA"])
(ldsNumBytesB, ldsNumBytesAlignedB) = calcLdsNumBytesAB("B", state["LdsPadB"], state["LdsBlockSizePerPadB"])
(ldsNumBytesMXSA, ldsNumBytesAlignedMXSA) = calcLdsNumBytesAB("MXSA", state["LdsPadMXSA"], state["LdsBlockSizePerPadMXSA"]) if state["ProblemType"]["MXBlockA"] else (0, 0)
(ldsNumBytesMXSB, ldsNumBytesAlignedMXSB) = calcLdsNumBytesAB("MXSB", state["LdsPadMXSB"], state["LdsBlockSizePerPadMXSB"]) if state["ProblemType"]["MXBlockB"] else (0, 0)
(ldsNumBytesMetadata, ldsNumBytesAlignedMetadata) = calcLdsNumBytesM()
state["LdsOffsetA_Blk"] = 0
state["LdsOffsetB_Blk"] = 0
state["LdsOffsetMXSA_Blk"] = 0
state["LdsOffsetMXSB_Blk"] = 0
state["LdsOffsetMetadata_Blk"] = 0
# todo, can the alignment be a power of 2?
state["LdsOffsetA"] = 0
state["LdsNumElementsAlignedA"] = int(ldsNumBytesAlignedA)
state["LdsNumElementsAlignedMXSA"] = int(ldsNumBytesAlignedMXSA)
state["LdsNumElementsAlignedB"] = int(ldsNumBytesAlignedB)
state["LdsNumElementsAlignedMXSB"] = int(ldsNumBytesAlignedMXSB)
state["LdsNumElementsAlignedMetadata"] = int(ldsNumBytesAlignedMetadata)
# check for auto DtlPlusLdsBuf
if state["DtlPlusLdsBuf"] == -1:
if state["PrefetchGlobalRead"] > 2:
state["DtlPlusLdsBuf"] = 1
else:
state["DtlPlusLdsBuf"] = 0
# disable DtlPlusLdsBuf if not applicable
if state["DtlPlusLdsBuf"]:
# DTLA + DTLB only
if not(state["DirectToLdsA"] and state["DirectToLdsB"]):
state["DtlPlusLdsBuf"] = 0
# PGR>=2 only
if state["PrefetchGlobalRead"] < 2:
state["DtlPlusLdsBuf"] = 0
# SIA==3 only
if state["_ScheduleIterAlg"] != 3:
state["DtlPlusLdsBuf"] = 0
# restrict feature combinations
if state["DtlPlusLdsBuf"]:
# force 1LDSBuffer = 0
state["1LDSBuffer"] = 0
# Here, 1LDSBuffer == -1 is not resolved yet.
# (cannot move 1LDSBuffer==-1 resolution code above because of referring ldsNumBytesAB)
# Assuming larger buffer here
numLdsBlk = 1 if state["1LDSBuffer"] == 1 else 2
if state["PrefetchGlobalRead"] >= 3:
# PGR>=3, number of LDS Block is set to PGR
numLdsBlk = state["PrefetchGlobalRead"]
# force 1LDSBuffer = 0
state["1LDSBuffer"] = 0
if state["PrefetchGlobalRead"] >= 2 and state["DtlPlusLdsBuf"]:
# PGR>=2 + DtlPlusLdsBuf case, try to allocate PGR+1 LDSBlk to schedule GR over barrier
numLdsBlk = state["PrefetchGlobalRead"] + 1
def setLdsOffsets(offsetBlk, numLdsBlk, ldsNumBytesB):
if numLdsBlk <= 1:
# 1LDSBuffer case, use 2 to calculate ldsNumBytesAB
numLdsBlk = 2
state["LdsOffsetA_Blk"] = offsetBlk
state["LdsOffsetMXSA_Blk"] = state["LdsOffsetA_Blk"] + state["LdsNumElementsAlignedA"]
state["LdsOffsetMXSB_Blk"] = state["LdsOffsetMXSA_Blk"] + state["LdsNumElementsAlignedMXSA"]
state["LdsOffsetMetadata_Blk"] = state["LdsOffsetMXSB_Blk"] + state["LdsNumElementsAlignedMXSB"]
state["LdsOffsetB_Blk"] = state["LdsOffsetMetadata_Blk"] + state["LdsNumElementsAlignedMetadata"]
ldsNumBytesAB = (numLdsBlk - 2) * offsetBlk + state["LdsOffsetB_Blk"] + ldsNumBytesB
return ldsNumBytesAB
state["ldsNumBytesA"] = ldsNumBytesA
state["ldsNumBytesB"] = ldsNumBytesB
state["ldsNumBytesMXSA"] = ldsNumBytesMXSA
state["ldsNumBytesMXSB"] = ldsNumBytesMXSB
state["ldsNumBytesMetadata"] = ldsNumBytesMetadata
# Half-wave configs need a +4-byte LDS base shift (see LdsPadding).
halfBankShiftA = 0
if wmmaV3 and state.get("enableLDSTrA", False) \
and auto_LdsPadA and auto_LdsBlockSizePerPadA:
if state["ProblemType"]["MacDataTypeA"].numBytes() == 0.5:
halfBankShiftA = get_fp4_mt_config(state["MacroTile0"], "shift",
state["MIWaveTile"][0], state["MIWaveGroup"][0])
elif state["ProblemType"]["MacDataTypeA"].is8bitFloat():
halfBankShiftA = get_fp8_mt_config(state["MacroTile0"], "shift",
state["MIWaveTile"][0], state["MIWaveGroup"][0])
halfBankShiftB = 0
if wmmaV3 and state.get("enableLDSTrB", False) \
and auto_LdsPadB and auto_LdsBlockSizePerPadB:
if state["ProblemType"]["MacDataTypeB"].numBytes() == 0.5:
halfBankShiftB = get_fp4_mt_config(state["MacroTile1"], "shift",
state["MIWaveTile"][1], state["MIWaveGroup"][1])
elif state["ProblemType"]["MacDataTypeB"].is8bitFloat():
halfBankShiftB = get_fp8_mt_config(state["MacroTile1"], "shift",
state["MIWaveTile"][1], state["MIWaveGroup"][1])
state["LdsOffsetA"] = halfBankShiftA
state["LdsOffsetMXSA"] = state["LdsOffsetA"] + state["LdsNumElementsAlignedA"]
state["LdsOffsetMXSB"] = state["LdsOffsetMXSA"] + state["LdsNumElementsAlignedMXSA"]
state["LdsOffsetMetadata"] = state["LdsOffsetMXSB"] + state["LdsNumElementsAlignedMXSB"]
rawLdsOffsetB = state["LdsOffsetMetadata"] + state["LdsNumElementsAlignedMetadata"]
if halfBankShiftB > 0:
# B's lroB must land at 4 mod 8 to keep the half-wave alignment.
if rawLdsOffsetB % 8 != 4:
rawLdsOffsetB += (4 - rawLdsOffsetB % 8) % 8
state["LdsOffsetB"] = rawLdsOffsetB
_oneLdsBufAtEval = state["1LDSBuffer"] # may still be -1 (auto) here; resolved to 0/1 below
_segRes = segIntEval(state)
_segApplicable = _segRes["applicable"] # resolved to LDSSegmentInterleave 0/1 below
state["LDSSegInterleaveOffsets"] = _segRes["offsets"] # consumed by emit sites when applied
_segAligned = _segRes["applicable"] and _segRes["aligned"]
_segReason = _segRes["reason"] # why not applied (may change if budget disables aligned)
if state["PrefetchGlobalRead"]:
offsetBlk = state["LdsOffsetB"] + ldsNumBytesAlignedB
# Buffer-swap delta must be 8-aligned to keep buffer 1 in half-wave mode.
if (halfBankShiftA > 0 or halfBankShiftB > 0) and offsetBlk % 8 != 0:
offsetBlk += 8 - (offsetBlk % 8)
# Aligned interleave grows the per-buffer block; keep it only if it still double-buffers
# within MaxLDS, else disable (a too-tight forced kernel is rejected below, not run baseline).
if _segAligned:
_ok, _inflated = segAlignedBudget(_segRes["blockSpan"], numLdsBlk, offsetBlk, state["MaxLDS"])
if _ok:
offsetBlk = _inflated # already a power of two -> roundup below is a no-op
else:
_segApplicable = False
_segAligned = False
_segReason = "aligned needs more LDS than MaxLDS"
roundupOffsetBlk = int(2**(math.ceil(math.log(offsetBlk, 2)))) if offsetBlk > 0 else 0
if not isaInfoMap[isa].asmCaps["HasWMMA"]:
state["StoreSwapAddr"] = (state["PrefetchGlobalRead"] == 2) and \
(state["1LDSBuffer"] == 0) and numLdsBlk == 2 and \
(offsetBlk + roundupOffsetBlk) > state["MaxLDS"]
else:
state["StoreSwapAddr"] = offsetBlk > 0 and (state["1LDSBuffer"] != 1) and \
(offsetBlk + int(2**(math.ceil(math.log(offsetBlk, 2)))) > state["MaxLDS"])
if offsetBlk > 0 and not state["StoreSwapAddr"] and not state["UseSubtileImpl"] and numLdsBlk == 2:
# Rounds offsetBlk to a power of two to enable inlining {s,v}_xor constants for swapping offsets
# skip ceiling to nearest power of two for numLdsBlk>=3
offsetBlk = roundupOffsetBlk
ldsNumBytesAB = setLdsOffsets(offsetBlk, numLdsBlk, ldsNumBytesB)
if _segAligned:
# setLdsOffsets assumes an [A|B] span, but aligned spreads A1 a segment further,
# so reserve the real per-buffer span (max() never shrinks the baseline).
ldsNumBytesAB = max(ldsNumBytesAB, (numLdsBlk - 1) * offsetBlk + _segRes["blockSpan"])
# decrement numLdsBlk for DtlPlusLdsBuf if it exceeds MaxLDS
# PGR 2 case, reject kernel (need to use StoreSwapAddr in that case)
if state["DtlPlusLdsBuf"] and ldsNumBytesAB > state["MaxLDS"]:
numLdsBlk -= 1
# continue with original logic for PGR2 + numLdsBlk==2
if state["PrefetchGlobalRead"] == 2:
if (offsetBlk + roundupOffsetBlk) > state["MaxLDS"]:
state["StoreSwapAddr"] = True
else:
offsetBlk = roundupOffsetBlk
# re-calculate LDS size with numLdsBlk==2
ldsNumBytesAB = setLdsOffsets(offsetBlk, numLdsBlk, ldsNumBytesB)
# unset DtlPlusLdsBuf
state["DtlPlusLdsBuf"] = 0
else:
ldsNumBytesAB = state["LdsOffsetB"] + ldsNumBytesB
state["NumLdsBlk"] = numLdsBlk
# Resolve the -1/0/1 knob to the applied 0/1 (LDSSI1 always means "applied"; identical
# variants dedup). Defer the one case whose applicability isn't known yet: an unresolved
# 1LDSBuffer (-1) whose only blocker is "needs 1LDSBuffer==0" -- re-decided after it resolves.
_segRequested = state["LDSSegmentInterleave"]
_segDeferForBuf = _oneLdsBufAtEval == -1 and _segReason == "needs 1LDSBuffer==0"
if not _segDeferForBuf:
state["LDSSegmentInterleave"] = 1 if _segApplicable else 0
if _segRequested == 1 and not _segApplicable:
reject(state, printRejectionReason, "LDSSegmentInterleave=1 requested but not applicable: %s" % _segReason)
# lds buffer size for reduction
# if User want to control the LDS usage, we may open this para in the future
ldsNumBytesReduction = state["LocalSplitU"] * state["MacroTile0"] * state["MacroTile1"] * int(state["ProblemType"]["ComputeDataType"].numBytes()) if state["LocalSplitU"] > 1 else 0
state["LocalSplitUReuseLDS"] = 1
if ldsNumBytesReduction > state["MaxLDS"]:
state["LocalSplitUReuseLDS"] = math.ceil(ldsNumBytesReduction / state["MaxLDS"])
# reserve all the LDS to LSU.
ldsNumBytesReduction = state["MaxLDS"]
# lds max occupancy
ldsSizeOccupancy = isaInfoMap[isa].archCaps["DeviceLDS"] // state["MaxOccupancy"]
ldsNumBytesOccupancy = ldsSizeOccupancy
if state["EnableMatrixInstruction"]:
if state["DirectToLds"] and state["1LDSBuffer"]:
reject(state, printRejectionReason, "1LDSBuffer must be 0 for directToLds")
if state["1LDSBuffer"] == -1:
if ldsNumBytesAB <= max(ldsSizeOccupancy,32768) or \
(state["ProblemType"]["ComputeDataType"].numBytes() * state["MacroTile0"] * state["MacroTile1"] > 32768*4 and \
not (ldsNumBytesAB > isaInfoMap[isa].archCaps["DeviceLDS"])):
state["1LDSBuffer"] = 0
else:
state["1LDSBuffer"] = 1
if _segDeferForBuf:
# 1LDSBuffer resolved now; re-run the oracle. A newly-eligible tight interleave is safe (pure
# reorder, no LDS growth); an aligned one needs LDS not reserved above, so a forced request is
# rejected rather than run as baseline.
_segRes2 = segIntEval(state)
if _segRes2["applicable"] and not _segRes2["aligned"]:
state["LDSSegInterleaveOffsets"] = _segRes2["offsets"]
state["LDSSegmentInterleave"] = 1
else:
state["LDSSegmentInterleave"] = 0
if _segRequested == 1:
_segReason2 = "aligned needs LDS reserved before 1LDSBuffer resolution" \
if _segRes2["applicable"] else _segRes2["reason"]
reject(state, printRejectionReason, "LDSSegmentInterleave=1 requested but not applicable: %s" % _segReason2)
if state["1LDSBuffer"]:
if not state["PrefetchGlobalRead"]:
reject(state, printRejectionReason, "PGR=0 already use 1 LDS buffer only")
# Should be able to support as long as NO scheduleLocalWrite
if (not state["_ScheduleIterAlg"] == 2) and (not state["_ScheduleIterAlg"] == 3) and (state["ScheduleLocalWrite"]):
reject(state, printRejectionReason, "1LDSBuffer only support SIA2 or SIA3, or SIA1 without SLW")
state["LdsOffsetA"] = halfBankShiftA
state["LdsOffsetMXSA"] = state["LdsOffsetA"] + state["LdsNumElementsAlignedA"]
rawLdsOffsetB_1LDS = state["LdsOffsetMXSA"] + state["LdsNumElementsAlignedMXSA"]
# Align B for bank conflict avoidance: half-wave (4-byte aligned) or full-wave (8-byte aligned)
if halfBankShiftB > 0:
if rawLdsOffsetB_1LDS % 8 != 4:
rawLdsOffsetB_1LDS += (4 - rawLdsOffsetB_1LDS % 8) % 8
state["LdsOffsetB"] = rawLdsOffsetB_1LDS
state["LdsOffsetMXSB"] = state["LdsOffsetB"] + state["LdsNumElementsAlignedB"]
state["LdsOffsetMetadata"] = state["LdsOffsetMXSB"] + state["LdsNumElementsAlignedMXSB"]
ldsNumBytesAB = state["LdsOffsetMetadata"] + ldsNumBytesMetadata
state["StoreSwapAddr"] = False
# lds size is the greater of the two
ldsNumBytes = max(ldsNumBytesAB, ldsNumBytesReduction, ldsNumBytesOccupancy)
if state["NumElementsPerBatchStore"] == -1:
if ldsNumBytes > 32768 or \
state["ProblemType"]["ComputeDataType"].numBytes() * state["MacroTile0"] * state["MacroTile1"] > 32768*4:
state["NumElementsPerBatchStore"] = 0
state["StorePriorityOpt"] = False
state["StoreSyncOpt"] = 0
state["GroupLoadStore"] = False
else:
state["NumElementsPerBatchStore"] = 16 if not state["ProblemType"]["DataType"].numBytes() == 8 else 1
# Mbsk prefetch optimization
if state["_GlobalAccumulation"] != 'MultipleBufferSingleKernel' and state["AdaptiveGemmGSUA"] == 0:
state["MbskPrefetchMethod"] = 0
elif state["MbskPrefetchMethod"] == -1:
numStoreElements = state["NumElementsPerThread"] // state["StoreVectorWidth"]
state["MbskPrefetchMethod"] = 1 if numStoreElements >= 4 else 0
if state["MbskPrefetchMethod"] == 1:
state["NumMbskPrefetchElements"] = 16
storeRegs = state["StoreVectorWidth"] * state["ProblemType"]["ComputeDataType"].numRegisters()
# exceed 16*4 = 64 VPGRs
if storeRegs > 4:
state["NumMbskPrefetchElements"] //= storeRegs // 4
if state["NumElementsPerBatchStore"] == 0 or state["NumElementsPerBatchStore"] > state["NumMbskPrefetchElements"]:
state["NumElementsPerBatchStore"] = state["NumMbskPrefetchElements"]
if state["StoreRemapVectorWidth"] == -1:
# use de_read_b64 as default in storeRemap to avoid bank conflict
defaultRemap = 8 // int(state["ProblemType"]["DestDataType"].numBytes())
defaultRemap = max(defaultRemap, state["MacroTile0"]//state["WavefrontSize"])
if state["EnableMatrixInstruction"]:
ldsRemapPad = max(defaultRemap, state["MIOutputVectorWidth"])
ldsNumElementsRemapC = (state["MacroTile0"]+ldsRemapPad)* state["MatrixInstN"] * state["MIWaveGroup"][1]
computeBytes = int(state["ProblemType"]["ComputeDataType"].numBytes())
# max(GlobalAccumulation case, non-GlobalAccumulation)
ldsNumElementsRemapC = max(ldsNumElementsRemapC, ldsNumElementsRemapC * (computeBytes / state["ProblemType"]["DestDataType"].numBytes()))
ldsSize = ldsNumElementsRemapC * state["ProblemType"]["DestDataType"].numBytes()
if not math.log(state["MacroTile0"],2).is_integer() or \
ldsSize > state["MaxLDS"] or \
state["SourceSwap"] or \
(state["GlobalSplitU"] > 1 or state["GlobalSplitU"] == -1) and (state["_GlobalAccumulation"] != 'MultipleBuffer') or \
state["MatrixInstBN"] > 1 and state["MatrixInstN"] == 4 :
state["StoreRemapVectorWidth"] = 0
else:
state["StoreRemapVectorWidth"] = defaultRemap
else:
state["StoreRemapVectorWidth"] = defaultRemap
if not state["SourceSwap"]:
if not state["StoreRemapVectorWidth"]:
reject(state, printRejectionReason, "reject to reduce number of kernels")
elif state["VectorWidthA"] > 1:
reject(state, printRejectionReason, "reject to reduce number of kernels")
# GuaranteeNoPartial
if state["ProblemType"]["TLUA"]:
state["GuaranteeNoPartialA"] = state["AssertFree0ElementMultiple"]%state["GlobalReadVectorWidthA"]==0
else:
state["GuaranteeNoPartialA"] = True
if state["ProblemType"]["TLUB"]:
state["GuaranteeNoPartialB"] = state["AssertFree1ElementMultiple"]%state["GlobalReadVectorWidthB"]==0
else:
state["GuaranteeNoPartialB"] = True
state["GuaranteeNoPartialMetadata"] = False if state["ProblemType"]["Sparse"] else True
if state["StoreRemapVectorWidth"]:
if state["SourceSwap"]:
reject(state, printRejectionReason, "SourceSwap not compatible with StoreRemap")
return
if state["VectorWidthA"] > 1 or state["VectorWidthB"] > 1:
reject(state, printRejectionReason, "VW>1 not compatible with StoreRemap")
return
# Sparse problem
if state["ProblemType"]["Sparse"]:
if state["DirectToLdsA"] or state["DirectToLdsB"] or state["DirectToLdsMetadata"]:
if state["ExpandPointerSwap"]:
reject(state, printRejectionReason, "Sparse + DirectToLds + ExpandPointerSwap=1 currently unsupported")
return False
if state["PrefetchGlobalRead"] == 1:
reject(state, printRejectionReason, "Sparse + DirectToLds + PrefetchGlobalRead=1 currently unsupported")
return False
# if state["PrefetchGlobalRead"] and not state["ExpandPointerSwap"]:
# reject(state, printRejectionReason, "Sparse A kernel only support PGR with EPS=1.")
# return
if not isaInfoMap[isa].asmCaps["HasSWMMAC"] and state["EnableMatrixInstruction"] and state["MIArchVgpr"]:
reject(state, printRejectionReason, "Current ISA does not support MIArchVgpr in Sparse kernels.")
return
state["AssertSummationElementMultiple"] = 8
# check if need to use lds init Acc vgprs
state["LdsInitCVgprs"] = False
if isaInfoMap[isa].archCaps["HasAccCD"] and \
state["EnableMatrixInstruction"] and state["StorePriorityOpt"] and \
state["ProblemType"]["DataType"].isDouble():
state["LdsInitCVgprs"] = True
# Resolve InitCIterWmma (-1=auto, 0=disable, 1=force enable).
autoInitCIterWmma = state["EnableMatrixInstruction"] and not state["LdsInitCVgprs"] \
and not state["ForceUnrollSubIter"] \
and not state["ProblemType"]["DataType"].isComplex() \
and not state["ProblemType"]["Sparse"] \
and state["ProblemType"]["NumIndicesSummation"] == 1 \
and isaInfoMap[isa].asmCaps.get("HasWMMA_AccImmZero", False)
if state["InitCIterWmma"] == -1:
state["InitCIterWmma"] = 1 if autoInitCIterWmma else 0
elif state["InitCIterWmma"] == 1 and not autoInitCIterWmma:
reject(state, printRejectionReason,
"InitCIterWmma=1 requires EnableMatrixInstruction/HasWMMA_AccImmZero, single summation index, and not LdsInitCVgprs/ForceUnrollSubIter/Complex/Sparse")
return
# force MIArchVgpr when using WMMA
if state["EnableMatrixInstruction"] and isaInfoMap[isa].asmCaps["HasWMMA"]:
state["MIArchVgpr"] = True
if state["MIArchVgpr"]:
if not state["EnableMatrixInstruction"]:
reject(state, printRejectionReason, "MIArchVgpr only support for MatrixInstruction")
return
if isaInfoMap[isa].asmCaps["HasMFMA"]:
if not (state["ProblemType"]["ComputeDataType"].isDouble() or \
state["ProblemType"]["ComputeDataType"].isSingle() or \
(state["ProblemType"]["ComputeDataType"].isHalf() and state["ProblemType"]["HighPrecisionAccumulate"]) or \
state["ProblemType"]["ComputeDataType"].isInt32() or \
state["ProblemType"]["ComputeDataType"].isComplex()):
reject(state, printRejectionReason, "MIArchVgpr now only support fp64, fp64c, fp32, fp32c, fp16, int8 MatrixInstruction.")
return
#check not support cases and calculate lds resources
ldsNumBytesRemapC = 0
if state["StoreRemapVectorWidth"]:
if not state["EnableMatrixInstruction"]:
reject(state, printRejectionReason, "storeRemap only support MatrixInstruction kernel")
return
if ((state["GlobalSplitU"] > 1 or state["GlobalSplitU"] == -1) and (state["_GlobalAccumulation"] != 'MultipleBuffer' or state["_GlobalAccumulation"] == 'MultipleBufferSingleKernel')) or \
((state["GlobalSplitU"] == 1 or state["GlobalSplitU"] == -1) and state["_GlobalAccumulation"] == 'SingleBuffer'):
reject(state, printRejectionReason, "storeRemap doesn't support GlobalSplitU yet, except GSU algorithm 2")
return
if packedC0 or packedC1:
reject(state, printRejectionReason, "storeRemap doesn't support packedC0 and packedC1 yet")
return
if state["MatrixInstBN"] > 1 and state["MatrixInstN"] == 4:
reject(state, printRejectionReason, "storeRemap doesn't support MI4x4 multi blocks in N direction yet")
return
if not math.log(state["MacroTile0"],2).is_integer():
reject(state, printRejectionReason, "storeRemap only supports power-of-2 MT0")
# TODO - this return should be here, but this is a hotfix,
# Somehow we have a "Validation Failed" kernel in rocBLAS now (SRVW=4 and MT0=96) and this will stop the whole building process
# Actions: 1. Hotfix, comment out this "return" temporarily for that invalidated kernel
# 2. Remove / replace that invalidated kernel
# 3. Put back this return
# 4. How to design a better way to prevent from invalid kernel in rocBLAS?
# return
storeInstMinWidth = 1 # minimum dwordx1
storeInstMaxWidth = 4 # maximum dwordx4
srMinVw = max(storeInstMinWidth, int(storeInstMinWidth/state["ProblemType"]["DestDataType"].numRegisters()))
numReg = max(state["ProblemType"]["DestDataType"].numRegisters(), state["ProblemType"]["ComputeDataType"].numRegisters())
srMaxVw = int(storeInstMaxWidth/numReg)
# FIXME: Add StoreRemapVectorWidthGSU and StoreRemapVectorWidthNonGSU
while srMaxVw < state["StoreRemapVectorWidth"]:
state["StoreRemapVectorWidth"] = state["StoreRemapVectorWidth"] // 2
if srMinVw > state["StoreRemapVectorWidth"] or srMaxVw < state["StoreRemapVectorWidth"]:
reject(state, printRejectionReason, "StoreRemapVectorWidth %u is not allowed for this data type" % state["StoreRemapVectorWidth"])
return
if state["StoreRemapVectorWidth"] * state["WavefrontSize"] < state["MacroTile0"]:
reject(state, printRejectionReason, "storeRemap: Per wave single global write instruction doesn't enough to write one M column." + \
" Please use larger StoreRemapVectorWidth.")
return
if (state["MacroTile0"]*state["MatrixInstN"])//state["MIWaveGroup"][0] < state["StoreRemapVectorWidth"]*state["WavefrontSize"]:
reject(state, printRejectionReason, "storeRemap: number elements of lds less than per wave per local read elements." + \
" Please use smaller StoreRemapVectorWidth.")
return
ldsRemapPad = max(state["StoreRemapVectorWidth"],state["MIOutputVectorWidth"])
ldsNumBytesRemapC = (state["MacroTile0"]+ldsRemapPad)* state["MatrixInstN"] * state["MIWaveGroup"][1]
computeBytes = state["ProblemType"]["ComputeDataType"].numBytes()
multiplierGSU = computeBytes
if state["ProblemType"]["DestDataType"].numBytes() > state["ProblemType"]["DataType"].numBytes():
# Determine ratio of output to input element size.
# SRVW remaps output so we need to scale up resources.
multiplier = state["ProblemType"]["DestDataType"].numBytes()
else:
multiplier = state["ProblemType"]["DataType"].numBytes()
ldsNumBytesRemapCNonGSU = int(ldsNumBytesRemapC * multiplier)
ldsNumBytesRemapCGSU = int(ldsNumBytesRemapC * multiplierGSU)
ldsNumBytesRemapC = int(ldsNumBytesRemapC * max(multiplier, multiplierGSU))
#print("ldsNumBytesRemapC=%u" % ldsNumBytesRemapC)
# if LDS is bound by RemapC (SRVW), then 1LDSBuffer actually doesn't help in SIA3
# since LDS usage couldn't be reduced
if state["1LDSBuffer"] and (state["_ScheduleIterAlg"] == 3) and (ldsNumBytes < ldsNumBytesRemapC):
# TODO- Remove this DataType test condition,
# Currently we do this test is just because we don't want to affect existing logic in rocBLAS
if state["ProblemType"]["DataType"].isInt8():
reject(state, printRejectionReason, "LDS usage is bound be StoreRemap, thus 1LDSBuffer wouldn't have any help. Skip.")
return
ldsNumBytes = max(ldsNumBytes, ldsNumBytesRemapC)
state["LdsOffsetBias"] = 0 # TODO: ldsBiasOffset = ldsNumBytesAB
state["LdsOffsetBiasNonGSU"] = 0
state["LdsOffsetBiasGSU"] = 0
# TODO: Should change name to LdsOffsetEpilogue or something.
if state["StoreRemapVectorWidth"]:
state["LdsOffsetBiasNonGSU"] = ldsNumBytesRemapCNonGSU
state["LdsOffsetBiasGSU"] = ldsNumBytesRemapCGSU
state["LdsOffsetBias"] = ldsNumBytesRemapC
# Calcualte the correct LDS usages
def calcEpilogueTurns(factorDims: List) -> int:
maxTurn = 0
divisor = state["SubGroup0"] * state["SubGroup1"]
for d in factorDims:
turn = math.ceil(state["MacroTile%d"%d] / divisor)
maxTurn = max(maxTurn, turn)
return maxTurn
# Calc the required LDS
vecDT = VectorDataTypes()
biasDim = state["ProblemType"]["UseBias"]
savDim = state["ProblemType"]["UseScaleAlphaVec"]
sAB = state["ProblemType"]["UseScaleAB"] == "Vector"
# Calc LDS for Bias
maxTurn = 0
if biasDim == 1:
maxTurn = calcEpilogueTurns([0])
elif biasDim == 2:
maxTurn = calcEpilogueTurns([1])
elif biasDim == 3:
maxTurn = calcEpilogueTurns([0, 1])
vecDT.bias(0).turn = maxTurn
vecDT.bias(1).turn = maxTurn
# Calc LDS for SAV
maxTurn = 0
if savDim == 1:
maxTurn = calcEpilogueTurns([0])
elif savDim == 2:
maxTurn = calcEpilogueTurns([1])
elif savDim == 3:
maxTurn = calcEpilogueTurns([0, 1])
vecDT.scaleAlpha(0).turn = maxTurn
vecDT.scaleAlpha(1).turn = maxTurn
# Calc LDS for ScaleA, ScaleB
if sAB:
vecDT.scaleA.turn = calcEpilogueTurns([0])
vecDT.scaleB.turn = calcEpilogueTurns([1])
epilogueSize = 0
# Bias
if state["ProblemType"]["UseBias"]:
# Currently all offsets starts from 0
if state["ProblemType"]["Gradient"]:
if state["ProblemType"]["BiasSrc"] == "A":
tile01 = state["ProblemType"]["Index01A"]
elif state["ProblemType"]["BiasSrc"] == "B":
tile01 = state["ProblemType"]["Index01B"]
elif state["ProblemType"]["BiasSrc"] == "D":
tile01 = -1
else:
assert 0 and "Unsupported tile01 for bias lds calculation."
# Don't need to calculate lds bias
if tile01 > -1:
maxKId = state["WavefrontSize"] // ((state["MatrixInstM"] if (tile01 == 0) else state["MatrixInstN"]) * state["MatrixInstB"])
for dataType in state["ProblemType"]["BiasDataTypeList"]:
epilogueSize = max(epilogueSize, int(state["MacroTile%d"%tile01] * maxKId * dataType.numBytes())) # TODO- GetTurn ?
else:
epilogueSize = int(state["NumThreads"] * state["ProblemType"]["ComputeDataType"].numBytes() * vecDT.bias(0).turn)
# Calculate max ldsNumBytes for other epilogues
if state["ProblemType"]["UseScaleAlphaVec"]:
epilogueSize += int(state["NumThreads"] * state["ProblemType"]["ComputeDataType"].numBytes() * vecDT.scaleAlpha(0).turn)
if state["ProblemType"]["UseScaleAB"] == "Vector":
epilogueSize += int(state["NumThreads"] * state["ProblemType"]["ComputeDataType"].numBytes() * (vecDT.scaleA.turn + vecDT.scaleB.turn))
ldsNumBytes = max(ldsNumBytes, state["LdsOffsetBias"] + epilogueSize)
state["LdsBytesNoAmax"] = ldsNumBytes
if state["ProblemType"]["OutputAmaxD"]:
# used in reduce inter wave
# 4 data * half_wave_num * amax bytePerE
num_workItems = state["NumThreads"]
half_wave_size = state["WavefrontSize"] // 2
amaxBPE = int(state["ProblemType"]["DataTypeAmaxD"].numBytes())
ldsAmaxDBytes = 4 * (num_workItems // half_wave_size) * amaxBPE
ldsNumBytes += ldsAmaxDBytes
state["LdsNumBytes"] = ldsNumBytes
ldsSize = ldsNumBytes
if ldsSize > state["MaxLDS"]:
reject(state, printRejectionReason, "Kernel Uses %u > %u bytes of LDS" % ( ldsSize, state["MaxLDS"]))
state["ValidDepthU"] = False
return
# LoopUnroll = DepthU / LocalSplitU
if "LocalSplitU" in state:
state["LoopUnroll"] = state["DepthU"] // state["LocalSplitU"]
if state["LoopUnroll"] * state["LocalSplitU"] != state["DepthU"]:
state["Valid"] = False
if state["KernelLanguage"] != "Assembly" and state["InnerUnroll"] != 1:
reject(state, printRejectionReason, "InnerUnroll only supported on assembly")
state["LoopUnroll"] //= state["InnerUnroll"]
if 0:
print("info: ", pvar(state, "LoopUnroll"), " LDS Stats:", pvar(state, "LdsOffsetA"), pvar(state, "LdsOffsetB"))
print("info: ", pvar(state["ProblemType"], "TLUA"), \
pvar(state, "NumLoadsCoalescedA"), pvar(state, "NumLoadsPerpendicularA"), \
pvar(state, "LSCA"), pvar(state, "LSPA"))
print("info:", pvar(state["ProblemType"], "TLUB"), \
pvar(state, "NumLoadsCoalescedB"), pvar(state, "NumLoadsPerpendicularB"), \
pvar(state, "LSCB"), pvar(state, "LSPB"))
state["LoopIters"] = state["LoopUnroll"]
if state["EnableMatrixInstruction"]:
state["LoopIters"] //= state["MatrixInstK"]
elif state["UseDotInstruction"]:
# dot2
state["LoopIters"] //= (state["NumDotElements"] * state["NumWaveSplitK"])
if state["LoopIters"] < 1:
reject(state, printRejectionReason, "LoopIters need to greater than 0")
return
# Reject SIA3 + PLR>0 + PGR2 + BufferLoad=0 when LoopIters <= 1.
# With flat addressing the prefetch scheduling duplicates a load.
# BufferLoad=1 is unaffected because SRD-based offsets avoid the duplication.
if not state["BufferLoad"] \
and state["ScheduleIterAlg"] == 3 and state["PrefetchLocalRead"] > 0 \
and state["PrefetchGlobalRead"] == 2 and state["LoopIters"] <= 1:
reject(state, printRejectionReason, "BufferLoad=0 with ScheduleIterAlg=3, PrefetchLocalRead and PrefetchGlobalRead=2 requires LoopIters > 1")
return
# Since we use PLR >= LoopIters for allocating numberOfIters vgprBuffer for a while
# we need to support both PLR >= LoopIters and CLR parameter for solutions in rocBLAS
if state["ClusterLocalRead"] and state["PrefetchLocalRead"] >= state["LoopIters"] and not state["_ScheduleIterAlg"] == 2 and not state["ForceUnrollSubIter"]:
# Reject configuration: DTV enabled on one side is incompatible with PLR = 0
if state["DirectToVgprA"] ^ state["DirectToVgprB"]:
reject(state, printRejectionReason, "DirectToVgpr does not work with PrefetchLocalRead(%u) >= LoopIters(%u)"%(state["PrefetchLocalRead"], state["LoopIters"]))
return
state["ClusterLocalRead"] = 0
state["PrefetchLocalRead"] = 0
if not state["EnableMatrixInstruction"]:
state["ClusterLocalRead"] = 0
# dot2: allow PLR=1
if state["UseDotInstruction"]:
state["PrefetchLocalRead"] = min(state["PrefetchLocalRead"], state["LoopIters"]-1)
if state["LoopIters"] % (state["PrefetchLocalRead"]+1) != 0:
reject(state, "dot2 kernel does not support LoopIters(%u) %% (PLR+1)(%u) != 0" % (state["LoopIters"], state["PrefetchLocalRead"]+1))
# reject iterations are not enough to use wider local read
if state["EnableMatrixInstruction"] and state["PrefetchLocalRead"] > 0:
# Multiple = WLR-size / input-size = how many iters could be covered by one WLR ?
wlrMultiple = state["LocalReadVectorWidthA"]//state["MIInputPerThreadA"]
# NOTE: wlrmultiple can be 0 for new MFMA
if not state["ProblemType"]["Sparse"] and not state["UseF32XEmulation"] and not(state["ProblemType"]["DataType"].is8bitFloat() and (state["MatrixInstK"] in [64, 128,])) and (not isaInfoMap[isa].asmCaps["HasWMMA_V3"]):
if wlrMultiple == 0:
reject(state, printRejectionReason, "LocalReadVectorWidthA %u is less than MIInputA" % (state["LocalReadVectorWidthA"]))
return
# for example, if the original ds_read is b32...
# 1. if LoopIters = 5 (b32 x 5 times), WLR-Multiple = 2 (b64), then we can fit the WLR
# 2. if LoopIters = 2 (b32 x 2 times), WLR-Multiple = 4 (b128), this is not allowed
# 3. if LoopIters = 2 (b32 x 2 times), WLR-Multiple = 2 (b64), this is allowed
if wlrMultiple and state["LoopIters"] % wlrMultiple != 0:
reject(state, printRejectionReason, "LocalReadVectorWidthA %u cannot be distributed evenly, LoopIters %u should be divisible by WLR-Multiple %u" \
% (state["LocalReadVectorWidthA"], state["LoopIters"], wlrMultiple))
if state["LoopIters"] - (state["PrefetchLocalRead"] * wlrMultiple) < 0 :
reject(state, printRejectionReason, "with PrefetchLocalRead %u LoopIters %u LocalReadVectorWidthA %u, not enough LoopIters to prefetch %ux%u iterations, " \
% (state["PrefetchLocalRead"],state["LoopIters"],state["LocalReadVectorWidthA"], state["PrefetchLocalRead"] , wlrMultiple) )
# Multiple = WLR-size / input-size = how many iters could be covered by one WLR ?
wlrMultiple = state["LocalReadVectorWidthB"]//state["MIInputPerThreadB"]
# NOTE: wlrmultiple can be 0 for new MFMA
if not state["ProblemType"]["Sparse"] and not state["UseF32XEmulation"] and not(state["ProblemType"]["DataType"].is8bitFloat() and (state["MatrixInstK"] in [64, 128,])) and (not isaInfoMap[isa].asmCaps["HasWMMA_V3"]):
if wlrMultiple == 0:
reject(state, printRejectionReason, "LocalReadVectorWidthB %u is less than MIInputB" % (state["LocalReadVectorWidthB"]))
return
# for example, if the original ds_read is b32...
# 1. if LoopIters = 5 (b32 x 5 times), WLR-Multiple = 2 (b64), then we can fit the WLR
# 2. if LoopIters = 2 (b32 x 2 times), WLR-Multiple = 4 (b128), this is not allowed
# 3. if LoopIters = 2 (b32 x 2 times), WLR-Multiple = 2 (b64), this is allowed
if wlrMultiple and state["LoopIters"] % wlrMultiple != 0:
reject(state, printRejectionReason, "LocalReadVectorWidthB %u cannot be distributed evenly, LoopIters %u should be divisible by WLR-Multiple %u" \
% (state["LocalReadVectorWidthB"], state["LoopIters"], wlrMultiple))
if state["LoopIters"] - (state["PrefetchLocalRead"] * wlrMultiple) < 0 :
reject(state, printRejectionReason, "with PrefetchLocalRead %u LoopIters %u LocalReadVectorWidthB %u, not enough LoopIters to prefetch %ux%u iterations, " \
% (state["PrefetchLocalRead"],state["LoopIters"],state["LocalReadVectorWidthB"], state["PrefetchLocalRead"] , wlrMultiple) )
if state["PrefetchGL2"] > 0:
if not isaInfoMap[isa].asmCaps["HasGlobalPrefetch"]:
reject(state, printRejectionReason, "ISA %s does not support global prefetch" % isa)
return
# TODO: support staggerU if needed
_disableRuntimeStaggerU(state)
# TODO: support GSU if needed
state["InternalSupportParams"]["SupportUserGSU"] = False
if state["GlobalSplitU"] > 1 or state["GlobalSplitU"] == -1:
reject(state, printRejectionReason, "Currently PrefetchGL2 does not support GSU")
return
if state["StreamK"] != 0:
reject(state, printRejectionReason, "PrefetchGL2 does not support Stream-K")
return
if state["ProblemType"]["Batched"] and not state["ProblemType"]["StridedBatched"]:
reject(state, printRejectionReason, "PrefetchGL2 does not support general batch")
return
if state["ProblemType"]["Sparse"]:
if state["DirectToVgprSparseMetadata"]:
reject(state, printRejectionReason, "PrefetchGL2 with Sparse requires DirectToVgprSparseMetadata=0 (TDM metadata path)")
return
# # reject conditions with lower performance
# if state["ScheduleIterAlg"] == 2 and \
# (state["ExpandPointerSwap"] != 1 or state["LoopIters"] != 1 or state["ScheduleGlobalRead"] != 1):
# reject(state, printRejectionReason, "ScheduleIterAlg 2 only work with EPS1_SGR1, LoopIter=1")
# reject for PGR>=3
if state["PrefetchGlobalRead"] >= 3:
# DTLA + DTLB only
if not(state["DirectToLdsA"] and state["DirectToLdsB"]):
reject(state, printRejectionReason, "PrefetchGlobalRead>=3 Supports only DirectToLdsA and DirectToLdsB")
# does not work with PLR=0
if state["PrefetchLocalRead"] == 0:
reject(state, printRejectionReason, "PrefetchGlobalRead>=3 Supports only PrefetchLocalRead >= 1")
# support SIA==3 only
if state["_ScheduleIterAlg"] != 3:
reject(state, printRejectionReason, "PrefetchGlobalRead>=3 Supports only ScheduleIterAlg == 3")
# TODO: enable PGR>=3 for Sparse
if state["ProblemType"]["Sparse"]:
reject(state, printRejectionReason, "PrefetchGlobalRead>=3 + Sparse is not supported yet")
if state["TransposeLDS"] == 1:
if not state["EnableMatrixInstruction"]:
reject(state, printRejectionReason, "TransposeLds Supports only in MatrixInstruction=1")
if state["ProblemType"]["TLUA"] and state["ProblemType"]["TLUB"]:
# TODO: Now in rocBLAS, lot of logic yamls are Type=NT and TLDS=1? Why aren't they rejected and how to get rid of them?
reject(state, printRejectionReason, "TransposeLds requires TLUA=0 or TLUB=0")
if state["EnableMatrixInstruction"]:
# enable widerLocalRead
if state["LocalReadVectorWidthA"] > state["MIInputPerThreadA"]:
# wider localRead support 2 types
# 1. prefetch all lds to register
# 2. using larger InnerUnroll
if not (state["PrefetchLocalRead"] >= state["LoopIters"] and state["InnerUnroll"] == 1) and \
not state["ClusterLocalRead"] and \
not state["InnerUnroll"] >= state["LocalReadVectorWidthA"] // state["MIInputPerThreadA"]:
reject(state, printRejectionReason, "wider localRead only support ClusterLocalRead or (InnerUnroll > WiderLocalReadxN)")
if state["LocalReadVectorWidthB"] > state["MIInputPerThreadB"]:
# wider localRead support 2 types
# 1. prefetch all lds to register
# 2. using larger InnerUnroll
if not (state["PrefetchLocalRead"] >= state["LoopIters"] and state["InnerUnroll"] == 1) and \
not state["ClusterLocalRead"] and \
not state["InnerUnroll"] >= state["LocalReadVectorWidthB"] // state["MIInputPerThreadB"]:
reject(state, printRejectionReason, "wider localRead only support ClusterLocalRead or (InnerUnroll > WiderLocalReadxN)")
if state["GlobalReadPerMfma"] > 1 and state["PrefetchGlobalRead"] >= 2:
reject(state, printRejectionReason, "GlobalReadPerMfma need to be 1 if PGR>=2")
if state["UseInstOffsetForGRO"] == -1:
state["UseInstOffsetForGRO"] = 1 if state["DirectToLds"] else 0
state["ULSGRODoubleG2L"] = 0
if state["UnrollLoopSwapGlobalReadOrder"] == 1:
bpeA = state["ProblemType"]["DataType"].numBytes()
bpr = 4
numVgprG2LA = roundUp((state["NumLoadsCoalescedA"] * state["NumLoadsPerpendicularA"] * \
state["GlobalReadVectorWidthA"] * bpeA) / (float)(bpr))
numVgprG2LB = roundUp((state["NumLoadsCoalescedB"] * state["NumLoadsPerpendicularB"] * \
state["GlobalReadVectorWidthB"] * bpeA) / (float)(bpr))
if numVgprG2LA % 2 == 1 or numVgprG2LB % 2 == 1:
reject(state, printRejectionReason, "G2LA/B vgpr has bubble inside. Cannot use UnrollLoopSwapGlobalReadOrder=1.")
if state["GlobalReadVectorWidthA"] != state["GlobalReadVectorWidthB"]:
# TODO: Add a configuration to schedule better.
state["ULSGRODoubleG2L"] = 1
minGRVW = min(state["GlobalReadVectorWidthA"], state["GlobalReadVectorWidthB"])
if minGRVW * bpeA < 4:
# G2LA/B vgpr index will jump.
state["ULSGRODoubleG2L"] = 1
if state["ExpandPointerSwap"] == 1:
reject(state, printRejectionReason, "ExpandPointerSwap need to be 0 if UnrollLoopSwapGlobalReadOrder")
if state["PrefetchGlobalRead"] < 2:
reject(state, printRejectionReason, "PrefetchGlobalRead need to be >=2 if UnrollLoopSwapGlobalReadOrder")
if state["ProblemType"]["DataTypeA"].numBytes() != state["ProblemType"]["DataTypeB"].numBytes():
reject(state, printRejectionReason, "UnrollLoopSwapGlobalReadOrder doesn't support mixed precision.")
if state["ProblemType"]["MXBlockA"] and (not state["DirectToLdsMXSA"]) or state["ProblemType"]["MXBlockB"] and (not state["DirectToLdsMXSB"]):
reject(state, printRejectionReason, "UnrollLoopSwapGlobalReadOrder doesn't support MX + non DTL")
if state["ExpandPointerSwap"] == 1 and state["LDSTrInst"]:
reject(state, printRejectionReason, "LDSTrInst + ExpandPointerSwap not supported")
# guard against out of bounds reads
# None: don't guard against ou
# ShiftPtr: shift read pointers to be in bounds, then unshift registers (source & assembly),
# ShiftPtr does not support very small problem dims < global load vector width since the shift
# would move outside the array bounds.
# If GLVW==1 or Assert*ElementMultiple for the coalesced dim is > GRVW, then shifting is not
# necessary and the shift/unshift code will not be generated
state["EdgeType"] = "ShiftPtr" # Use ShiftPtr by default
if state["enableTDMA"] and state["enableTDMB"]:
state["EdgeType"] = "None"
# Precise bounds check uses the "num_records" field in the buffer to
# precisely detect when we are inbounds or not. Only a one-dimensional
# check is used since this is faster and also for computation we only
# need to ensure that none of the loads fault. threads which are
# computing bogus sections of the C tile will later be ignored.
# precise checking only works when all elements of the load are in-bounds
# since if the vload crosses boundary we ignore all components not just the
# ones that are OOB. See comments for groOffsetInMacroTile in KernelWriterAssembly.py
#
# So check for the cases where the unroll loop can
# generate partial loads here and reject PBC solutions:
# For non-TLU the free dim is in perp dim - should always be TRUE? TODO
#--
# ShiftPtr can't use UseSgprForGRO since it needs to modify the VGPR pointers
if bufferLoad and state["_UseSgprForGRO"] and state["EdgeType"]=="ShiftPtr":
if not state["GuaranteeNoPartialA"] or not state["GuaranteeNoPartialB"] or not state["GuaranteeNoPartialMetadata"]:
state["_UseSgprForGRO"] = False
#reject(state, printRejectionReason, "PBC with wide load has insufficient overlap guarantees- try GRVW=1 or adding appropriate Assert*ElementMultiple")
if state["EnableMatrixInstruction"]:
cont1 = not state["GuaranteeNoPartialB"]
cont2 = ((state["MatrixInstN"] % state["GlobalReadVectorWidthB"]) != 0)
if cont1 and cont2:
reject(state, printRejectionReason, "MatrixInstN %u %% GlobalReadVectorWidthB %u must be 0" % \
(state["MatrixInstN"], state["GlobalReadVectorWidthB"]))
else: # mac
# if not bufferLoad or not state["GuaranteeNoPartialA"]:
# Restrict GRVW/VW combos so shift-ptr logic will work
if state["GlobalReadVectorWidthA"] > 1 \
and state["GlobalReadVectorWidthA"] != state["VectorWidthA"]:
reject(state, printRejectionReason, "GlobalReadVectorWidthA %u must be == VectorWidthA %u or == 1" % \
(state["GlobalReadVectorWidthA"], state["VectorWidthA"]))
if state["GlobalReadVectorWidthB"] > 1 \
and state["GlobalReadVectorWidthB"] != state["VectorWidthB"]:
reject(state, printRejectionReason, "GlobalReadVectorWidthB %u must be == VectorWidthB %u or == 1" % \
(state["GlobalReadVectorWidthB"], state["VectorWidthB"]))
# Use SGPR to store an offset from GlobalReadOffsetA+0.
# (as opposed to using dedicated VGPR for each GRO
# Requires preciseBounds check since we rely on the buffer bounds check, not
# individual vector registers doing bounds compares.
if state["_UseSgprForGRO"] == 1 and (state["ProblemType"]["SwizzleTensorA"] or state["ProblemType"]["SwizzleTensorB"]):
reject(state, printRejectionReason, "UseSgprForGRO for Swizzle is not supported")
if state["_UseSgprForGRO"] == -1:
# Don't use SGPR if it looks like we might not have enough - better to leave PBC enabled even if we have to use VGPR
# 40 is based on current SGPR usage, this may need to be tuned in the future:
numLoadsA = state["NumLoadsCoalescedA"]*state["NumLoadsPerpendicularA"]
numLoadsB = state["NumLoadsCoalescedB"]*state["NumLoadsPerpendicularB"]
numLoadMXSA = state["NumLoadsCoalescedMXSA"]*state["NumLoadsPerpendicularMXSA"] if state["ProblemType"]["MXBlockA"] else 0
numLoadMXSB = state["NumLoadsCoalescedMXSB"]*state["NumLoadsPerpendicularMXSB"] if state["ProblemType"]["MXBlockB"] else 0
numLoadsM = 0
if state["ProblemType"]["Sparse"] and not state["DirectToVgprSparseMetadata"]:
numLoadsM = state["NumLoadsCoalescedMetadata"]*state["NumLoadsPerpendicularMetadata"]
if (numLoadsA + numLoadsB + numLoadsM + numLoadMXSA + numLoadMXSB > 35) or state["ProblemType"]["SwizzleTensorA"] or state["ProblemType"]["SwizzleTensorB"]:
#print "info: Disabling UseSgprForGRO since predicting too many SGPR will be used"
state["_UseSgprForGRO"] = 0
else:
state["_UseSgprForGRO"] = 1
if packedC0 and not state["GuaranteeNoPartialA"]:
reject(state, printRejectionReason, "packedC0 requires GuaranteeNoPartialA")
if packedC1 and not state["GuaranteeNoPartialB"]:
reject(state, printRejectionReason, "packedC1 requires GuaranteeNoPartialB")
if packedC0 or packedC1:
state["_UseSgprForGRO"] = 0
if state["EdgeType"] != "ShiftPtr":
reject(state, printRejectionReason, "Packed dims requires EdgeType==ShiftPtr")
if state["KernelLanguage"] == "Assembly":
if not bufferLoad:
reject(state, printRejectionReason, "Packed dims for Assembly requires BufferLoad")
if packedC0: # VectorWidth must not span tensor dim
if state["KernelLanguage"] == "Source":
if state["AssertFree0ElementMultiple"]<state["VectorWidthA"]:
reject(state, printRejectionReason, "packedC0 Source requires AF0EM>=VectorWidth (for loads and stores)")
else:
if state["AssertFree0ElementMultiple"]<state["VectorWidthA"]\
or state["AssertFree0ElementMultiple"] == 1:
if state["VectorStore"] <= 0:
state["_VectorStore"] = 0
else:
reject(state, printRejectionReason, "packedC0 Assembly requires AF0EM>=VectorWidth or not VectorStore (for stores)")
state["AssignedDerivedParameters"] = True
# Set E
if state["ProblemType"]["UseE"]:
if (state["_GlobalAccumulation"] == 'SingleBuffer') and (state["GlobalSplitU"] > 1 or state["GlobalSplitU"] == -1):
reject(state, printRejectionReason, "GlobalSplitU > 1 only compatible with MultipleBuffer")
if len(state["PackedC1IndicesX"]) > 1:
reject(state, printRejectionReason, "Use E does not support len(PackedC1IndicesX) > 1.")
if not state["BufferStore"]:
reject(state, printRejectionReason, "Use E only supports BufferStore due to no suppress no store.")
if state["StoreRemapVectorWidth"] and (state["GlobalSplitU"] == 1 or state["GlobalSplitU"] == -1):
reject(state, printRejectionReason, "Use E does not support StoreRemapVectorWidth if GSU == 1.")
if state["GroupLoadStore"]:
reject(state, printRejectionReason, "Use E does not support GroupLoadStore.")
# Activation
# Function call is set to false if GSU != 1 or Activation is not fused or ActivationType is not All.
if not (state["ActivationFused"] and state["ProblemType"]["ActivationType"] in ['all', 'hipblaslt_all']) \
and state["ActivationFuncCall"]:
state["ActivationFuncCall"] = False
if state["ActivationAlt"]:
reject(state, printRejectionReason, "Currently does not accept ActivationAlt.")
# Bias reduction
if state["ProblemType"]["UseBias"] and state["ProblemType"]["Gradient"]:
if (state["_GlobalAccumulation"] == 'SingleBuffer') and (state["GlobalSplitU"] > 1 or state["GlobalSplitU"] == -1):
reject(state, printRejectionReason, "GlobalSplitU > 1 only compatible with MultipleBuffer for bias reduction")
if len(state["PackedC1IndicesX"]) > 1:
reject(state, printRejectionReason, "Bias reduction does not support len(PackedC1IndicesX) > 1.")
if not state["BufferStore"]:
reject(state, printRejectionReason, "Bias reduction only supports BufferStore due to no suppress no store.")
if state["StoreRemapVectorWidth"] and (state["GlobalSplitU"] == 1 or state["GlobalSplitU"] == -1):
reject(state, printRejectionReason, "Bias reduction does not support StoreRemapVectorWidth if GSU == 1.")
if state["GroupLoadStore"]:
reject(state, printRejectionReason, "Bias reduction does not support GroupLoadStore.")
# Bias and ScaleAlphaVec
if state["ProblemType"]["UseBias"] != 0 and state["ProblemType"]["UseScaleAlphaVec"] != 0 and state["ProblemType"]["UseBias"] != state["ProblemType"]["UseScaleAlphaVec"]:
reject(state, printRejectionReason, "When both UseBias and UseScaleAlphaVec are enabled then UseBias and UseScaleAlphaVec must have same settings.")
# ScaleAB or ScaleABVec
if state["ProblemType"]["DataTypeA"] != state["ProblemType"]["MacDataTypeA"] and \
state["ProblemType"]["DataTypeB"] != state["ProblemType"]["MacDataTypeB"] and \
state["ProblemType"]["UseScaleAB"] == "Vector":
reject(state, "Currently does not support using scaleABVec if DataTypeA != MacDataTypeA and DataTypeB != MacDataTypeB.")
if state["ProblemType"]["UseScaleAB"] and state["OptNoLoadLoop"]:
# Hard to check alpha == 1.0 directly
# Turn off ONLL for now
# TODO: support ONLL if necessary
state["OptNoLoadLoop"] = 0
# if state["GlobalSplitU"] > 1 or state["GlobalSplitU"] == -1:
# if state["ProblemType"]["SupportUserArgs"] and state["_GlobalAccumulation"] != 'MultipleBufferSingleKernel':
# reject(state, printRejectionReason, "Currently SupportUserArgs does not support GSU > 1.")
if state["_GlobalAccumulation"] == 'MultipleBufferSingleKernel':
if state["NumElementsPerBatchStore"] == 1:
reject(state, printRejectionReason, "too many store at MultipleBufferSingleKernel direct reject")
if state["ProblemType"]["UseScaleCD"]:
reject(state, printRejectionReason, "MultipleBufferSingleKernel not support UseScaleCD yet")
if state["ProblemType"]["UseE"]:
reject(state, printRejectionReason, "MultipleBufferSingleKernel not support UseE yet")
if state["ProblemType"]["BiasSrc"] != "D":
reject(state, printRejectionReason, "MultipleBufferSingleKernel not support BiasSrc not D yet")
if state["ProblemType"]["DataType"].isDouble():
reject(state, printRejectionReason, "MultipleBufferSingleKernel not support " + str(state["ProblemType"]["DataType"]) + " yet")
#Need to force disabling PreloadKernArgs if compiler does not support
#Can not just reject the solution since the user library may find any solutions
if state["PreloadKernArgs"]:
if ((rocmVersion.major < 6 or (rocmVersion.major == 6 and rocmVersion.patch < 32650)) or \
not (isa == (9, 0, 10) or isa[:2] == (9, 4) or isa == (9, 5, 0) or isa == (12, 5, 0))):
#print("Force to Disable PreloadKernArgs since this hipcc version doesn't support",)
state["PreloadKernArgs"] = False
# change negative ExtraLatencyForLR to 0 for non DirectToVgpr
if state["ExtraLatencyForLR"] < 0 and not (state["DirectToVgprA"] or state["DirectToVgprB"]):
state["ExtraLatencyForLR"] = 0
########################################
@ staticmethod
def getParametersIndented(state, indent):
s = ""
s += "%sProblemType: %s\n" % (indent, str(state["ProblemType"]))
for key in sorted(state):
s += "%s%s: %s\n" % (indent, str(key), str(state[key]))
return s
class NonprimitiveParameterValueException(Exception):
pass
##########################
# make class look like dict
def keys(self):
return list(self._state.keys())
def __len__(self):
return len(self._state)
def __iter__(self):
return iter(self._state)
def __getitem__(self, key):
return self._state[key]
def __setitem__(self, key, value):
self._name = None
self._state[key] = value
def __str__(self):
if self._name is None:
self._name = getSolutionNameFull(self._state, self.splitGSU)
return self._name
def __repr__(self):
return self.__str__()
def getAttributes(self):
return self._state
def __hash__(self):
deviceNames = str(self._state.get("DeviceNames", ""))
return hash(str(self) + self._state.get("codeObjectFile", "") + deviceNames)
def __eq__(self, other):
if not isinstance(other, Solution):
return False
if str(self) != str(other):
return False
# Check DeviceNames to prevent deduplication of solutions for different devices
selfDeviceNames = self._state.get("DeviceNames", None)
otherDeviceNames = other._state.get("DeviceNames", None)
return selfDeviceNames == otherDeviceNames
def __ne__(self, other):
result = self.__eq__(other)
if result is NotImplemented:
return result
return not result