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

829 lines
32 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.
#
################################################################################
from .CustomKernels import getCustomKernelConfig
from rocisa.enum import DataTypeEnum
from . import SolutionLibrary
from .CustomYamlLoader import load_yaml_stream
from Tensile import __version__
from Tensile.Common import printExit, printWarning, print2, \
versionIsCompatible, IsaInfo
from Tensile.Common.TimingInstrumentation import timing_context
from Tensile.Common.Architectures import gfxToIsa
from Tensile.SolutionStructs import Solution, ProblemSizes
from Tensile.SolutionStructs.Solution import getTypeMismatchCollector, resetTypeMismatchCollector
from Tensile.SolutionStructs.Problem import ProblemType, problemTypeToEnum
from typing import IO, NamedTuple, List, Dict, Optional
from Tensile.SolutionStructs.Solution import BiasTypeArgs, ActivationArgs, GateTypeArgs
import io
import os
import sys
import subprocess
import re
import zlib
try:
import orjson as json
except ImportError:
try:
import ujson as json
print2("orjson not installed. Fallback to ujson.")
except ImportError:
try:
import simplejson as json
print2("orjson, ujson not installed. Fallback to simplejson.")
except ImportError:
import json
print2("orjson, ujson, simplejson not installed. Fallback to json.")
try:
import yaml
except ImportError:
printExit(
"You must install PyYAML to use Tensile (to parse config files). See http://pyyaml.org/wiki/PyYAML for installation instructions."
)
try:
from yaml import CSafeLoader as yamlLoader
except ImportError:
from yaml import SafeLoader as yamlLoader
printWarning("CSafeLoader not installed. Fallback to SafeLoader.")
try:
from yaml import CSafeDumper as yamlDumper
except ImportError:
from yaml import SafeDumper as yamlDumper
printWarning("CSafeDumper not installed. Fallback to SafeDumper.")
# Custom YAML loader that preserves int type for 0 and 1 (doesn't auto-convert to bool)
# This allows type validation to catch int-vs-bool mismatches in YAML files.
class StrictTypeLoader(yamlLoader):
"""YAML loader that does NOT auto-convert 0/1 to False/True.
Standard YAML parsers treat 0 and 1 as booleans, but we want to preserve
the actual type written in the YAML to catch type mismatches during validation.
Accepts both YAML-standard (true/false) and Python-style (True/False) booleans.
"""
pass
# Remove the implicit bool resolver for integers
# By default, YAML treats various forms as bool (yes/no, true/false, on/off, 0/1)
# We remove the rule that treats plain scalars matching bool patterns as booleans
# This forces explicit 'true'/'false' for booleans and preserves 0/1 as integers
StrictTypeLoader.yaml_implicit_resolvers = {
k: [r for r in v if r[0] != 'tag:yaml.org,2002:bool']
for k, v in yamlLoader.yaml_implicit_resolvers.items()
}
# Add back a custom bool resolver that matches true/false/True/False but NOT 0/1
# This regex matches: true, false, True, False (but not yes, no, on, off, 0, 1)
StrictTypeLoader.add_implicit_resolver(
'tag:yaml.org,2002:bool',
re.compile(r'^(?:true|false|True|False)$', re.X),
list('tTfF')
)
try:
import msgpack
except ImportError:
print("Message pack python library not detected. Must use YAML backend instead.")
###################
# Writing functions
###################
# YAML keywords that need quoting when used as string values
_YAML_BOOL_KEYWORDS = frozenset({
'true', 'false', 'yes', 'no', 'on', 'off',
'True', 'False', 'Yes', 'No', 'On', 'Off',
'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF',
})
_YAML_NULL_KEYWORDS = frozenset({'null', 'Null', 'NULL', '~'})
_YAML_SPECIAL_STARTS = frozenset('-?:,[]{}#&*!|>\'"%%@`')
def _fast_yaml_scalar(v):
"""Format a Python value as an inline YAML scalar."""
if v is None:
return 'null'
if isinstance(v, bool):
return 'true' if v else 'false'
if isinstance(v, int):
return str(v)
if isinstance(v, float):
return repr(v)
if isinstance(v, str):
return _fast_yaml_str(v)
if isinstance(v, list):
return _fast_yaml_flow_list(v)
return repr(v)
def _fast_yaml_str(s):
"""Format a Python string as a YAML scalar, quoting only when necessary."""
if not s or s in _YAML_BOOL_KEYWORDS or s in _YAML_NULL_KEYWORDS:
return f"'{s}'"
escaped = s.replace("'", "''")
if s[0] in _YAML_SPECIAL_STARTS or s[0] == ' ' or s[-1] == ' ':
return f"'{escaped}'"
if ': ' in s or ' #' in s or s.endswith(':') or '\n' in s:
return f"'{escaped}'"
# Check if it looks like a number
c = s[0]
if c.isdigit() or (c in '+-.' and len(s) > 1):
try:
float(s)
return f"'{s}'"
except ValueError:
pass
return s
def _fast_yaml_flow_list(lst):
"""Format a Python list as a YAML flow sequence: [a, b, c]."""
if not lst:
return '[]'
return f"[{', '.join(_fast_yaml_scalar(item) for item in lst)}]"
def fast_yaml_dump(solutionStates, f):
"""
Write a list of solution dicts as YAML, optimized for speed.
Produces output compatible with yaml.load(f, CSafeLoader).
Only handles the plain Python types found in solution state dicts:
int, bool, str, float, None, list, and dict (one level of nesting).
Limitations versus CSafeDumper:
Structural:
- Only one level of dict nesting. Sub-dicts whose values are themselves
dicts fall through to repr() via _yamlScalar.
- Lists of dicts are not supported. _yamlFlowList calls _yamlScalar on
each item, which has no dict case and falls through to repr().
- No block style for complex structures; everything is written
inline/flow.
- No YAML anchors/aliases. Shared references (e.g. the same ProblemType
dict in multiple solutions) are duplicated in the output.
Type handling:
- Float special values (inf, -inf, nan) are written via repr(), which
produces Python syntax rather than the YAML-spec forms (.inf, -.inf,
.nan).
- Tuples, sets, bytes, and datetime objects all fall through to repr(),
producing Python-syntax output that is not valid YAML. CSafeDumper
converts tuples to sequences, datetimes to timestamps, etc.
String quoting:
- Dict keys are never quoted. Keys that are YAML keywords (true, yes,
null, etc.) or contain special characters are written bare.
- No detection of YAML timestamp-like strings. Values such as
"2024-01-01" or "12:30:00" are written unquoted and may be parsed
back as dates or times by the loader.
- Octal/hex-like strings (e.g. "0x1F", "0o17") may pass through
unquoted and be misinterpreted as numbers by some YAML loaders.
- Multiline strings (containing literal newlines) are single-quoted
with the newline embedded directly, which can produce broken output.
CSafeDumper uses block scalars (|) or double-quoted strings with
escaped newlines.
These limitations are acceptable because this writer targets the specific
shape of solution state dicts (flat dicts with occasional one-level-deep
sub-dicts of simple scalars). The validation in writeSolutions() catches
any mismatch against yaml.dump output at runtime.
"""
for sol in solutionStates:
first = True
for k in sorted(sol.keys()):
v = sol[k]
if first:
prefix = '- '
first = False
else:
prefix = ' '
if isinstance(v, dict):
f.write(f'{prefix}{k}:\n')
for k2 in sorted(v.keys()):
f.write(f' {k2}: {_fast_yaml_scalar(v[k2])}\n')
else:
f.write(f'{prefix}{k}: {_fast_yaml_scalar(v)}\n')
def write(filename_noExt, data, format="yaml"):
"""Writes data to file with specified format; extension is appended based on format."""
if format == "yaml":
writeYAML(filename_noExt + ".yaml", data)
elif format == "json":
writeJson(filename_noExt + ".json", data)
elif format == "msgpack":
writeMsgPack(filename_noExt + ".dat", data)
else:
printExit("Unrecognized write format {}".format(format))
def writeYAML(filename, data, **kwargs):
"""Writes data to file in YAML format."""
# set default kwags for yaml dump
if "explicit_start" not in kwargs:
kwargs["explicit_start"] = True
if "explicit_end" not in kwargs:
kwargs["explicit_end"] = True
if "default_flow_style" not in kwargs:
kwargs["default_flow_style"] = None
with open(filename, "w") as f:
yaml.dump(data, f, Dumper=yamlDumper, **kwargs)
def writeJson(filename, data):
"""Writes data to file in json format."""
with open(filename, "w") as f:
json_object = json.dumps(data, option=json.OPT_INDENT_2).decode("utf-8") if 'orjson' in sys.modules else json.dumps(data, indent=2)
f.write(json_object)
def writeMsgPack(filename, data):
"""Writes data to file in compressed Message Pack format (.dat.zlib)."""
raw = msgpack.packb(data)
compressed = zlib.compress(raw, 9)
with open(filename + ".zlib", "wb") as f:
f.write(compressed)
try:
os.unlink(filename)
except FileNotFoundError:
pass
def _writeSolutionsHeader(f: IO[str], problemSizes: Optional[ProblemSizes], biasTypeArgs: Optional[BiasTypeArgs], activationArgs: Optional[ActivationArgs], gateTypeArgs: Optional[GateTypeArgs] = None) -> None:
"""Write the YAML header (version, problem sizes, bias/activation/gate args)."""
f.write("- MinimumRequiredVersion: {}\n".format(__version__))
f.write("- ProblemSizes:\n")
if problemSizes:
for sizeRange in problemSizes.ranges:
f.write(" - Range: {}\n".format(sizeRange))
for problemExact in problemSizes.exacts:
#FIXME-problem, this ignores strides:
f.write(" - Exact: {}\n".format(problemExact))
if biasTypeArgs:
f.write("- BiasTypeArgs: [{}]\n".format([btype.value for btype in biasTypeArgs.biasTypes]))
if activationArgs:
f.write("- ActivationArgs:\n")
for setting in activationArgs.settingList:
f.write(" - [Enum: %s]\n"%(setting.activationEnum))
if gateTypeArgs:
f.write("- GateTypeArgs: [{}]\n".format([gtype.value for gtype in gateTypeArgs.gateTypes]))
def _findBodyOffset(filename: str, headerKeys: set[str]) -> int:
"""Find the character offset where solution entries begin, skipping the header."""
with open(filename, "r") as f:
while True:
pos = f.tell()
line = f.readline()
if not line:
return pos
if line.startswith("- "):
key = line[2:].split(":")[0].strip()
if key not in headerKeys:
return pos
def writeSolutions(filename: str, problemSizes: Optional[ProblemSizes], biasTypeArgs: Optional[BiasTypeArgs], activationArgs: Optional[ActivationArgs], solutions: list, gateTypeArgs: Optional[GateTypeArgs] = None, cache: bool = False) -> None:
"""Writes solution YAML file."""
if cache:
# Solutions unchanged; rewrite only the header in place
with timing_context("python_wsol_prepare"):
with timing_context("python_wsol_prepare_cache"):
newHeader = io.StringIO()
_writeSolutionsHeader(newHeader, problemSizes, biasTypeArgs, activationArgs, gateTypeArgs)
newHeader = newHeader.getvalue()
headerKeys = {line[2:].split(":")[0].strip()
for line in newHeader.splitlines() if line.startswith("- ")}
oldBodyOffset = _findBodyOffset(filename, headerKeys)
with timing_context("python_wsol_header"):
if len(newHeader) == oldBodyOffset:
# Same size header; overwrite in place, body untouched
with open(filename, "r+") as f:
f.write(newHeader)
else:
# Header size changed; must shift the body
with open(filename, "r+") as f:
f.seek(oldBodyOffset)
solutionsBody = f.read()
f.seek(0)
f.write(newHeader)
f.write(solutionsBody)
f.truncate()
return
with timing_context("python_wsol_prepare"):
with timing_context("python_wsol_prepare_nocache"):
solutionStates: list[dict] = []
for solution in solutions:
solutionState = solution.getAttributes()
solutionState["ProblemType"] = solutionState["ProblemType"].state
problemTypeToEnum(solutionState["ProblemType"])
isa = solutionState["ISA"]
solutionState["ISA"] = [isa[0], isa[1], isa[2]]
solutionStates.append(solutionState)
with open(filename, "w") as f:
with timing_context("python_wsol_header"):
_writeSolutionsHeader(f, problemSizes, biasTypeArgs, activationArgs, gateTypeArgs)
with timing_context("python_wsol_dump"):
fast_yaml_dump(solutionStates, f)
###############################
# Reading and parsing functions
###############################
def read(filename, customizedLoader=False):
name, extension = os.path.splitext(filename)
if extension == ".yaml":
return load_yaml_stream(filename, StrictTypeLoader) if customizedLoader else readYAML(filename)
if extension == ".json":
return readJson(filename)
else:
printExit("Unrecognized read format {}".format(extension))
def readYAML(filename):
"""Reads and returns YAML data from file."""
with open(filename, "r") as f:
data = yaml.load(f, StrictTypeLoader)
return data
def readJson(filename):
"""Reads and returns JSON data from file."""
with open(filename, "r") as f:
data = json.loads(f.read())
return data
def parseSolutionsFile(
filename,
assembler,
splitGSU: bool,
printSolutionRejectionReason: bool,
printIndexAssignmentInfo: bool,
isaInfoMap
):
"""Wrapper function to read and parse a solutions file."""
return parseSolutionsData(
read(filename),
filename,
assembler,
splitGSU,
printSolutionRejectionReason,
printIndexAssignmentInfo,
isaInfoMap
)
def parseSolutionsData(
data,
srcFile,
assembler,
splitGSU: bool,
printSolutionRejectionReason: bool,
printIndexAssignmentInfo: bool,
isaInfoMap
):
"""Parses problem sizes and solutions from the data of a solutions file."""
if len(data) < 3:
printExit("Solution file {} is missing required fields (len = {} < 3" \
.format(srcFile, len(data)))
versionString = data[0]["MinimumRequiredVersion"]
if not versionIsCompatible(versionString):
printWarning("Version = {} in solution file {} does not match Tensile version = {}" \
.format(srcFile, versionString, __version__) )
if "ProblemSizes" not in data[1]:
printExit("Solution file {} doesn't begin with ProblemSizes".format(srcFile))
problemSizesConfig = data[1]["ProblemSizes"]
solutionStartIdxInData = 2
if (len(data) > solutionStartIdxInData) and "BiasTypeArgs" in data[solutionStartIdxInData]:
solutionStartIdxInData += 1
if (len(data) > solutionStartIdxInData) and "ActivationArgs" in data[solutionStartIdxInData]:
solutionStartIdxInData += 1
if (len(data) > solutionStartIdxInData) and "GateTypeArgs" in data[solutionStartIdxInData]:
solutionStartIdxInData += 1
solutions = []
for i in range(solutionStartIdxInData, len(data)):
solutionState = data[i]
# force redo the deriving of parameters, make sure old version logic yamls can be validated
solutionState["AssignedProblemIndependentDerivedParameters"] = False
solutionState["AssignedDerivedParameters"] = False
solutionObject = Solution(
solutionState,
splitGSU,
printSolutionRejectionReason,
printIndexAssignmentInfo,
assembler,
isaInfoMap,
srcFile,
raiseProblemTypeOnTypeMismatch=False,
)
solutions.append(solutionObject)
problemType = solutions[0]["ProblemType"]
problemSizes = ProblemSizes(problemType, problemSizesConfig)
return (problemSizes, solutions)
def getRealDataTypeA(dataType):
if dataType == DataTypeEnum.Float8BFloat8.value:
return DataTypeEnum.Float8.value
elif dataType == DataTypeEnum.BFloat8Float8.value:
return DataTypeEnum.BFloat8.value
elif dataType == DataTypeEnum.Float8BFloat8_fnuz.value:
return DataTypeEnum.Float8_fnuz.value
elif dataType == DataTypeEnum.BFloat8Float8_fnuz.value:
return DataTypeEnum.BFloat8_fnuz.value
else:
return dataType
def getRealDataTypeB(dataType):
if dataType == DataTypeEnum.Float8BFloat8.value:
return DataTypeEnum.BFloat8.value
elif dataType == DataTypeEnum.BFloat8Float8.value:
return DataTypeEnum.Float8.value
elif dataType == DataTypeEnum.Float8BFloat8_fnuz.value:
return DataTypeEnum.BFloat8_fnuz.value
elif dataType == DataTypeEnum.BFloat8Float8_fnuz.value:
return DataTypeEnum.Float8_fnuz.value
else:
return dataType
class LibraryLogic(NamedTuple):
"""Return tuple for parseLibraryLogicData()"""
schedule: str
architecture: str
problemType: ProblemType
solutions: list
exactLogic: list
library: SolutionLibrary.MasterSolutionLibrary
typeMismatches: dict = {}
def parseLibraryLogicFile(
filename,
assembler,
splitGSU: bool,
printSolutionRejectionReason: bool,
printIndexAssignmentInfo: bool,
isaInfoMap: Dict[str, IsaInfo],
lazyLibraryLoading: bool
):
"""Wrapper function to read and parse a library logic file."""
return parseLibraryLogicData(
read(filename, True),
filename,
assembler,
splitGSU,
printSolutionRejectionReason,
printIndexAssignmentInfo,
isaInfoMap,
lazyLibraryLoading
)
def parseLibraryLogicData(
data,
srcFile,
assembler,
splitGSU: bool,
printSolutionRejectionReason: bool,
printIndexAssignmentInfo: bool,
isaInfoMap: Dict[str, IsaInfo],
lazyLibraryLoading: bool
):
"""Parses the data of a library logic file."""
# Reset the type mismatch collector at the start to capture all type
# mismatches from both ProblemType and Solution constructors
resetTypeMismatchCollector()
if isinstance(data, List):
data = parseLibraryLogicList(data, srcFile)
if "CUCount" not in data:
data["CUCount"] = None
if 'MacDataTypeA' not in data["ProblemType"]: #it will either be set as d['MacDataType'] or a specified input
data["ProblemType"]['MacDataTypeA'] = getRealDataTypeA(data["ProblemType"]['DataType'])
if 'MacDataTypeB' not in data["ProblemType"]:
data["ProblemType"]['MacDataTypeB'] = getRealDataTypeB(data["ProblemType"]['DataType'])
if 'DataTypeA' not in data["ProblemType"]:
data["ProblemType"]['DataTypeA'] = data["ProblemType"]['MacDataTypeA']
else:
data["ProblemType"]['DataTypeA'] = getRealDataTypeA(data["ProblemType"]['DataTypeA'])
if 'DataTypeB' not in data["ProblemType"]:
data["ProblemType"]['DataTypeB'] = data["ProblemType"]['MacDataTypeB']
else:
data["ProblemType"]['DataTypeB'] = getRealDataTypeB(data["ProblemType"]['DataTypeB'])
if not versionIsCompatible(data["MinimumRequiredVersion"]):
printWarning("Version = {} in library logic file {} does not match Tensile version = {}" \
.format(srcFile, data["MinimumRequiredVersion"], __version__) )
# unpack problemType
problemType = ProblemType(
data["ProblemType"],
printIndexAssignmentInfo,
srcFile=srcFile,
raiseOnTypeMismatch=False,
)
# unpack solution
def solutionStateToSolution(solutionState, assembler, isaInfoMap) -> Solution:
if solutionState["KernelLanguage"] == "Assembly":
solutionState["ISA"] = gfxToIsa(data["ArchitectureName"])
solutionState["CUCount"] = data["CUCount"]
solutionState["DeviceNames"] = data.get("DeviceNames", None)
# force redo the deriving of parameters, make sure old version logic yamls can be validated
solutionState["AssignedProblemIndependentDerivedParameters"] = False
solutionState["AssignedDerivedParameters"] = False
if solutionState["CustomKernelName"]:
isp = {}
if "InternalSupportParams" in solutionState:
isp = solutionState["InternalSupportParams"]
customConfig = getCustomKernelConfig(solutionState["CustomKernelName"], isp)
for key, value in customConfig.items():
solutionState[key] = value
if "MatrixInstruction" in customConfig and len(customConfig["MatrixInstruction"]) != 4:
raise ValueError(f"Custom kernel MatrixInstruction can only be of length 4, found {customConfig['MatrixInstruction']}")
# overwrite problemType if any
solutionState["ProblemType"] = problemType
if 'MacDataTypeA' not in solutionState["ProblemType"]: #it will either be set as d['MacDataType'] or a specified input
solutionState["ProblemType"]['MacDataTypeA'] = getRealDataTypeA(solutionState["ProblemType"]['DataType'])
if 'MacDataTypeB' not in solutionState["ProblemType"]:
solutionState["ProblemType"]['MacDataTypeB'] = getRealDataTypeB(solutionState["ProblemType"]['DataType'])
if 'DataTypeA' not in solutionState["ProblemType"]:
solutionState["ProblemType"]['DataTypeA'] = solutionState["ProblemType"]['MacDataTypeA']
else:
solutionState["ProblemType"]['DataTypeA'] = getRealDataTypeA(solutionState["ProblemType"]['DataTypeA'])
if 'DataTypeB' not in solutionState["ProblemType"]:
solutionState["ProblemType"]['DataTypeB'] = solutionState["ProblemType"]['MacDataTypeB']
else:
solutionState["ProblemType"]['DataTypeB'] = getRealDataTypeB(solutionState["ProblemType"]['DataTypeB'])
solutionObject = Solution(
solutionState,
splitGSU,
printSolutionRejectionReason,
printIndexAssignmentInfo,
assembler,
isaInfoMap,
srcFile,
raiseProblemTypeOnTypeMismatch=False,
)
return solutionObject
solutions = [solutionStateToSolution(solutionState, assembler, isaInfoMap) for solutionState in data["Solutions"]]
typeMismatches = getTypeMismatchCollector()
newLibrary, _ = SolutionLibrary.MasterSolutionLibrary.FromOriginalState(
data,
solutions,
splitGSU,
printSolutionRejectionReason,
printIndexAssignmentInfo,
assembler,
isaInfoMap,
lazyLibraryLoading,
logicFile=srcFile
)
return LibraryLogic(data["ScheduleName"], data["ArchitectureName"], problemType, solutions, \
data.get("ExactLogic"), newLibrary, typeMismatches)
def parseLibraryLogicList(data, srcFile="?"):
"""Parses the data of a matching table style library logic file."""
if len(data) < 9:
printExit("Library logic file {} is missing required fields (len = {} < 9)" \
.format(srcFile, len(data)))
rv = {}
rv["MinimumRequiredVersion"] = data[0]["MinimumRequiredVersion"]
rv["ScheduleName"] = data[1]
rv["DeviceNames"] = data[3]
rv["ProblemType"] = data[4]
rv["Solutions"] = data[5]
if type(data[2]) is dict:
rv["ArchitectureName"] = data[2]["Architecture"]
rv["CUCount"] = data[2]["CUCount"]
else:
rv["ArchitectureName"] = data[2]
rv["CUCount"] = None
# TODOBEN: figure out what to do with these...
rv["ExactLogic"] = data[7]
rv["RangeLogic"] = data[8]
# optional fields
if len(data) > 10 and data[10]:
rv["PerfMetric"] = data[10]
# library logic fields
libraryType = None
if len(data) > 11 and data[11]:
libraryType = data[11]
else:
printExit("Library logic file {} is missing required field matching property." \
.format(srcFile))
if libraryType == "FreeSize":
rv["LibraryType"] = "FreeSize"
rv["Library"] = {}
rv["Library"]["indexOrder"] = None
rv["Library"]["table"] = [0, len(data[5])]
rv["Library"]["distance"] = None
elif libraryType == "Prediction":
rv["LibraryType"] = "Prediction"
rv["Library"] = {}
rv["Library"]["indexOrder"] = None
rv["Library"]["table"] = [0, len(data[5])]
rv["Library"]["distance"] = None
else:
rv["LibraryType"] = "Matching"
rv["Library"] = {}
rv["Library"]["indexOrder"] = data[6]
rv["Library"]["table"] = data[7]
rv["Library"]["distance"] = libraryType
return rv
def rawLibraryLogic(data):
"""Returns a tuple of the data in a library logic file."""
versionString = data[0]
scheduleName = data[1]
architectureName = data[2]
deviceNames = data[3]
problemTypeState = data[4]
solutionStates = data[5]
indexOrder = data[6]
exactLogic = data[7]
rangeLogic = data[8]
otherFields = []
dataLength = len(data)
if dataLength > 9:
for idx in range(9, dataLength):
otherFields.append(data[idx])
return (versionString, scheduleName, architectureName, deviceNames,\
problemTypeState, solutionStates, indexOrder, exactLogic, rangeLogic, otherFields)
#################
# Other functions
#################
def getCUCount() -> int:
"""Return the number of CU Count in current Hardware."""
CU = os.environ.get("CU", None)
if CU is None:
try:
res = subprocess.run("rocminfo | grep Compute", stdout=subprocess.PIPE, shell=True, env={**os.environ, "ROCR_VISIBLE_DEVICES": "0"})
CU_RE = r"Compute Unit:(?P<COMPUTE_UNIT>[\w ]+)"
lines = res.stdout.decode("utf-8").strip().split('\n')
if lines:
match = re.search(CU_RE, lines[-1])
if match:
CU = int(match.group('COMPUTE_UNIT').strip())
except Exception:
pass
if CU is None:
printExit("Failed to get Compute Unit count from rocminfo or env variable 'CU'")
return int(CU)
def createLibraryLogic(schedulePrefix, architectureName, deviceNames, libraryType, logicTuple):
"""Creates the data for a library logic file suitable for writing to YAML."""
problemType = logicTuple[0]
solutions = logicTuple[1]
indexOrder = logicTuple[2]
exactLogic = logicTuple[3]
rangeLogic = logicTuple[4]
tileSelection = False
if len(logicTuple) > 5 and logicTuple[5]:
tileSelection = True
data = []
# Tensile version
data.append({"MinimumRequiredVersion": __version__})
# schedule name
data.append(schedulePrefix) # change from Tensile to vega10
# schedule architecture name and get CU count
CUCount=getCUCount()
data.append({"Architecture": architectureName, "CUCount": CUCount} if architectureName=="gfx942" and CUCount and CUCount!=304 else architectureName)
# schedule device names
data.append(deviceNames)
# problem type
problemTypeState = problemType.state
problemTypeState["DataType"] = \
problemTypeState["DataType"].value
problemTypeState["MacDataTypeA"] = \
problemTypeState["MacDataTypeA"].value
problemTypeState["MacDataTypeB"] = \
problemTypeState["MacDataTypeB"].value
problemTypeState["DataTypeA"] = \
problemTypeState["DataTypeA"].value
problemTypeState["DataTypeB"] = \
problemTypeState["DataTypeB"].value
problemTypeState["DataTypeE"] = \
problemTypeState["DataTypeE"].value
problemTypeState["DataTypeAmaxD"] = \
problemTypeState["DataTypeAmaxD"].value
problemTypeState["DestDataType"] = \
problemTypeState["DestDataType"].value
problemTypeState["ComputeDataType"] = \
problemTypeState["ComputeDataType"].value
problemTypeState["BiasDataTypeList"] = \
[btype.value for btype in problemTypeState["BiasDataTypeList"]]
if "GateResidualDataTypeList" in problemTypeState:
problemTypeState["GateResidualDataTypeList"] = \
[btype.value for btype in problemTypeState["GateResidualDataTypeList"]]
problemTypeState["ActivationComputeDataType"] = \
problemTypeState["ActivationComputeDataType"].value
problemTypeState["ActivationType"] = \
problemTypeState["ActivationType"].value
problemTypeState["F32XdlMathOp"] = \
problemTypeState["F32XdlMathOp"].value
if "DataTypeMetadata" in problemTypeState:
problemTypeState["DataTypeMetadata"] = \
problemTypeState["DataTypeMetadata"].value
if "DataTypeMXSA" in problemTypeState:
problemTypeState["DataTypeMXSA"] = \
problemTypeState["DataTypeMXSA"].value
if "DataTypeMXSB" in problemTypeState:
problemTypeState["DataTypeMXSB"] = \
problemTypeState["DataTypeMXSB"].value
data.append(problemTypeState)
# solutions
solutionList = []
for solution in solutions:
solutionState = solution.getAttributes()
del solutionState["ProblemType"]
isa = solutionState["ISA"]
solutionState["ISA"] = [isa[0], isa[1], isa[2]]
solutionList.append(solutionState)
if tileSelection:
tileSolutions = logicTuple[5]
for solution in tileSolutions:
solutionState = solution.getAttributes()
del solutionState["ProblemType"]
solutionList.append(solutionState)
data.append(solutionList)
# index order
data.append(indexOrder)
# exactLogic
exactLogicList = []
if exactLogic:
for key in exactLogic:
exactLogicList.append([list(key), exactLogic[key]])
data.append(exactLogicList)
else:
data.append(None)
# rangeLogic
data.append(rangeLogic)
if tileSelection:
tileSelectionLogic = {}
tileSelectionIndices = logicTuple[6]
tileSelectionLogic["TileSelectionIndices"] = tileSelectionIndices
data.append(tileSelectionLogic)
else:
data.append(None)
data.append(logicTuple[7]) # PerfMetric
data.append(libraryType) # LibraryType
return data