From 5ef62bd5f5d4b84af8303f1db39dcfe762c2d4dd Mon Sep 17 00:00:00 2001 From: David Dixon Date: Thu, 2 Jan 2025 18:55:56 -0600 Subject: [PATCH 01/10] add toolchain files --- tensilelite/Tensile/Toolchain/Assembly.py | 210 ++++++++++++++++++ tensilelite/Tensile/Toolchain/Source.py | 225 ++++++++++++++++++++ tensilelite/Tensile/Toolchain/Validators.py | 193 +++++++++++++++++ tensilelite/Tensile/Toolchain/__init__.py | 0 4 files changed, 628 insertions(+) create mode 100644 tensilelite/Tensile/Toolchain/Assembly.py create mode 100644 tensilelite/Tensile/Toolchain/Source.py create mode 100644 tensilelite/Tensile/Toolchain/Validators.py create mode 100644 tensilelite/Tensile/Toolchain/__init__.py diff --git a/tensilelite/Tensile/Toolchain/Assembly.py b/tensilelite/Tensile/Toolchain/Assembly.py new file mode 100644 index 0000000000..d21db27ce4 --- /dev/null +++ b/tensilelite/Tensile/Toolchain/Assembly.py @@ -0,0 +1,210 @@ +################################################################################ +# +# Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +################################################################################ + +import collections +import math +import os +import shlex +import shutil +import subprocess +import warnings + +from pathlib import Path +from typing import List, Literal, Union, Tuple + +from .. import Utils +from ..TensileInstructions import getGfxName +from ..Common import globalParameters, print2, ensurePath +class AssemblyToolchain: + def __init__(self, assembler: str, bundler: str, buildIdKind: str, coVersion: Literal[4, 5]): + self.assembler = assembler + self.bundler = bundler + self.buildIdKind = buildIdKind + self.coVersion = coVersion + + def invoke(self, args: List[str], desc: str=""): + """Invokes a subprocess with the provided arguments. + + Args: + args: A list of arguments to pass to the subprocess. + desc: A description of the subprocess invocation. + + Raises: + RuntimeError: If the subprocess invocation fails. + """ + print2(f"{desc}: {' '.join(args)}") + try: + out = subprocess.check_output(args, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as err: + raise RuntimeError( + f"Error with {desc}: {err.output}\n" + f"Failed command: {' '.join(args)}" + ) + print2(f"Output: {out}") + return out + + def assemble(self, srcPath: str, destPath: str, gfx: str, wavefrontSize: int, debug: bool=False): + """Assemble an assembly source file into an object file. + + Args: + srcPath: The path to the assembly source file. + destPath: The destination path for the generated object file. + coVersion: The code object version to use. + isa: The target GPU architecture in ISA format. + wavefrontSize: The wavefront size to use. + """ + launcher = shlex.split(os.environ.get('Tensile_ASM_COMPILER_LAUNCHER', '')) + args = [ + *launcher, + self.assembler, + "-x", "assembler", + "--target=amdgcn-amd-amdhsa", + f"-mcode-object-version={self.coVersion}", + f"-mcpu={gfx}", + "-mwavefrontsize64" if wavefrontSize == 64 else "-mno-wavefrontsize64" + "-g" if debug else "", + "-c", + "-o", destPath, srcPath + ] + + return self.invoke(args, "Assembling assembly source code into object file (.s -> .o)") + + def link(self, srcPaths: List[str], destPath: str): + """Links object files into a code object file. + + Args: + srcPaths: A list of paths to object files. + destPath: A destination path for the generated code object file. + + Raises: + RuntimeError: If linker invocation fails. + """ + if os.name == "nt": + # Use args file on Windows b/c the command may exceed the limit of 8191 characters + with open(Path.cwd() / "clang_args.txt", "wt") as file: + file.write(" ".join(objFiles)) + file.flush() + args = [ + self.assembler, + "--target=amdgcn-amd-amdhsa", + "-o", destPath, "@clang_args.txt"] + else: + args = [ + self.assembler, + "--target=amdgcn-amd-amdhsa", + "-Xlinker", f"--build-id={self.buildIdKind}", + "-o", destPath, *srcPaths + ] + + return self.invoke(args, "Linking assembly object files into code object (*.o -> .co)") + + def compress(self, srcPath: str, destPath: str, gfx: str): + """Compresses a code object file using the provided bundler. + + Args: + srcPath: The source path of the code object file to be compressed. + destPath: The destination path for the compressed code object file. + gfx: The target GPU architecture. + + Raises: + RuntimeError: If compressing the code object file fails. + """ + args = [ + self.bundler, + "--compress", + "--type=o", + "--bundle-align=4096", + f"--targets=host-x86_64-unknown-linux,hipv4-amdgcn-amd-amdhsa--{gfx}", + "--input=/dev/null", + f"--input={srcPath}", + f"--output={destPath}", + ] + + return self.invoke(args, "Bundling/compressing code object file (.co -> .co)") + + +def _batchObjectFiles(objFiles: List[str], coPathDest: Union[Path, str], maxObjFiles: int=10000) -> List[str]: + numObjFiles = len(objFiles) + + if numObjFiles <= maxObjFiles: + return objFiles + + batchedObjFiles = [objFiles[i:i+maxObjFiles] for i in range(0, numObjFiles, maxObjFiles)] + numBatches = int(math.ceil(numObjFiles / maxObjFiles)) + + newObjFiles = [str(coPathDest) + "." + str(i) for i in range(0, numBatches)] + newObjFilesOutput = [] + + for batch, filename in zip(batchedObjFiles, newObjFiles): + if len(batch) > 1: + args = [globalParameters["ROCmLdPath"], "-r"] + batch + [ "-o", filename] + print2(f"Linking object files into fewer object files: {' '.join(args)}") + subprocess.check_call(args) + newObjFilesOutput.append(filename) + else: + newObjFilesOutput.append(batchedObjFiles[0]) + + return newObjFilesOutput + +def buildAssemblyCodeObjectFiles(toolchain: AssemblyToolchain, kernels, writerAsm, outputPath, compress: bool=True): + + isAsm = lambda k: k["KernelLanguage"] == "Assembly" + + extObj = ".o" + extCo = ".co" + extCoRaw = ".co.raw" + + destDir = Path(ensurePath(os.path.join(outputPath, 'library'))) + asmDir = Path(ensurePath(os.path.join(globalParameters["WorkingPath"], "assembly"))) + + archKernelMap = collections.defaultdict(list) + for k in filter(isAsm, kernels): + archKernelMap[tuple(k['ISA'])].append(k) + + coFiles = [] + for arch, archKernels in archKernelMap.items(): + if len(archKernels) == 0: + continue + + gfx = getGfxName(arch) + + objectFiles = [str(asmDir / (writerAsm.getKernelFileBase(k) + extObj)) for k in archKernels if 'codeObjectFile' not in k] + coFileMap = collections.defaultdict(list) + if len(objectFiles): + coFileMap[asmDir / ("TensileLibrary_"+ gfx + extCoRaw)] = objectFiles + for kernel in archKernels: + coName = kernel.get("codeObjectFile", None) + if coName: + coFileMap[asmDir / (coName + extCoRaw)].append(str(asmDir / (writerAsm.getKernelFileBase(kernel) + extObj))) + for coFileRaw, objFiles in coFileMap.items(): + objFiles = _batchObjectFiles(objFiles, coFileRaw) + toolchain.link(objFiles, str(coFileRaw)) + coFile = destDir / coFileRaw.name.replace(extCoRaw, extCo) + if compress: + toolchain.compress(str(coFileRaw), str(coFile), gfx) + else: + shutil.move(coFileRaw, coFile) + coFiles.append(coFile) + + return coFiles diff --git a/tensilelite/Tensile/Toolchain/Source.py b/tensilelite/Tensile/Toolchain/Source.py new file mode 100644 index 0000000000..7d3e32ae63 --- /dev/null +++ b/tensilelite/Tensile/Toolchain/Source.py @@ -0,0 +1,225 @@ +################################################################################ +# +# Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +################################################################################ + +import itertools +import os +import re +import shlex +import shutil +import subprocess +from pathlib import Path +from typing import Iterable, List, Union + +from ..Common import globalParameters, print2, ensurePath, ParallelMap2, splitArchs + +class SourceToolchain: + def __init__(self, compiler: str, bundler: str, buildIdKind: str, asanBuild: bool=False, saveTemps: bool=False): + self.compiler = compiler + self.bundler = bundler + self.buildIdKind = buildIdKind + self.asanBuild = asanBuild + self.saveTemps = saveTemps + + def invoke(self, args: List[str], desc: str=""): + """Invokes a subprocess with the provided arguments. + + Args: + args: A list of arguments to pass to the subprocess. + desc: A description of the subprocess invocation. + + Raises: + RuntimeError: If the subprocess invocation fails. + """ + print2(f"{desc}: {' '.join(args)}") + try: + out = subprocess.check_output(args, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as err: + raise RuntimeError( + f"Error with {desc}: {err.output}\n" + f"Failed command: {' '.join(args)}" + ) + print2(f"Output: {out}") + return out + + def compile(self, srcPath: str, destPath: str, includePath: str, gfxs: List[str]): + """Compiles a source file into an object file. + + Args: + cmdlineArchs: List of architectures for offloading. + kernelFile: The path to the kernel source file. + buildPath: The build directory path. + objectFilename: The name of the output object file. + outputPath: The output directory path. + globalParameters: A dictionary of global parameters. + + Raises: + RuntimeError: If the compilation command fails. + """ + launcher = shlex.split(os.environ.get("Tensile_CXX_COMPILER_LAUNCHER", "")) + + hipFlags = [ + "-D__HIP_HCC_COMPAT_MODE__=1", + "--offload-device-only", + "-x", "hip", "-O3", + "-I", includePath, + "-Xoffload-linker", f"--build-id={self.buildIdKind}", + "-std=c++17", + ] + if self.asanBuild: + hipFlags.extend(["-fsanitize=address", "-shared-libasan", "-fuse-ld=lld"]) + if self.saveTemps: + hipFlags.append("--save-temps") + if os.name == "nt": + hipFlags.extend(["-fms-extensions", "-fms-compatibility", "-fPIC", "-Wno-deprecated-declarations"]) + + archFlags = [f"--offload-arch={gfx}" for gfx in gfxs] + + args = [ + *launcher, self.compiler, *hipFlags, *archFlags, srcPath, "-c", "-o", destPath + ] + + return self.invoke(args, f"Compiling HIP source kernels into objects (.cpp -> .o)") + + + def targets(self, objFile: str): + """Lists the target triples in an object file. + + Args: + objFile: The object file path. + + Returns: + List of target triples in the object file. + """ + args = [self.bundler, "--type=o", f"--input={objFile}", "-list"] + return self.invoke(args, f"Listing target triples in object file").decode().split("\n") + + def unbundle(self, target: str, srcPath: str, destPath: str): + """Unbundles source code object files using the Clang Offload Bundler. + + Args: + target: The target triple, see https://llvm.org/docs/AMDGPUUsage.html#target-triples. + infile: The path to the input object file. + outfileRaw: The path to the unbundled code object. + + Raises: + RuntimeError: If unbundling the source code object file fails. + """ + args = [ + self.bundler, + "--type=o", + f"--targets={target}", + f"--input={srcPath}", + f"--output={destPath}", + "--unbundle", + ] + + return self.invoke(args, f"Unbundling source code object file") + + +def _computeSourceCodeObjectFilename(target: str, base: str, buildPath: Union[Path, str], arch: str) -> Union[Path, None]: + """Generates a code object file path using the target, base, and build path. + + Args: + target: The target triple. + base: The base name for the output file (name without extension). + buildPath: The build directory path. + + Returns: + Path to the code object file. + """ + coPath = None + buildPath = Path(buildPath) + if "TensileLibrary" in base and "fallback" in base: + coPath = buildPath / "{0}_{1}.hsaco.raw".format(base, arch) + elif "TensileLibrary" in base: + variant = [t for t in ["", "xnack-", "xnack+"] if t in target][-1] + baseVariant = base + "-" + variant if variant else base + if arch in baseVariant: + coPath = buildPath / (baseVariant + ".hsaco.raw") + else: + coPath= buildPath / "{0}.so-000-{1}.hsaco.raw".format(base, arch) + + return coPath + + +def _buildSourceCodeObjectFile(toolchain: SourceToolchain, outputPath: Union[Path, str], kernelPath: Union[Path, str]) -> List[str]: + """Compiles a HIP source code file into a code object file. + + Args: + cxxCompiler: The C++ compiler to use. + cxxCompiler: The offload bundler to use. + outputPath: The output directory path where code objects will be placed. + kernelPath: The path to the kernel source file. + + Returns: + List of paths to the created code objects. + """ + buildPath = Path(ensurePath(os.path.join(globalParameters['WorkingPath'], 'code_object_tmp'))) + destPath = Path(ensurePath(os.path.join(outputPath, 'library'))) + kernelPath = Path(kernelPath) + + if "CmakeCxxCompiler" in globalParameters and globalParameters["CmakeCxxCompiler"] is not None: + os.environ["CMAKE_CXX_COMPILER"] = globalParameters["CmakeCxxCompiler"] + + objFilename = kernelPath.stem + '.o' + coPathsRaw = [] + coPaths= [] + + _, cmdlineArchs = splitArchs() + + objPath = str(buildPath / objFilename) + toolchain.compile(str(kernelPath), objPath, str(outputPath), cmdlineArchs) + + for target in toolchain.targets(objPath): + match = re.search("gfx.*$", target) + if match: + arch = re.sub(":", "-", match.group()) + coPathRaw = _computeSourceCodeObjectFilename(target, kernelPath.stem, buildPath, arch) + if not coPathRaw: continue + toolchain.unbundle(target, objPath, str(coPathRaw)) + + coPath = str(destPath / coPathRaw.stem) + coPathsRaw.append(coPathRaw) + coPaths.append(coPath) + + for src, dst in zip(coPathsRaw, coPaths): + shutil.move(src, dst) + + return coPaths + +def buildSourceCodeObjectFiles(toolchain: SourceToolchain, kernelFiles: List[Path], outputPath: Path) -> Iterable[str]: + """Compiles HIP source code files into code object files. + + Args: + cxxCompiler: The C++ compiler to use. + kernelFiles: List of paths to the kernel source files. + outputPath: The output directory path where code objects will be placed. + removeTemporaries: Whether to clean up temporary files. + + Returns: + List of paths to the created code objects. + """ + args = zip(itertools.repeat(toolchain), itertools.repeat(outputPath), kernelFiles) + coFiles = ParallelMap2(_buildSourceCodeObjectFile, args, "Compiling source kernels") + return itertools.chain.from_iterable(coFiles) diff --git a/tensilelite/Tensile/Toolchain/Validators.py b/tensilelite/Tensile/Toolchain/Validators.py new file mode 100644 index 0000000000..a19440e054 --- /dev/null +++ b/tensilelite/Tensile/Toolchain/Validators.py @@ -0,0 +1,193 @@ +################################################################################ +# +# Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +################################################################################ + +import os +import re +from pathlib import Path +from typing import List, NamedTuple, Union +from subprocess import run, PIPE + +ROCM_BIN_PATH = Path("/opt/rocm/bin") +ROCM_LLVM_BIN_PATH = Path("/opt/rocm/lib/llvm/bin") + +if os.name == "nt": + def _windowsLatestRocmBin(path: Union[Path, str]) -> Path: + """Get the path to the latest ROCm bin directory, on Windows. + + This function assumes that ROCm versions are differentiated with the form ``X.Y``. + + Args: + path: The path to the ROCm root directory, typically ``C:/Program Files/AMD/ROCm``. + + Returns: + The path to the ROCm bin directory for the latest ROCm version. + Typically of the form ``C:/Program Files/AMD/ROCm/X.Y/bin``. + """ + path = Path(path) + pattern = re.compile(r"^\d+\.\d+$") + versions = filter(lambda d: d.is_dir() and pattern.match(d.name), path.iterdir()) + latest = max(versions, key=lambda d: tuple(map(int, d.name.split(".")))) + return latest / "bin" + # LLVM binaries are in the same directory as ROCm binaries on Windows + ROCM_BIN_PATH = _windowsLatestRocmBin("C:/Program Files/AMD/ROCm") + ROCM_LLVM_BIN_PATH = _windowsLatestRocmBin("C:/Program Files/AMD/ROCm") + + +osSelect = lambda linux, windows: linux if os.name != "nt" else windows + + +class ToolchainDefaults(NamedTuple): + CXX_COMPILER= osSelect(linux="amdclang++", windows="clang++.exe") + C_COMPILER= osSelect(linux="amdclang", windows="clang.exe") + OFFLOAD_BUNDLER= osSelect(linux="clang-offload-bundler", windows="clang-offload-bundler.exe") + ASSEMBLER = osSelect(linux="amdclang++", windows="clang++.exe") + HIP_CONFIG = osSelect(linux="hipconfig", windows="hipconfig") + + +def _supportedComponent(component: str, targets: List[str]) -> bool: + isSupported = any([component == t for t in targets]) or any([Path(component).name == t for t in targets]) + return isSupported + + +def supportedCCompiler(compiler: str) -> bool: + """Determine if a C compiler/assembler is supported by Tensile. + + Args: + compiler: The name of a compiler to test for support. + + Return: + If supported True; otherwise, False. + """ + return _supportedComponent(compiler, [ToolchainDefaults.C_COMPILER]) + + +def supportedCxxCompiler(compiler: str) -> bool: + """Determine if a C++/HIP compiler/assembler is supported by Tensile. + + Args: + compiler: The name of a compiler to test for support. + + Return: + If supported True; otherwise, False. + """ + return _supportedComponent(compiler, [ToolchainDefaults.CXX_COMPILER]) + + +def supportedOffloadBundler(bundler: str) -> bool: + """Determine if an offload bundler is supported by Tensile. + + Args: + bundler: The name of an offload bundler to test for support. + + Return: + If supported True; otherwise, False. + """ + return _supportedComponent(bundler, [ToolchainDefaults.OFFLOAD_BUNDLER]) + + +def supportedHip(smi: str) -> bool: + """Determine if an offload bundler is supported by Tensile. + + Args: + bundler: The name of an offload bundler to test for support. + + Return: + If supported True; otherwise, False. + """ + return _supportedComponent(smi, [ToolchainDefaults.HIP_CONFIG]) + + +def _exeExists(file: Path) -> bool: + """Check if a file exists and is executable. + + Args: + file: The file to check. + + Returns: + If the file exists and is executable, True; otherwise, False + """ + return True if os.access(file, os.X_OK) else False + + +def _validateExecutable(file: str, searchPaths: List[Path]) -> str: + """Validate that the given toolchain component is in the PATH and executable. + + Args: + file: The executable to validate. + searchPaths: List of directories to search for the executable. + + Returns: + The validated executable with an absolute path. + """ + if not any(( + supportedCxxCompiler(file), supportedCCompiler(file), supportedOffloadBundler(file), supportedHip(file) + )): + raise ValueError(f"{file} is not a supported toolchain component for OS: {os.name}") + + if _exeExists(Path(file)): return file + for path in searchPaths: + path /= file + if _exeExists(path): return str(path) + raise FileNotFoundError(f"`{file}` either not found or not executable in any search path: {':'.join(map(str, searchPaths))}") + + +def validateToolchain(*args: str): + """Validate that the given toolchain components are in the PATH and executable. + + Args: + args: List of executable toolchain components to validate. + + Returns: + List of validated executables with absolute paths. + + Raises: + ValueError: If no toolchain components are provided. + FileNotFoundError: If a toolchain component is not found in the PATH. + """ + if not args: + raise ValueError("No toolchain components to validate, at least one argument is required") + + searchPaths = [ + ROCM_BIN_PATH, + ROCM_LLVM_BIN_PATH, + ] + [Path(p) for p in os.environ["PATH"].split(os.pathsep)] + + out = (_validateExecutable(x, searchPaths) for x in args) + return next(out) if len(args) == 1 else tuple(out) + + +def getVersion(executable: str, versionFlag: str="--version", regex: str=r"version\s+([\d.]+)") -> str: + """Print the version of a toolchain component. + + Args: + executable: The toolchain component to check the version of. + versionFlag: The flag to pass to the executable to get the version. + """ + args = f'"{executable}" "{versionFlag}"' + try: + output = run(args, stdout=PIPE, shell=True).stdout.decode().strip() + match = re.search(regex, output, re.IGNORECASE) + return match.group(1) if match else "" + except Exception as e: + raise RuntimeError(f"Failed to get version when calling {args}: {e}") diff --git a/tensilelite/Tensile/Toolchain/__init__.py b/tensilelite/Tensile/Toolchain/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From 8fbb690b3b6b7731847c4933f24fe9e17fa88aed Mon Sep 17 00:00:00 2001 From: David Dixon Date: Thu, 2 Jan 2025 18:56:32 -0600 Subject: [PATCH 02/10] Remove build commands --- .../Tensile/BuildCommands/AssemblyCommands.py | 122 ----------- .../Tensile/BuildCommands/SharedCommands.py | 40 ---- .../Tensile/BuildCommands/SourceCommands.py | 194 ------------------ tensilelite/Tensile/BuildCommands/__init__.py | 0 4 files changed, 356 deletions(-) delete mode 100644 tensilelite/Tensile/BuildCommands/AssemblyCommands.py delete mode 100644 tensilelite/Tensile/BuildCommands/SharedCommands.py delete mode 100644 tensilelite/Tensile/BuildCommands/SourceCommands.py delete mode 100644 tensilelite/Tensile/BuildCommands/__init__.py diff --git a/tensilelite/Tensile/BuildCommands/AssemblyCommands.py b/tensilelite/Tensile/BuildCommands/AssemblyCommands.py deleted file mode 100644 index df6c7a6764..0000000000 --- a/tensilelite/Tensile/BuildCommands/AssemblyCommands.py +++ /dev/null @@ -1,122 +0,0 @@ -import collections -import math -import os -import shutil -import subprocess - -from pathlib import Path -from typing import List, Union - -from .. import Utils -from ..TensileInstructions import getGfxName -from ..Common import globalParameters, print2, ensurePath, printWarning -from ..KernelWriterAssembly import KernelWriterAssembly -from .SharedCommands import compressCodeObject - -def _linkIntoCodeObject( - objFiles: List[str], coPathDest: Union[Path, str], kernelWriterAssembly: KernelWriterAssembly, assembler: str -): - """Links object files into a code object file. - - Args: - objectFiles: A list of object files to be linked. - coPathDest: The destination path for the code object file. - kernelWriterAssembly: An instance of KernelWriterAssembly to get link arguments. - - Raises: - RuntimeError: If linker invocation fails. - """ - if os.name == "nt": - # Use args file on Windows b/c the command may exceed the limit of 8191 characters - with open(Path.cwd() / "clangArgs.txt", 'wt') as file: - file.write(" ".join(objFiles)) - file.flush() - args = [assembler, '-target', 'amdgcn-amd-amdhsa', '-o', coFileRaw, '@clangArgs.txt'] - subprocess.check_call(args, cwd=asmDir) - else: - numObjFiles = len(objFiles) - maxObjFiles = 10000 - - if numObjFiles > maxObjFiles: - batchedObjFiles = [objFiles[i:i+maxObjFiles] for i in range(0, numObjFiles, maxObjFiles)] - numBatches = int(math.ceil(numObjFiles / maxObjFiles)) - - newObjFiles = [str(coPathDest) + "." + str(i) for i in range(0, numBatches)] - newObjFilesOutput = [] - - for batch, filename in zip(batchedObjFiles, newObjFiles): - if len(batch) > 1: - args = [globalParameters["ROCmLdPath"], "-r"] + batch + [ "-o", filename] - print2(f"Linking object files into fewer object files: {' '.join(args)}") - subprocess.check_call(args) - newObjFilesOutput.append(filename) - else: - newObjFilesOutput.append(batchedObjFiles[0]) - - objFiles = newObjFilesOutput - - args = kernelWriterAssembly.getLinkCodeObjectArgs(objFiles, str(coPathDest)) - print2(f"Linking object files into code object: {' '.join(args)}") - subprocess.check_call(args) - - - -def buildAssemblyCodeObjectFiles(kernels, kernelWriterAssembly, outputPath, assembler: str, offloadBundler: str, compress: bool=True): - - isAsm = lambda k: k["KernelLanguage"] == "Assembly" - - extObj = ".o" - extCo = ".co" - extCoRaw = ".co.raw" - - destDir = Path(ensurePath(os.path.join(outputPath, 'library'))) - asmDir = Path(kernelWriterAssembly.getAssemblyDirectory()) - - archKernelMap = collections.defaultdict(list) - for k in filter(isAsm, kernels): - archKernelMap[tuple(k['ISA'])].append(k) - - coFiles = [] - for arch, archKernels in archKernelMap.items(): - if len(archKernels) == 0: - continue - - gfx = getGfxName(arch) - if globalParameters["LazyLibraryLoading"]: - objectFiles = [str(asmDir / (kernelWriterAssembly.getKernelFileBase(k) + extObj)) for k in archKernels if 'codeObjectFile' not in k] - - coFileMap = collections.defaultdict(list) - - if len(objectFiles): - coFileMap[asmDir / ("TensileLibrary_"+ gfx + extCoRaw)] = objectFiles - - for kernel in archKernels: - coName = kernel.get("codeObjectFile", None) - if coName: - coFileMap[asmDir / (coName + extCoRaw)].append(str(asmDir / (kernelWriterAssembly.getKernelFileBase(kernel) + extObj))) - - for coFileRaw, objFiles in coFileMap.items(): - - _linkIntoCodeObject(objFiles, coFileRaw, kernelWriterAssembly, assembler) - coFile = destDir / coFileRaw.name.replace(extCoRaw, extCo) - if compress: - compressCodeObject(coFileRaw, coFile, gfx, offloadBundler) - else: - shutil.move(coFileRaw, coFile) - - coFiles.append(coFile) - else: - # no mergefiles - def newCoFileName(kName): - return os.path.join(destDir, kName + '_' + gfx + '.co') - - def orgCoFileName(kName): - return os.path.join(asmDir, kName + '.co') - - for src, dst in Utils.tqdm(((orgCoFileName(kName), newCoFileName(kName)) for kName in \ - map(lambda k: kernelWriterAssembly.getKernelFileBase(k), archKernels)), "Copying code objects"): - shutil.copyfile(src, dst) - coFiles.append(dst) - printWarning("Code object files are not compressed in `--no-merge-files` build mode.") - - return coFiles diff --git a/tensilelite/Tensile/BuildCommands/SharedCommands.py b/tensilelite/Tensile/BuildCommands/SharedCommands.py deleted file mode 100644 index 5d77ae45a8..0000000000 --- a/tensilelite/Tensile/BuildCommands/SharedCommands.py +++ /dev/null @@ -1,40 +0,0 @@ -import subprocess - -from typing import Union -from pathlib import Path - -from ..Common import print2 - -def compressCodeObject( - coPathSrc: Union[Path, str], coPathDest: Union[Path, str], gfx: str, bundler: str -): - """Compresses a code object file using the provided bundler. - - Args: - coPathSrc: The source path of the code object file to be compressed. - coPathDest: The destination path for the compressed code object file. - gfx: The target GPU architecture. - bundler: The path to the Clang Offload Bundler executable. - - Raises: - RuntimeError: If compressing the code object file fails. - """ - args = [ - bundler, - "--compress", - "--type=o", - "--bundle-align=4096", - f"--targets=host-x86_64-unknown-linux,hipv4-amdgcn-amd-amdhsa--{gfx}", - "--input=/dev/null", - f"--input={str(coPathSrc)}", - f"--output={str(coPathDest)}", - ] - - print2(f"Bundling/compressing code objects: {' '.join(args)}") - try: - out = subprocess.check_output(args, stderr=subprocess.STDOUT) - print2(f"Output: {out}") - except subprocess.CalledProcessError as err: - raise RuntimeError( - f"Error compressing code object via bundling: {err.output}\nFailed command: {' '.join(args)}" - ) diff --git a/tensilelite/Tensile/BuildCommands/SourceCommands.py b/tensilelite/Tensile/BuildCommands/SourceCommands.py deleted file mode 100644 index 0b1ed01d2f..0000000000 --- a/tensilelite/Tensile/BuildCommands/SourceCommands.py +++ /dev/null @@ -1,194 +0,0 @@ -import itertools -import os -import re -import shlex -import shutil -import subprocess -from pathlib import Path -from typing import Iterable, List, Union - -from ..Common import globalParameters, print2, ensurePath, supportedCompiler, ParallelMap2, splitArchs, which - -def _compileSourceObjectFile(cmdlineArchs: List[str], cxxCompiler: str, cxxSrcPath: str, objDestPath: str, outputPath: str): - """Compiles a source file into an object file. - - Args: - cmdlineArchs: List of architectures for offloading. - cxxCompiler: The C++ compiler to use. - kernelFile: The path to the kernel source file. - buildPath: The build directory path. - objectFilename: The name of the output object file. - outputPath: The output directory path. - globalParameters: A dictionary of global parameters. - - Raises: - RuntimeError: If the compilation command fails. - """ - archFlags = ['--offload-arch=' + arch for arch in cmdlineArchs] - - #TODO(@jichangjichang) Needs to be fixed when Maneesh's change is made available - hipFlags = ["-D__HIP_HCC_COMPAT_MODE__=1"] - hipFlags.extend( - ["--genco"] if cxxCompiler == "hipcc" else ["--cuda-device-only", "-x", "hip", "-O3"] - ) - - hipFlags.extend(['-I', outputPath]) - hipFlags.extend(["-Xoffload-linker", "--build-id=%s"%globalParameters["BuildIdKind"]]) - hipFlags.append('-std=c++17') - if globalParameters["AsanBuild"]: - hipFlags.extend(["-fsanitize=address", "-shared-libasan", "-fuse-ld=lld"]) - if globalParameters["SaveTemps"]: - hipFlags.append('--save-temps') - - launcher = shlex.split(os.environ.get('Tensile_CXX_COMPILER_LAUNCHER', '')) - - if os.name == "nt": - hipFlags.extend(['-fms-extensions', '-fms-compatibility', '-fPIC', '-Wno-deprecated-declarations']) - - args = launcher + [which(cxxCompiler)] + hipFlags + archFlags + [cxxSrcPath, '-c', '-o', objDestPath] - - try: - out = subprocess.check_output(args, stderr=subprocess.STDOUT) - print2(f"Output: {out}" if out else "") - except subprocess.CalledProcessError as err: - raise RuntimeError(f"Error compiling source object file: {err.output}\nFailed command: {' '.join(args)}") - - -def _listTargetTriples(bundler: str, objFile: str) -> List[str]: - """Lists the target triples in an object file. - - Args: - bundler: The path to the bundler, typically ``clang-offload-bundler``. - objFile: The object file path. - - Returns: - List of target triples in the object file. - """ - args = [bundler, "--type=o", f"--input={objFile}", "-list"] - try: - listing = subprocess.check_output(args, stderr=subprocess.STDOUT).decode().split("\n") - except subprocess.CalledProcessError as err: - raise RuntimeError(f"Error listing target triples in object files: {err.output}\nFailed command: {' '.join(args)}") - return listing - - -def _computeSourceCodeObjectFilename(target: str, base: str, buildPath: Union[Path, str], arch: str) -> Union[Path, None]: - """Generates a code object file path using the target, base, and build path. - - Args: - target: The target triple. - base: The base name for the output file (name without extension). - buildPath: The build directory path. - - Returns: - Path to the code object file. - """ - coPath = None - buildPath = Path(buildPath) - if "TensileLibrary" in base and "fallback" in base: - coPath = buildPath / "{0}_{1}.hsaco.raw".format(base, arch) - elif "TensileLibrary" in base: - variant = [t for t in ["", "xnack-", "xnack+"] if t in target][-1] - baseVariant = base + "-" + variant if variant else base - if arch in baseVariant: - coPath = buildPath / (baseVariant + ".hsaco.raw") - else: - coPath= buildPath / "{0}.so-000-{1}.hsaco.raw".format(base, arch) - - return coPath - - -def _unbundleSourceCodeObjects(bundler: str, target: str, infile: str, outfileRaw: str): - """Unbundles source code object files using the Clang Offload Bundler. - - Args: - bundler: The path to the bundler, typically ``clang-offload-bundler``. - target: The target architecture string. - infile: The input file path. - outfileRaw: The output raw file path. - - Raises: - RuntimeError: If unbundling the source code object file fails. - """ - args = [ - bundler, - "--type=o", - f"--targets={target}", - f"--input={infile}", - f"--output={outfileRaw}", - "--unbundle", - ] - - print2("Unbundling source code object file: " + " ".join(args)) - try: - out = subprocess.check_output(args, stderr=subprocess.STDOUT) - print2(f"Output: {out}" if out else "") - except subprocess.CalledProcessError as err: - raise RuntimeError(f"Error unbundling source code object file: {err.output}\nFailed command: {' '.join(args)}") - - -def _buildSourceCodeObjectFile(cxxCompiler: str, offloadBundler: str, outputPath: Union[Path, str], kernelPath: Union[Path, str]) -> List[str]: - """Compiles a HIP source code file into a code object file. - - Args: - cxxCompiler: The C++ compiler to use. - outputPath: The output directory path where code objects will be placed. - kernelPath: The path to the kernel source file. - - Returns: - List of paths to the created code objects. - """ - buildPath = Path(ensurePath(os.path.join(globalParameters['WorkingPath'], 'code_object_tmp'))) - destPath = Path(ensurePath(os.path.join(outputPath, 'library'))) - kernelPath = Path(kernelPath) - - if "CmakeCxxCompiler" in globalParameters and globalParameters["CmakeCxxCompiler"] is not None: - os.environ["CMAKE_CXX_COMPILER"] = globalParameters["CmakeCxxCompiler"] - - objFilename = kernelPath.stem + '.o' - coPathsRaw = [] - coPaths= [] - - if not supportedCompiler(cxxCompiler): - raise RuntimeError("Unknown compiler {}".format(cxxCompiler)) - - _, cmdlineArchs = splitArchs() - - objPath = str(buildPath / objFilename) - _compileSourceObjectFile(cmdlineArchs, cxxCompiler, str(kernelPath), objPath, str(outputPath)) - - if not offloadBundler: - raise RuntimeError("No bundler found; set TENSILE_ROCM_OFFLOAD_BUNDLER_PATH to point to clang-offload-bundler") - - for target in _listTargetTriples(offloadBundler, objPath): - match = re.search("gfx.*$", target) - if match: - arch = re.sub(":", "-", match.group()) - coPathRaw = _computeSourceCodeObjectFilename(target, kernelPath.stem, buildPath, arch) - if not coPathRaw: continue - _unbundleSourceCodeObjects(offloadBundler, target, objPath, str(coPathRaw)) - - coPath = str(destPath / coPathRaw.stem) - coPathsRaw.append(coPathRaw) - coPaths.append(coPath) - - for src, dst in zip(coPathsRaw, coPaths): - shutil.move(src, dst) - - return coPaths - -def buildSourceCodeObjectFiles(cxxCompiler: str, offloadBundler: str, kernelFiles: List[Path], outputPath: Path) -> Iterable[str]: - """Compiles HIP source code files into code object files. - - Args: - cxxCompiler: The C++ compiler to use. - kernelFiles: List of paths to the kernel source files. - outputPath: The output directory path where code objects will be placed. - removeTemporaries: Whether to clean up temporary files. - - Returns: - List of paths to the created code objects. - """ - args = zip(itertools.repeat(cxxCompiler), itertools.repeat(offloadBundler), itertools.repeat(outputPath), kernelFiles) - coFiles = ParallelMap2(_buildSourceCodeObjectFile, args, "Compiling source kernels") - return itertools.chain.from_iterable(coFiles) diff --git a/tensilelite/Tensile/BuildCommands/__init__.py b/tensilelite/Tensile/BuildCommands/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 From d7e6dded0589cbf364f44f0b6061cfffb72b9f56 Mon Sep 17 00:00:00 2001 From: David Dixon Date: Thu, 2 Jan 2025 18:57:49 -0600 Subject: [PATCH 03/10] Remove Toolchain.py --- tensilelite/Tensile/Utilities/Toolchain.py | 173 --------------------- 1 file changed, 173 deletions(-) delete mode 100644 tensilelite/Tensile/Utilities/Toolchain.py diff --git a/tensilelite/Tensile/Utilities/Toolchain.py b/tensilelite/Tensile/Utilities/Toolchain.py deleted file mode 100644 index 6158bd52f9..0000000000 --- a/tensilelite/Tensile/Utilities/Toolchain.py +++ /dev/null @@ -1,173 +0,0 @@ -import os -import re -from pathlib import Path -from typing import List, NamedTuple, Union -from warnings import warn -from subprocess import run, PIPE - -ROCM_BIN_PATH = Path("/opt/rocm/bin") -ROCM_LLVM_BIN_PATH = Path("/opt/rocm/lib/llvm/bin") - -if os.name == "nt": - def _windowsLatestRocmBin(path: Union[Path, str]) -> Path: - """Get the path to the latest ROCm bin directory, on Windows. - - This function assumes that ROCm versions are differentiated with the form ``X.Y``. - - Args: - path: The path to the ROCm root directory, typically ``C:/Program Files/AMD/ROCm``. - - Returns: - The path to the ROCm bin directory for the latest ROCm version. - Typically of the form ``C:/Program Files/AMD/ROCm/X.Y/bin``. - """ - path = Path(path) - pattern = re.compile(r'^\d+\.\d+$') - versions = filter(lambda d: d.is_dir() and pattern.match(d.name), path.iterdir()) - latest = max(versions, key=lambda d: tuple(map(int, d.name.split('.')))) - return latest / "bin" - # LLVM binaries are in the same directory as ROCm binaries on Windows - ROCM_BIN_PATH = _windowsLatestRocmBin("C:/Program Files/AMD/ROCm") - ROCM_LLVM_BIN_PATH = _windowsLatestRocmBin("C:/Program Files/AMD/ROCm") - - -osSelect = lambda linux, windows: linux if os.name != "nt" else windows - - -class ToolchainDefaults(NamedTuple): - CXX_COMPILER= osSelect(linux="amdclang++", windows="clang++.exe") - C_COMPILER= osSelect(linux="amdclang", windows="clang.exe") - OFFLOAD_BUNDLER= osSelect(linux="clang-offload-bundler", windows="clang-offload-bundler.exe") - ASSEMBLER = osSelect(linux="amdclang++", windows="clang++.exe") - HIP_CONFIG = osSelect(linux="hipconfig", windows="hipconfig") - - -def _supportedComponent(component: str, targets: List[str]) -> bool: - isSupported = any([component == t for t in targets]) or any([Path(component).name == t for t in targets]) - return isSupported - - -def supportedCCompiler(compiler: str) -> bool: - """Determine if a C compiler/assembler is supported by Tensile. - - Args: - compiler: The name of a compiler to test for support. - - Return: - If supported True; otherwise, False. - """ - return _supportedComponent(compiler, [ToolchainDefaults.C_COMPILER]) - - -def supportedCxxCompiler(compiler: str) -> bool: - """Determine if a C++/HIP compiler/assembler is supported by Tensile. - - Args: - compiler: The name of a compiler to test for support. - - Return: - If supported True; otherwise, False. - """ - return _supportedComponent(compiler, [ToolchainDefaults.CXX_COMPILER]) - - -def supportedOffloadBundler(bundler: str) -> bool: - """Determine if an offload bundler is supported by Tensile. - - Args: - bundler: The name of an offload bundler to test for support. - - Return: - If supported True; otherwise, False. - """ - return _supportedComponent(bundler, [ToolchainDefaults.OFFLOAD_BUNDLER]) - - -def supportedHip(smi: str) -> bool: - """Determine if an offload bundler is supported by Tensile. - - Args: - bundler: The name of an offload bundler to test for support. - - Return: - If supported True; otherwise, False. - """ - return _supportedComponent(smi, [ToolchainDefaults.HIP_CONFIG]) - - -def _exeExists(file: Path) -> bool: - """Check if a file exists and is executable. - - Args: - file: The file to check. - - Returns: - If the file exists and is executable, True; otherwise, False - """ - if os.access(file, os.X_OK): - return True - return False - - -def _validateExecutable(file: str, searchPaths: List[Path]) -> str: - """Validate that the given toolchain component is in the PATH and executable. - - Args: - file: The executable to validate. - searchPaths: List of directories to search for the executable. - - Returns: - The validated executable with an absolute path. - """ - if not any(( - supportedCxxCompiler(file), supportedCCompiler(file), supportedOffloadBundler(file), supportedHip(file) - )): - raise ValueError(f"{file} is not a supported toolchain component for OS: {os.name}") - - if _exeExists(Path(file)): return file - for path in searchPaths: - path /= file - if _exeExists(path): return str(path) - raise FileNotFoundError(f"`{file}` either not found or not executable in any search path: {':'.join(map(str, searchPaths))}") - - -def validateToolchain(*args: str): - """Validate that the given toolchain components are in the PATH and executable. - - Args: - args: List of executable toolchain components to validate. - - Returns: - List of validated executables with absolute paths. - - Raises: - ValueError: If no toolchain components are provided. - FileNotFoundError: If a toolchain component is not found in the PATH. - """ - if not args: - raise ValueError("No toolchain components to validate, at least one argument is required") - - searchPaths = [ - ROCM_BIN_PATH, - ROCM_LLVM_BIN_PATH, - ] + [Path(p) for p in os.environ["PATH"].split(os.pathsep)] - - out = (_validateExecutable(x, searchPaths) for x in args) - return next(out) if len(args) == 1 else tuple(out) - - -def getVersion(executable: str, versionFlag: str="--version", regex: str=r'version\s+([\d.]+)') -> str: - """Print the version of a toolchain component. - - Args: - executable: The toolchain component to check the version of. - versionFlag: The flag to pass to the executable to get the version. - """ - args = f'"{executable}" "{versionFlag}"' - try: - output = run(args, stdout=PIPE, shell=True).stdout.decode().strip() - match = re.search(regex, output, re.IGNORECASE) - return match.group(1) if match else "" - except Exception as e: - raise RuntimeError(f"Failed to get version when calling {args}: {e}") - From 634b104bdd5b35f2f5028fce4b8ebbb124e4aa64 Mon Sep 17 00:00:00 2001 From: David Dixon Date: Thu, 2 Jan 2025 18:58:33 -0600 Subject: [PATCH 04/10] incorporate new toolchain class --- install.sh | 6 +- tensilelite/Tensile/BenchmarkProblems.py | 31 +- tensilelite/Tensile/ClientWriter.py | 2 +- tensilelite/Tensile/Common.py | 13 +- tensilelite/Tensile/Components/Signature.py | 4 +- tensilelite/Tensile/KernelWriter.py | 265 +++-------------- tensilelite/Tensile/KernelWriterAssembly.py | 25 +- tensilelite/Tensile/KernelWriterBase.py | 3 + tensilelite/Tensile/Ops/AMaxGenerator.py | 2 +- tensilelite/Tensile/Ops/LayerNormGenerator.py | 2 +- tensilelite/Tensile/Ops/SoftmaxGenerator.py | 2 +- tensilelite/Tensile/Parallel.py | 4 +- tensilelite/Tensile/SolutionStructs.py | 5 +- tensilelite/Tensile/Tensile.py | 20 +- tensilelite/Tensile/TensileCreateLibrary.py | 276 +++++++++--------- .../Tensile/TensileInstructions/Base.py | 7 - .../Tensile/TensileInstructions/Code.py | 6 +- .../Tensile/TensileInstructions/Utils.py | 33 +-- 18 files changed, 262 insertions(+), 444 deletions(-) diff --git a/install.sh b/install.sh index aa1ac582cd..d542c5a112 100755 --- a/install.sh +++ b/install.sh @@ -393,7 +393,7 @@ matrices_dir_install= gpu_architecture=all cpu_ref_lib=blis tensile_logic= -tensile_cov= +tensile_cov="4" tensile_threads=$(nproc) tensile_fork= tensile_lazy_library_loading=true @@ -564,10 +564,6 @@ while true; do esac done -if [[ -z $tensile_cov ]]; then - tensile_cov=default -fi - if [[ "${cpu_ref_lib}" == blis ]]; then LINK_BLIS=true elif [[ "${cpu_ref_lib}" == lapack ]]; then diff --git a/tensilelite/Tensile/BenchmarkProblems.py b/tensilelite/Tensile/BenchmarkProblems.py index 0f913681dd..29b49ec915 100644 --- a/tensilelite/Tensile/BenchmarkProblems.py +++ b/tensilelite/Tensile/BenchmarkProblems.py @@ -42,6 +42,8 @@ from .SolutionStructs import Solution, ProblemType, ProblemSizes from .TensileCreateLibrary import copyStaticFiles, writeSolutionsAndKernels from .CustomKernels import getCustomKernelConfig +from .Toolchain.Assembly import AssemblyToolchain +from .Toolchain.Source import SourceToolchain def generateForkedSolutions(problemType, constantParams, forkPermutations, cxxCompiler): @@ -110,7 +112,8 @@ def generateCustomKernelSolutions(problemType, customKernels, internalSupportPar return solutions def writeBenchmarkFiles(stepBaseDir, solutions, problemSizes, \ - biasTypeArgs, factorDimArgs, activationArgs, icacheFlushArgs, stepName, solutionSummationSizes, cxxCompiler, assembler, offloadBundler): + biasTypeArgs, factorDimArgs, activationArgs, icacheFlushArgs, stepName, solutionSummationSizes, \ + asmToolchain: AssemblyToolchain, srcToolchain: SourceToolchain): """Write all the files needed for a given benchmarking step""" copyStaticFiles() @@ -138,19 +141,19 @@ def writeBenchmarkFiles(stepBaseDir, solutions, problemSizes, \ kernelSerialNaming = Solution.getSerialNaming(kernels) kernelMinNaming = Solution.getMinNaming(kernels) - kernelWriterAssembly = KernelWriterAssembly(kernelMinNaming, kernelSerialNaming, cxxCompiler) + kernelWriterAssembly = KernelWriterAssembly(kernelMinNaming, kernelSerialNaming, srcToolchain.compiler) # write solution, kernels and CMake problemType = solutions[0]["ProblemType"] - codeObjectFiles, _ = writeSolutionsAndKernels( \ - globalParameters["WorkingPath"], cxxCompiler, assembler, offloadBundler, \ + codeObjectFiles, _= writeSolutionsAndKernels( \ + globalParameters["WorkingPath"], asmToolchain, srcToolchain, \ solutions, kernels, kernelHelperOjbs, \ kernelWriterAssembly, errorTolerant=True ) # ^ this is where solutions is mutated newLibraryDir = ensurePath(os.path.join(globalParameters["WorkingPath"], 'library')) newLibraryFile = os.path.join(newLibraryDir, "TensileLibrary") - newLibrary = SolutionLibrary.MasterSolutionLibrary.BenchmarkingLibrary(solutions, cxxCompiler) + newLibrary = SolutionLibrary.MasterSolutionLibrary.BenchmarkingLibrary(solutions, srcToolchain.compiler) newLibrary.applyNaming(kernelMinNaming) LibraryIO.write(newLibraryFile, Utils.state(newLibrary), globalParameters["LibraryFormat"]) @@ -192,7 +195,7 @@ def writeBenchmarkFiles(stepBaseDir, solutions, problemSizes, \ def benchmarkProblemType(problemTypeConfig, problemSizeGroupConfig, problemSizeGroupIdx, useCache, - cxxCompiler: str, cCompiler: str, assembler: str, offloadBundler: str + asmToolchain: AssemblyToolchain, srcToolchain: SourceToolchain, cCompiler: str ): """Run the benchmarking for a single entry in the BenchmarkProblems of a Tensile config""" benchmarkTestFails = 0 @@ -275,10 +278,10 @@ def benchmarkProblemType(problemTypeConfig, problemSizeGroupConfig, problemSizeG maxPossibleSolutions = len(forkPermutations) regSolutions = generateForkedSolutions(benchmarkProcess.problemType, \ - benchmarkStep.constantParams, forkPermutations, cxxCompiler) + benchmarkStep.constantParams, forkPermutations, srcToolchain.compiler) kcSolutions = generateCustomKernelSolutions(benchmarkProcess.problemType, \ benchmarkStep.customKernels, benchmarkStep.internalSupportParams, \ - not benchmarkStep.customKernelWildcard, cxxCompiler) + not benchmarkStep.customKernelWildcard, srcToolchain.compiler) maxPossibleSolutions += len(kcSolutions) solutions = regSolutions + kcSolutions @@ -307,7 +310,7 @@ def benchmarkProblemType(problemTypeConfig, problemSizeGroupConfig, problemSizeG codeObjectFiles = writeBenchmarkFiles(stepBaseDir, solutions, \ benchmarkStep.problemSizes, benchmarkStep.biasTypeArgs, \ benchmarkStep.factorDimArgs, benchmarkStep.activationArgs, \ - benchmarkStep.icacheFlushArgs, shortName, [], cxxCompiler, assembler, offloadBundler) + benchmarkStep.icacheFlushArgs, shortName, [], asmToolchain, srcToolchain) # ^ this mutates solutions # write cache data @@ -341,7 +344,7 @@ def benchmarkProblemType(problemTypeConfig, problemSizeGroupConfig, problemSizeG writeClientConfigIni(True, benchmarkStep.problemSizes, benchmarkStep.biasTypeArgs, benchmarkStep.factorDimArgs, benchmarkStep.activationArgs, - benchmakrStep.icacheFlushArgs, conProblemType, + benchmarkStep.icacheFlushArgs, conProblemType, globalParameters["WorkingPath"], codeObjectFiles, resultsFileName, outFile) @@ -356,7 +359,7 @@ def benchmarkProblemType(problemTypeConfig, problemSizeGroupConfig, problemSizeG if not os.path.exists(resultsFileName) or globalParameters["ForceRedoBenchmarkProblems"]: libraryLogicPath = None forBenchmark = True - returncode = runClient(libraryLogicPath, forBenchmark, enableTileSelection, cxxCompiler, cCompiler) + returncode = runClient(libraryLogicPath, forBenchmark, enableTileSelection, srcToolchain.compiler, cCompiler) if returncode: benchmarkTestFails += 1 @@ -376,9 +379,9 @@ def benchmarkProblemType(problemTypeConfig, problemSizeGroupConfig, problemSizeG return (resultsFileBaseFinal, benchmarkTestFails) -def main(config, useCache, cxxCompiler: str, cCompiler: str, assembler: str, offloadBundler: str): +def main(config, useCache, asmToolchain: AssemblyToolchain, srcToolchain: SourceToolchain, cCompiler: str): """Entry point for the "BenchmarkProblems" section of a Tensile config yaml""" - ClientExecutable.getClientExecutable(cxxCompiler, cCompiler) + ClientExecutable.getClientExecutable(srcToolchain.compiler, cCompiler) if config is None: print(f'No config specified in {globalParameters["ConfigPath"]}, built client only') @@ -417,7 +420,7 @@ def main(config, useCache, cxxCompiler: str, cCompiler: str, assembler: str, off # benchmark problem size group (resultsFileBaseFinal, benchmarkErrors) = \ - benchmarkProblemType(problemTypeConfig, sizeGroupConfig, idx, useCache, cxxCompiler, cCompiler, assembler, offloadBundler) + benchmarkProblemType(problemTypeConfig, sizeGroupConfig, idx, useCache, asmToolchain, srcToolchain, cCompiler) totalTestFails += benchmarkErrors print("clientExit={} {} for {}" \ diff --git a/tensilelite/Tensile/ClientWriter.py b/tensilelite/Tensile/ClientWriter.py index af537ef215..4b9f7c1ccc 100644 --- a/tensilelite/Tensile/ClientWriter.py +++ b/tensilelite/Tensile/ClientWriter.py @@ -24,7 +24,7 @@ from . import ClientExecutable from . import LibraryIO -from .TensileInstructions import getGfxName, DataType, getCOVFromParam +from .TensileInstructions import getGfxName, DataType from .Common import globalParameters, pushWorkingPath, popWorkingPath, print1, printExit, CHeader, printWarning, listToInitializer, ClientExecutionLock from .SolutionStructs import Problem, ProblemType, ProblemSizesMock, ProblemSizesMockDummy, ActivationArgs, BiasTypeArgs, FactorDimArgs from .TensileCreateLibrary import copyStaticFiles diff --git a/tensilelite/Tensile/Common.py b/tensilelite/Tensile/Common.py index 97a7a708bd..8325ba2d93 100644 --- a/tensilelite/Tensile/Common.py +++ b/tensilelite/Tensile/Common.py @@ -25,9 +25,9 @@ from . import __version__ from . import Parallel from .TensileInstructions import getGfxName, TensileInstructions -from .Utilities.Toolchain import supportedCxxCompiler as supportedCompiler from collections import OrderedDict from copy import deepcopy +from typing import Tuple import math import os.path @@ -35,6 +35,10 @@ import sys import time import re + + +IsaVersion = Tuple[int, int, int] + startTime = time.time() @@ -250,7 +254,7 @@ else: globalParameters["RuntimeLanguage"] = "HIP" -globalParameters["CodeObjectVersion"] = "default" +globalParameters["CodeObjectVersion"] = "4" globalParameters["Architecture"] = "all" # might be deprecated @@ -1582,7 +1586,7 @@ def capRow(caps, cap): printTable([headerRow] + asmCapRows + archCapRows) def which(p): - if supportedCompiler(p) and 'CMAKE_CXX_COMPILER' in os.environ and os.path.isfile(os.environ['CMAKE_CXX_COMPILER']): + if 'CMAKE_CXX_COMPILER' in os.environ and os.path.isfile(os.environ['CMAKE_CXX_COMPILER']): return os.environ['CMAKE_CXX_COMPILER'] if os.name == "nt": exes = [p+x for x in ['.exe', '', '.bat']] # bat may be front end for file with no extension @@ -1698,6 +1702,9 @@ def assignGlobalParameters(config, cxxCompiler=None): if "KeepBuildTmp" in config: globalParameters["KeepBuildTmp"] = config["KeepBuildTmp"] + if "CodeObjectVersion" in config: + globalParameters["CodeObjectVersion"] = config["CodeObjectVersion"] + # read current gfx version returncode = detectGlobalCurrentISA() if globalParameters["CurrentISA"] == (0,0,0): diff --git a/tensilelite/Tensile/Components/Signature.py b/tensilelite/Tensile/Components/Signature.py index bc3ff3adc7..9c1b8c4b2b 100644 --- a/tensilelite/Tensile/Components/Signature.py +++ b/tensilelite/Tensile/Components/Signature.py @@ -25,7 +25,7 @@ from ..Component import Signature from ..Common import globalParameters from ..Utils import DataDirection -from ..TensileInstructions import SignatureBase, getCOVFromParam +from ..TensileInstructions import SignatureBase from ..TensileInstructions import SignatureValueKind as SVK from ..Activation import ActivationType @@ -128,7 +128,7 @@ def __call__(self, writer) -> SignatureBase: sgprWgZ = 1 if kernel["ProblemType"]["NumIndicesC"] > 2 else 0 signature = SignatureBase(kernelName=writer.states.kernelName, kernArgsVersion=kernel["InternalSupportParams"]["KernArgsVersion"], - codeObjectVersion=getCOVFromParam(kernel["CodeObjectVersion"]), + codeObjectVersion=kernel["CodeObjectVersion"], groupSegmentSize=group_segment_size, sgprWorkGroup=[1, 1, sgprWgZ], vgprWorkItem=0, diff --git a/tensilelite/Tensile/KernelWriter.py b/tensilelite/Tensile/KernelWriter.py index 12b77bfbcf..3ac5e61da7 100644 --- a/tensilelite/Tensile/KernelWriter.py +++ b/tensilelite/Tensile/KernelWriter.py @@ -26,12 +26,12 @@ from .TensileInstructions import Item, TensileInstructions, slash50, replaceHolder, \ KernelBody, Module, StructuredModule, TextBlock, Dump, LabelManager, \ RegisterPool, Assert, fastdeepcopy, TensileInstructionsPassOptions, \ - TensileInstructionsPass, getAsmCompileArgs, getAsmLinkCodeObjectArgs, \ + TensileInstructionsPass, \ SLongBranchPositive, SBranch, SCBranchSCC0, SCBranchSCC1 from .TensileInstructions.Instructions import * from .KernelWriterModules import * from .TensilePass import TensilePass, TensilePassOptions -from .Common import globalParameters, CHeader, roundUp, Backup, print2, printExit +from .Common import globalParameters, CHeader, print1, printWarning, roundUp, Backup, print2, printExit from .Component import Component, LraTileProperties from .Components.Signature import UserArgumentsInfo from .CustomKernels import isCustomKernelConfig @@ -44,7 +44,6 @@ import abc import os import shutil -import subprocess import sys import collections from dataclasses import dataclass, field @@ -2879,6 +2878,7 @@ def kernelBody( self, kernel, tensorParametersA, tensorParametersB ): TensileInstructionsPass(moduleKernelBody, tipo) error = self.states.overflowedResources + print2(f" found error code {error} with overflowed resources set to {self.states.overflowedResources}") return (error, str(moduleKernelBody)) @@ -4962,194 +4962,53 @@ def _shortenFileBase(self, kernel): return firstPart + secondPart - def _byteArrayScriptSource(self): - return """ -#!/usr/bin/env python - -fileString = "" -fileString += "/*******************************************************************************\\n" -fileString += "* Copyright (C) 2022 Advanced Micro Devices, Inc. All rights reserved.\\n" -fileString += "*\\n" -fileString += "* Permission is hereby granted, free of charge, to any person obtaining a copy\\n" -fileString += '* of this software and associated documentation files (the \"Software\"), to deal\\n' -fileString += "* in the Software without restriction, including without limitation the rights\\n" -fileString += "* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cop-\\n" -fileString += "* ies of the Software, and to permit persons to whom the Software is furnished\\n" -fileString += "* to do so, subject to the following conditions:\\n" -fileString += "*\\n" -fileString += "* The above copyright notice and this permission notice shall be included in all\\n" -fileString += "* copies or substantial portions of the Software.\\n" -fileString += "*\\n" -fileString += '* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM-\\n' -fileString += "* PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\\n" -fileString += "* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\\n" -fileString += "* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\\n" -fileString += "* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNE-\\n" -fileString += "* CTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\\n" -fileString += "*******************************************************************************/\\n\\n" -fileString += "/**************************************************\\n" -fileString += "* This file was generated by Tensile: *\\n" -fileString += "* https://github.com/ROCmSoftwarePlatform/Tensile *\\n" -fileString += "**************************************************/\\n\\n\\n" -import os.path -fileString += '#include "Kernels.h"\\n\\n' -fileString += "/* code object byte array */\\n\\n" -codeObjectFileNames = [f for f in os.listdir(".") if (os.path.isfile(f) and f.endswith(".co"))] -for codeObjectFileName in codeObjectFileNames: - print codeObjectFileName - print "\\n" - kernelName=os.path.splitext(codeObjectFileName)[0] - codeObjectFile = open(codeObjectFileName, "r") - codeObjectByteArray = bytearray(codeObjectFile.read()) - codeObjectFile.close() -# write code object byte array for asm - fileString += "const unsigned char %s_coba[%u] = {\\n" % (kernelName, len(codeObjectByteArray)) - for byteIdx in range(0, len(codeObjectByteArray)): - byte = codeObjectByteArray[byteIdx] - fileString += "0x%02x" % byte - if byteIdx < len(codeObjectByteArray)-1: - fileString += "," - else: - fileString += "};\\n" - if byteIdx % 16 == 15: - fileString += "\\n" - text_file = open("Kernels.cpp", "w") - text_file.write("%s" % fileString) - text_file.close() -""" - - def _writeByteArrayScript(self): - asmPath = self.getAssemblyDirectory() - - bytearrayFileName = os.path.join(asmPath,"insert_byte_array.py") - if not os.path.isfile(bytearrayFileName): - with open(bytearrayFileName, 'w') as bytearrayFile: - bytearrayFile.write(self._byteArrayScriptSource()) - os.chmod(bytearrayFileName, 0o777) - return bytearrayFileName - - def getReplacementKernelPath(self, kernel): - if not isCustomKernelConfig(kernel): - return None - kernelName = self.getKernelName(kernel) + def _getCustomKernelSource(self, kernel, CustomKernelDirectory): + kernelName = self.getKernelFileBase(kernel) + with open(os.path.join(CustomKernelDirectory, (kernelName + ".s"))) as f: + hipccver = globalParameters['HipClangVersion'].split(".") + hipccMaj = int(hipccver[0]) + hipccPatch = int(hipccver[2].split("-")[0]) + if not (hipccMaj >= 6 and hipccPatch >= 32650): + code = [] + for line in f.readlines(): + if "amdhsa_user_sgpr_kernarg_preload" not in line: + code.append(line) + code = "".join(code) + else: + code = f.read() - if isCustomKernelConfig(kernel): - return os.path.join(globalParameters["CustomKernelDirectory"], (kernelName + ".s")) - else: # Replacement kernel - return ReplacementKernels.Get(kernelName) + self.tPA = tensorParametersA = {} + self.tPB = tensorParametersB = {} + self.states.kernel = kernel + self.states.language = "ASM" + self.states.version = tuple(kernel["ISA"]) if "ISA" in kernel else globalParameters["CurrentISA"] + if not globalParameters["AsmCaps"][self.states.version]["SupportedISA"]: + self.states.version = (9,0,0) + printWarning(f"ISA: {self.version} is not supported; overriding with {self.states.version}") - def _getKernelSource(self, kernel): + return code + + def _getKernelSource(self, kernel: Solution): """ Returns the source of the kernel, either C++ or assembly. """ - fileString = "" tensorParametersA = {} tensorParametersB = {} - self.initKernel(kernel, tensorParametersA, tensorParametersB ) + self.initKernel(kernel, tensorParametersA, tensorParametersB) self.stringIdx = 0 - (error, kb) = self.kernelBody( kernel, tensorParametersA, tensorParametersB) + (error, kb) = self.kernelBody(kernel, tensorParametersA, tensorParametersB) fileString += str(kb) if error != 0: if globalParameters["ForceGenerateKernel"]: - print ("warning: Generating kernel source resulted in error {}, but ForceGenerateKernel=1 so saving source".format(error)) + printWarning("Generating kernel source resulted in error {}, but ForceGenerateKernel=1 so saving source".format(error)) else: raise RuntimeError("Generating kernel source resulted in error {}".format(error)) return fileString - def _getKernelObjectAssemblyFile(self, kernel): - asmPath = self.getAssemblyDirectory() - # write assembly file to assembly directory - kernelName = self.getKernelFileBase(kernel) - fileBase = os.path.join(asmPath, kernelName ) - assemblyFileName = "%s.s" % fileBase - - replacementKernel = self.getReplacementKernelPath(kernel) - - if replacementKernel is not None: - self.tPA = tensorParametersA = {} - self.tPB = tensorParametersB = {} - if isCustomKernelConfig(kernel): - kernelFoundMessage = "Custom kernel filename " - # ISA version, such as 803 - self.states.kernel = kernel - self.states.language = "ASM" - self.states.version = globalParameters["CurrentISA"] - if "ISA" in kernel: - self.states.version = tuple(kernel["ISA"]) - if not globalParameters["AsmCaps"][self.states.version]["SupportedISA"]: - defaultIsa = (9,0,0) - print("warning: ISA:", self.version, " is not supported; overriding with ", defaultIsa) - self.states.version = defaultIsa - else: - kernelFoundMessage = "replacement_assemblyFilename " - self.initKernel(kernel, tensorParametersA, tensorParametersB ) - - shutil.copyfile(replacementKernel, assemblyFileName) - - # Temporary remove preload kernel argument for rpk - hipccver = globalParameters['HipClangVersion'].split(".") - hipccMaj = int(hipccver[0]) - hipccPatch = int(hipccver[2].split("-")[0]) - if not (hipccMaj >= 6 and hipccPatch >= 32650): - os.system("sed -i '/amdhsa_user_sgpr_kernarg_preload_length/d' %s"%assemblyFileName) - os.system("sed -i '/amdhsa_user_sgpr_kernarg_preload_offset/d' %s"%assemblyFileName) - - if globalParameters["PrintLevel"] >= 2: - print(kernelFoundMessage + assemblyFileName) - print(self.states.kernel) - else: - kernelSource = self._getKernelSource(kernel) - - if globalParameters["PrintLevel"] >= 2: - print("write_assemblyFilename %s" % assemblyFileName) - print(self.states.kernel) - - with open(assemblyFileName, 'w') as assemblyFile: - assemblyFile.write(kernelSource) - - return assemblyFileName - - def _getAssembledKernelObjectFile(self, kernel): - assemblyFileName = self._getKernelObjectAssemblyFile(kernel) - - base, ext = os.path.splitext(assemblyFileName) - objectFileName = base + '.o' - - debug = globalParameters.get("AsmDebug", False) - args = self.getCompileArgs(assemblyFileName, objectFileName, debug=debug) - if globalParameters["PrintCodeCommands"]: - print (' '.join(args), " && ") - - subprocess.check_call(args, cwd=self.getAssemblyDirectory()) - - if not globalParameters["KeepBuildTmp"]: - os.remove(assemblyFileName) - - return objectFileName - - def _getSingleCodeObjectFile(self, kernel): - objectFileName = self._getAssembledKernelObjectFile(kernel) - - base, ext = os.path.splitext(objectFileName) - coFileName = base + '.co' - - args = self.getLinkCodeObjectArgs([objectFileName], coFileName) - if globalParameters["PrintCodeCommands"]: - print (' '.join(args)) - - subprocess.check_call(args, cwd=self.getAssemblyDirectory()) - - return coFileName - - ############################################################################## - # - # Entry Functions - # - ############################################################################## ############################################################################## # get kernel name @@ -5167,10 +5026,8 @@ def getKernelName(self, kernel): kernelName = Solution.getNameMin(kernel, self.kernelMinNaming, True) return kernelName - def getAssemblyDirectory(self): - return Common.ensurePath(os.path.join(globalParameters["WorkingPath"], "assembly")) - - def getSourceFileString(self, kernel): + @abc.abstractmethod + def getSourceFileString(self, kernel) -> Tuple[int, str]: """ Returns a string suitable for placing in Kernels.cpp. This means the actual kernel source in the case of a source kernel, or an assembled code object byte array definition in the case of an assembly kernel, @@ -5182,43 +5039,8 @@ def getSourceFileString(self, kernel): * A code object file * A Python script which can create byte array variable definitions. """ + pass - try: - if kernel["KernelLanguage"] == "Assembly": - # asmPath = self.getAssemblyDirectory() - # kernelName = self.getKernelName(kernel) - - # Skip if .o files will have already been built for this file - # @TODO remove need for this with better code organization - if kernel.duplicate: - self.language = "ASM" - return (0, "") - if globalParameters["GenerateSourcesAndExit"]: - # only create the assembly file. - self._getKernelObjectAssemblyFile(kernel) - return (0, "") - else: - self._writeByteArrayScript() - self._getSingleCodeObjectFile(kernel) - - # I guess in this case we are making sure that the code object file exists by executing the code - # above but we aren't placing it into the source. - return (0, "") - - else: - return (0, self._getKernelSource(kernel)) - - except subprocess.CalledProcessError as exc: - print(exc) - return (-1, "") - except RuntimeError as exc: - if globalParameters["PrintSolutionRejectionReason"]: - print(exc) - return (-2, "") - - ############################################################################## - # header file string - ############################################################################## def getHeaderFileString(self, kernel): kernelName = self.getKernelName(kernel) fileString = "" # CHeader @@ -5227,21 +5049,6 @@ def getHeaderFileString(self, kernel): return fileString - ############################################################################## - # Compile Args - ############################################################################## - def getCompileArgs(self, sourceFileName, objectFileName, *moreArgs, isa=None, wavefrontSize=None, debug=False): - if isa is None: - isa = self.states.version - if wavefrontSize is None: - wavefrontSize = self.states.kernel["WavefrontSize"] - return getAsmCompileArgs(self.assembler, \ - globalParameters["CodeObjectVersion"], \ - isa, wavefrontSize, sourceFileName, objectFileName, *moreArgs, debug=debug) - - def getLinkCodeObjectArgs(self, objectFileNames, coFileName, *moreArgs): - return getAsmLinkCodeObjectArgs(self.assembler, \ - objectFileNames, coFileName, globalParameters['BuildIdKind'], *moreArgs) def setTensileInstructions(self, ti): self.ti = ti @@ -5294,3 +5101,7 @@ def findInstByName(module, names, count): else: _placeholder.add(SBranch(labelName=_target.getLabelName())) currentInstLength += _placeholder.countType(Instruction) + + @property + def isa(self): + return self.states.version diff --git a/tensilelite/Tensile/KernelWriterAssembly.py b/tensilelite/Tensile/KernelWriterAssembly.py index f59ff7a586..538b7849df 100644 --- a/tensilelite/Tensile/KernelWriterAssembly.py +++ b/tensilelite/Tensile/KernelWriterAssembly.py @@ -38,7 +38,7 @@ LabelManager, Assert from .TensileInstructions.Instructions import * from .TensilePass import getActivationFunctionModuleName, getActivationBranchModuleName -from .Common import globalParameters, print2, printExit, printWarning, roundUp +from .Common import globalParameters, print2, printExit, printWarning, roundUp, ensurePath from .TensileInstructions.Containers import HWRegContainer from .Component import Component from .KernelWriter import KernelWriter, ConstValues, StateValues, StateVgprs, CodeModules @@ -48,13 +48,15 @@ from .AsmMemoryInstruction import MemoryInstruction from .Activation import ActivationType from .Utils import DataDirection +from .CustomKernels import isCustomKernelConfig from math import ceil, log, floor from copy import deepcopy from dataclasses import dataclass, field -from typing import NamedTuple +from typing import NamedTuple, Tuple -import collections +import os +import subprocess ################################################################################ # Assembly Kernel @@ -68,6 +70,23 @@ class KernelWriterAssembly(KernelWriter): def __init__(self, kernelMinNaming, kernelSerialNaming, assembler: str): super(KernelWriterAssembly, self).__init__(kernelMinNaming, kernelSerialNaming, assembler) + + def getSourceFileString(self, kernel) -> Tuple[int, str]: + assert kernel["KernelLanguage"] == "Assembly" + # Skip if .o files will have already been built for this file + if kernel.duplicate: + self.language = "ASM" + return (0, "") # should this be an non zero number + + try: + code = self._getCustomKernelSource(kernel, globalParameters["CustomKernelDirectory"]) if isCustomKernelConfig(kernel) else self._getKernelSource(kernel) + errcode = 0 + except RuntimeError as e: + printWarning(f"Failed to generate assembly source code for {kernel}: {e}") + code = "" + errcode = -2 + return (errcode, code) + def getSgprOccupancy(self, sgprs): return self.states.regCaps["PhysicalMaxSgpr"]//sgprs diff --git a/tensilelite/Tensile/KernelWriterBase.py b/tensilelite/Tensile/KernelWriterBase.py index f7038dce6d..85e3fdca0b 100644 --- a/tensilelite/Tensile/KernelWriterBase.py +++ b/tensilelite/Tensile/KernelWriterBase.py @@ -26,6 +26,9 @@ from abc import abstractmethod from copy import deepcopy +KERNEL_HELPER_FILENAME_CPP: str = "Kernels.cpp" +KERNEL_HELPER_FILENAME_H: str = "Kernels.h" + class KernelWriterBase(ABC): def __init__(self): diff --git a/tensilelite/Tensile/Ops/AMaxGenerator.py b/tensilelite/Tensile/Ops/AMaxGenerator.py index 04f5dd4a28..d118293820 100644 --- a/tensilelite/Tensile/Ops/AMaxGenerator.py +++ b/tensilelite/Tensile/Ops/AMaxGenerator.py @@ -34,7 +34,7 @@ import Tensile.TensileInstructions as ti from Tensile.Common import detectGlobalCurrentISA, restoreDefaultGlobalParameters, \ assignGlobalParameters, getGfxName, gfxArch, globalParameters -from Tensile.Utilities.Toolchain import ToolchainDefaults, validateToolchain +from Tensile.Toolchain.Validators import ToolchainDefaults, validateToolchain def kernel_header(name: str, gfx_arch: str, vgpr: int, sgpr: int, lds: int): vgpr = ((vgpr+7)//8)*8 diff --git a/tensilelite/Tensile/Ops/LayerNormGenerator.py b/tensilelite/Tensile/Ops/LayerNormGenerator.py index 2d7dc17f20..e452b797f0 100644 --- a/tensilelite/Tensile/Ops/LayerNormGenerator.py +++ b/tensilelite/Tensile/Ops/LayerNormGenerator.py @@ -34,7 +34,7 @@ import Tensile.TensileInstructions as ti from Tensile.Common import detectGlobalCurrentISA, restoreDefaultGlobalParameters, \ assignGlobalParameters, getGfxName, gfxArch, globalParameters -from Tensile.Utilities.Toolchain import ToolchainDefaults, validateToolchain +from Tensile.Toolchain.Validators import ToolchainDefaults, validateToolchain def kernel_header(name: str, gfx_arch: str, vgpr: int, sgpr: int, lds: int): vgpr = ((vgpr+7)//8)*8 diff --git a/tensilelite/Tensile/Ops/SoftmaxGenerator.py b/tensilelite/Tensile/Ops/SoftmaxGenerator.py index ab40492258..077487673f 100644 --- a/tensilelite/Tensile/Ops/SoftmaxGenerator.py +++ b/tensilelite/Tensile/Ops/SoftmaxGenerator.py @@ -33,7 +33,7 @@ import Tensile.TensileInstructions as ti from Tensile.Common import detectGlobalCurrentISA, restoreDefaultGlobalParameters, \ assignGlobalParameters, getGfxName, gfxArch, globalParameters -from Tensile.Utilities.Toolchain import ToolchainDefaults, validateToolchain +from Tensile.Toolchain.Validators import ToolchainDefaults, validateToolchain def record_num_calls(f): @wraps(f) diff --git a/tensilelite/Tensile/Parallel.py b/tensilelite/Tensile/Parallel.py index bf9a44db1c..2bdd45f45e 100644 --- a/tensilelite/Tensile/Parallel.py +++ b/tensilelite/Tensile/Parallel.py @@ -182,7 +182,6 @@ def ParallelMap2(function, objects, message="", enable=True, multiArg=True, retu multiArg: True if objects represent multiple arguments (differentiates multi args vs single collection arg) """ - if return_as in ('generator', 'generator_unordered') and not joblibParallelSupportsGenerator(): return ParallelMapReturnAsGenerator(function, objects, message, enable, multiArg) @@ -192,8 +191,7 @@ def ParallelMap2(function, objects, message="", enable=True, multiArg=True, retu if threadCount <= 1 and globalParameters["ShowProgressBar"]: # Provide a progress bar for single-threaded operation. - callFunc = lambda args: function(*args) if multiArg else lambda args: function(args) - return [callFunc(args) for args in Utils.tqdm(objects, message)] + return [function(*args) if multiArg else function(args) for args in Utils.tqdm(objects, message)] countMessage = "" try: diff --git a/tensilelite/Tensile/SolutionStructs.py b/tensilelite/Tensile/SolutionStructs.py index abda64bf06..4468a683ea 100644 --- a/tensilelite/Tensile/SolutionStructs.py +++ b/tensilelite/Tensile/SolutionStructs.py @@ -1095,10 +1095,9 @@ def __init__(self, config, cxxCompiler: str): if "CodeObjectVersion" not in self._state: if "CodeObjectVersion" in config: - self._state["CodeObjectVersion"] = config["CodeObjectVersion"] + self._state["CodeObjectVersion"] = str(config["CodeObjectVersion"]) else: - self._state["CodeObjectVersion"] = globalParameters["CodeObjectVersion"] - + self._state["CodeObjectVersion"] = str(globalParameters["CodeObjectVersion"]) # assign parameters without defaults for key in config: if (key != "ProblemType" or key != "InternalSupportParams") and key not in self._state: diff --git a/tensilelite/Tensile/Tensile.py b/tensilelite/Tensile/Tensile.py index af03f130fa..7a4d36319b 100644 --- a/tensilelite/Tensile/Tensile.py +++ b/tensilelite/Tensile/Tensile.py @@ -31,7 +31,9 @@ import argparse from .Common import globalParameters, print1, printExit, printWarning, ensurePath, \ assignGlobalParameters, restoreDefaultGlobalParameters, HR -from .Utilities.Toolchain import ToolchainDefaults, validateToolchain +from .Toolchain.Assembly import AssemblyToolchain +from .Toolchain.Source import SourceToolchain +from .Toolchain.Validators import validateToolchain, ToolchainDefaults from . import BenchmarkProblems from . import ClientWriter from . import LibraryIO @@ -48,13 +50,13 @@ # LibraryLogic.main() to analyse final benchmark data and produce logic/yaml # ClientWriter.main() to create client which calls library based on above yaml ################################################################################ -def executeStepsInConfig(config, cxxCompiler: str, cCompiler: str, assembler: str, offloadBundler: str): +def executeStepsInConfig(config, asmToolchain: AssemblyToolchain, srcToolchain: SourceToolchain, cCompiler: str): ############################################################################## # Benchmark Problems ############################################################################## if "BenchmarkProblems" in config: - BenchmarkProblems.main(config["BenchmarkProblems"], config["UseCache"], cxxCompiler, cCompiler, assembler, offloadBundler) + BenchmarkProblems.main(config["BenchmarkProblems"], config["UseCache"], asmToolchain, srcToolchain, cCompiler) print1("") ############################################################################## @@ -72,7 +74,7 @@ def executeStepsInConfig(config, cxxCompiler: str, cCompiler: str, assembler: st libraryLogicConfig = config["LibraryLogic"] else: libraryLogicConfig = {} - LibraryLogic.main(libraryLogicConfig, cxxCompiler) + LibraryLogic.main(libraryLogicConfig, srcToolchain.compiler) print1("") else: print1("# LibraryLogic already done.") @@ -86,7 +88,7 @@ def executeStepsInConfig(config, cxxCompiler: str, cCompiler: str, assembler: st libraryClientConfig = config["LibraryClient"] else: libraryClientConfig = {} - ClientWriter.main(libraryClientConfig, cxxCompiler, cCompiler) + ClientWriter.main(libraryClientConfig, srcToolchain.compiler, cCompiler) print1("") @@ -112,7 +114,7 @@ def splitExtraParameters(par): argParser.add_argument("--runtime-language", dest="RuntimeLanguage", \ choices=["HIP", "OCL"], help="override which runtime language to use") argParser.add_argument("--code-object-version", dest="CodeObjectVersion", \ - choices=["default", "V4", "V5"], help="HSA code-object version") + choices=["4", "5"], action="store", default="4", help="HSA code-object version") argParser.add_argument("-v", "--verbose", action="store_true", \ help="set PrintLevel=2") argParser.add_argument("--debug", dest="debug", action="store_true", \ @@ -271,6 +273,10 @@ def Tensile(userArgs): cxxCompiler, cCompiler, assembler, offloadBundler = validateToolchain(args.CxxCompiler, args.CCompiler, args.Assembler, args.OffloadBundler) assignGlobalParameters(config.get("GlobalParameters", {}), cxxCompiler) + + asmToolchain = AssemblyToolchain(assembler, offloadBundler, globalParameters["BuildIdKind"], globalParameters["CodeObjectVersion"]) + srcToolchain = SourceToolchain(cxxCompiler, offloadBundler, globalParameters["BuildIdKind"], globalParameters["AsanBuild"], globalParameters["SaveTemps"]) + globalParameters["OutputPath"] = ensurePath(os.path.abspath(args.output_path)) globalParameters["WorkingPath"] = globalParameters["OutputPath"] @@ -289,7 +295,7 @@ def Tensile(userArgs): profiler = cProfile.Profile() profiler.enable() - executeStepsInConfig(config, cxxCompiler, cCompiler, assembler, offloadBundler) + executeStepsInConfig(config, asmToolchain, srcToolchain, cCompiler) if profiler: profiler.disable() diff --git a/tensilelite/Tensile/TensileCreateLibrary.py b/tensilelite/Tensile/TensileCreateLibrary.py index 41777a4cba..070c26b930 100644 --- a/tensilelite/Tensile/TensileCreateLibrary.py +++ b/tensilelite/Tensile/TensileCreateLibrary.py @@ -28,35 +28,37 @@ print("This file can no longer be run as a script. Run 'Tensile/bin/TensileCreateLibrary' instead.") exit(1) +import functools + from . import Common from . import ClientExecutable from . import EmbeddedData from . import LibraryIO from . import Utils +from .Toolchain.Assembly import AssemblyToolchain, buildAssemblyCodeObjectFiles +from .Toolchain.Source import SourceToolchain, buildSourceCodeObjectFiles +from .Toolchain.Validators import validateToolchain, getVersion, ToolchainDefaults from .TensileInstructions import getGfxName, TensileInstructions from .Common import globalParameters, HR, print1, print2, printExit, ensurePath, \ - CHeader, CMakeHeader, assignGlobalParameters, \ + CHeader, assignGlobalParameters, \ architectureMap, printWarning, \ - splitArchs + IsaVersion from .KernelWriterAssembly import KernelWriterAssembly +from .KernelWriterBase import KERNEL_HELPER_FILENAME_CPP, KERNEL_HELPER_FILENAME_H from .SolutionLibrary import MasterSolutionLibrary from .SolutionStructs import Solution from .CustomYamlLoader import load_logic_gfx_arch from .Utilities.Profile import profile -from .Utilities.Toolchain import getVersion, validateToolchain, ToolchainDefaults -from .BuildCommands import SourceCommands, AssemblyCommands - import argparse import collections import glob import itertools import os -import re import shutil import sys from timeit import default_timer as timer from pathlib import Path -from typing import Sequence, List, Union +from typing import Sequence, List, Union, NamedTuple, Optional def timing(func): def wrapper(*args, **kwargs): @@ -69,38 +71,42 @@ def wrapper(*args, **kwargs): return res return wrapper -################################################################################ -def processKernelSource(kernel, kernelWriterAssembly, ti): + + +class KernelCodeGenResult(NamedTuple): + err: int + src: str + header: Optional[str] + name: str + targetObjFilename: str + isa: IsaVersion + wavefrontSize: int + + +def processKernelSource(kernel, kernelWriterAssembly, ti) -> KernelCodeGenResult: """ Generate source for a single kernel. Returns (error, source, header, kernelName). """ - try: - kernelWriter = kernelWriterAssembly - # get kernel name - kernelWriter.setTensileInstructions(ti) - kernelName = kernelWriter.getKernelFileBase(kernel) - (err, src) = kernelWriter.getSourceFileString(kernel) - header = kernelWriter.getHeaderFileString(kernel) - # will be put in Kernels.h/cpp if None - filename = kernel._state.get("codeObjectFile", None) - - except RuntimeError: - return (1, "", "", kernelName, None) + kernelWriter = kernelWriterAssembly + # get kernel name + kernelWriter.setTensileInstructions(ti) + asmFilename = kernelWriter.getKernelFileBase(kernel) + err, src = kernelWriter.getSourceFileString(kernel) + header = kernelWriter.getHeaderFileString(kernel) + # will be put in Kernels.h/cpp if None + objFilename = kernel._state.get("codeObjectFile", None) - return (err, src, header, kernelName, filename) + return KernelCodeGenResult(err, src, header, asmFilename, objFilename, tuple(kernel["ISA"]), kernel["WavefrontSize"]) - -################################################################################ -def buildKernelSourceAndHeaderFiles(results, outputPath, kernelsWithBuildErrs): +def buildKernelSourceAndHeaderFiles(results, outputPath): """ Logs errors and writes appropriate info to kernelSourceFile and kernelHeaderFile. Arguments: results: list of (err, src, header, kernelName, filename) outputPath: path to source directory - kernelsWithBuildErrs: Dictionary to be updated with kernels that have errors kernelSourceFile: File to write source data to kernelHeaderFile: File to write header data to @@ -114,10 +120,6 @@ def buildKernelSourceAndHeaderFiles(results, outputPath, kernelsWithBuildErrs): validKernelCount = 0 for (err,src,header,kernelName, filename) in results: - # Keep track of kernels with errors - if err: - kernelsWithBuildErrs[kernelName] = err - # Don't create a file for empty kernels if len(src.strip()) == 0: continue @@ -163,13 +165,85 @@ def buildKernelSourceAndHeaderFiles(results, outputPath, kernelsWithBuildErrs): return sourceFilenames +def removeInvalidSolutionsAndKernels(results, kernels, solutions, errorTolerant, globalParameters): + removeKernels = [] + removeKernelNames = [] + removeSolutions = [] + removeResults = [] + + for kernIdx, r in Utils.tqdm(enumerate(results)) if globalParameters["PrintLevel"] > 1 else enumerate(results): + if r.err != 0: + if not errorTolerant: + print("\nKernel generation failed for kernel: {}".format(kernels[kernIdx]["SolutionIndex"])) + print(kernels[kernIdx]["SolutionNameMin"]) + removeKernels.append(kernels[kernIdx]) + kName = Solution.getKeyNoInternalArgs(kernels[kernIdx]) + if kName not in removeKernelNames: + removeKernelNames.append(kName) + removeResults.append(results[kernIdx]) + + if len(removeKernels) > 0 and not errorTolerant: + printExit("** kernel generation failure **") + + for kern in removeKernels: + kernels.remove(kern) + + for solution in Utils.tqdm(solutions, "Finding invalid solutions") if globalParameters["PrintLevel"] > 1 else solutions: + solutionKernels = solution.getKernels() + for kernel in solutionKernels: + kName = Solution.getKeyNoInternalArgs(kernel) + if kName in removeKernelNames: + removeSolutions.append(solution) + break + + for solut in removeSolutions: + solutions.remove(solut) + + for rel in removeResults: + results.remove(rel) + +def writeAssembly(asmPath: Union[Path, str], result: KernelCodeGenResult): + if result.err: + printExit(f"Failed to build kernel {result.name} because it has error code {result.err}") + path = Path(asmPath) / f"{result.name}.s" + with open(path, "w", encoding="utf-8") as f: + f.write(result.src) + + return path, result.isa, result.wavefrontSize + + +def writeHelpers(outputPath, kernelHelperObjs, KERNEL_HELPER_FILENAME_CPP, KERNEL_HELPER_FILENAME_H): + kernelSourceFilename = os.path.join(os.path.normcase(outputPath), KERNEL_HELPER_FILENAME_CPP) + kernelHeaderFilename = os.path.join(os.path.normcase(outputPath), KERNEL_HELPER_FILENAME_H) + kernelSourceFile = open(kernelSourceFilename, "a", encoding="utf-8") + kernelHeaderFile = open(kernelHeaderFilename, "a", encoding="utf-8") + + HeaderText = "" + # handle helper kernel function + for ko in kernelHelperObjs: + kernelName = ko.getKernelName() + + (err, src) = ko.getSourceFileString() + kernelSourceFile.write(src) + if err: + print("*** warning: invalid kernel#%u" % kernelName) + + HeaderText += ko.getHeaderFileString() + + # write kernel.h in one shot + kernelHeaderFile.write(HeaderText) + + if kernelSourceFile: + kernelSourceFile.close() + if kernelHeaderFile: + kernelHeaderFile.close() + + ################################################################################ # Write Solutions and Kernels for BenchmarkClient or LibraryClient ################################################################################ -@timing -def writeSolutionsAndKernels(outputPath, cxxCompiler, assembler, offloadBundler, solutions, kernels, kernelHelperObjs, \ +def writeSolutionsAndKernels(outputPath, asmToolchain, srcToolchain, solutions, kernels, kernelHelperObjs, \ kernelWriterAssembly, errorTolerant=False, compress=True): - codeObjectFiles = [] # Push working path into build_tmp folder because there may be more than @@ -179,105 +253,39 @@ def writeSolutionsAndKernels(outputPath, cxxCompiler, assembler, offloadBundler, # See buildSourceCodeObjectFile:167 for the call to this binary. Common.pushWorkingPath('build_tmp') Common.pushWorkingPath(os.path.basename(outputPath).upper()) + asmPath = ensurePath(os.path.join(globalParameters["WorkingPath"], "assembly")) - kernelFiles = [] - kernelSourceFile = None - kernelHeaderFile = None - - ############################################################################## - # Write Kernels - ############################################################################## - kernelsWithBuildErrs = {} + asmKernels = [k for k in kernels if k['KernelLanguage'] == 'Assembly'] # Kernels may be intended for different co files, but generate the same .o file # Mark duplicate kernels to avoid race condition # @TODO improve organization so this problem doesn't appear - objFilenames = set() - for kernel in kernels: - if kernel["KernelLanguage"] == "Assembly": - base = kernelWriterAssembly.getKernelFileBase(kernel) - if base in objFilenames: - kernel.duplicate = True - else: - objFilenames.add(base) - kernel.duplicate = False - numKernels = len(kernels) - - kIter = zip(kernels, itertools.repeat(kernelWriterAssembly), itertools.repeat(TensileInstructions())) - results = Common.ParallelMap2(processKernelSource, kIter, "Generating kernels") - - removeKernels = [] - removeKernelNames = [] - removeSolutions = [] - removeResults = [] - for kernIdx, res in Utils.tqdm(enumerate(results)) if globalParameters["PrintLevel"] > 1 else enumerate(results): - (err,src,header,kernelName, filename) = res - if(err == -2): - if not errorTolerant: - print("\nKernel generation failed for kernel: {}".format(kernels[kernIdx]["SolutionIndex"])) - print(kernels[kernIdx]["SolutionNameMin"]) - removeKernels.append(kernels[kernIdx]) - kName = Solution.getKeyNoInternalArgs(kernels[kernIdx]) - if kName not in removeKernelNames: - removeKernelNames.append(kName) - removeResults.append(results[kernIdx]) - if len(removeKernels) > 0 and not errorTolerant: - printExit("** kernel generation failure **") - for kern in removeKernels: - kernels.remove(kern) - for solution in Utils.tqdm(solutions, "Finding invalid solutions") if globalParameters["PrintLevel"] > 1 else solutions: - solutionKernels = solution.getKernels() - for kernel in solutionKernels: - kName = Solution.getKeyNoInternalArgs(kernel) - if kName in removeKernelNames: - removeSolutions.append(solution) - break - for solut in removeSolutions: - solutions.remove(solut) - for rel in removeResults: - results.remove(rel) - - kernelFiles += buildKernelSourceAndHeaderFiles(results, outputPath, kernelsWithBuildErrs) - - kernelsToBuild = kernels - if errorTolerant: - def success(kernel): - writer = kernelWriterAssembly - kernelName = writer.getKernelName(kernel) - return kernelName not in kernelsWithBuildErrs - kernelsToBuild = filter(success, kernelsToBuild) - elif len(kernelsWithBuildErrs) > 0: - print("\nKernel compilation failed in one or more subprocesses. May want to set CpuThreads=0 and re-run to make debug easier") - printExit("** kernel compilation failure **") - - kernelSourceFilename = os.path.join(os.path.normcase(outputPath), "Kernels.cpp") - kernelHeaderFilename = os.path.join(os.path.normcase(outputPath), "Kernels.h") - kernelSourceFile = open(kernelSourceFilename, "a", encoding="utf-8") - kernelHeaderFile = open(kernelHeaderFilename, "a", encoding="utf-8") - - HeaderText = "" - # handle helper kernel function - for ko in kernelHelperObjs: - kernelName = ko.getKernelName() - - (err, src) = ko.getSourceFileString() - kernelSourceFile.write(src) - if err: - print("*** warning: invalid kernel#%u"%kernelName) - - HeaderText += ko.getHeaderFileString() - - # write kernel.h in one shot - kernelHeaderFile.write(HeaderText) - - if kernelSourceFile: - kernelSourceFile.close() - if kernelHeaderFile: - kernelHeaderFile.close() - - if not globalParameters["GenerateSourcesAndExit"]: - codeObjectFiles += SourceCommands.buildSourceCodeObjectFiles(cxxCompiler, offloadBundler, kernelFiles, outputPath) - codeObjectFiles += AssemblyCommands.buildAssemblyCodeObjectFiles(kernelsToBuild, kernelWriterAssembly, outputPath, assembler, offloadBundler, compress) + visited = set() + for k in asmKernels: + base = kernelWriterAssembly.getKernelFileBase(k) + k.duplicate = True if base in visited else False + visited.add(base) + + numAsmKernels = len(asmKernels) + numKernels = len(asmKernels) + assert numKernels == numAsmKernels, "Only assembly kernels are supported in TensileLite" + asmIter = zip(asmKernels, itertools.repeat(kernelWriterAssembly), itertools.repeat(TensileInstructions())) + asmResults = Common.ParallelMap2(processKernelSource, asmIter, "Generating assembly kernels") + removeInvalidSolutionsAndKernels(asmResults, asmKernels, solutions, errorTolerant, globalParameters) + fn = functools.partial(writeAssembly, asmPath) + ret = Common.ParallelMap2(fn, asmResults, "Writing assembly kernels", return_as="list", multiArg=False) + for p, isa, wavefrontsize in ret: + asmToolchain.assemble(str(p), str(p.with_suffix(".o")), getGfxName(isa), wavefrontsize) + codeObjectFiles += buildAssemblyCodeObjectFiles(asmToolchain, asmKernels, kernelWriterAssembly, outputPath, compress) + + srcKernels = [k for k in kernels if k['KernelLanguage'] != 'Assembly'] + if srcKernels: + raise ValueError(f"Non-helper HIP source kernels are not supported Tensilelite, found {len(srcKernels)}") + srcIter = zip(srcKernels, itertools.repeat(kernelWriterAssembly), itertools.repeat(TensileInstructions())) + srcResults = Common.ParallelMap2(processKernelSource, srcIter, "Generating source kernels") + srcKernelFiles = buildKernelSourceAndHeaderFiles(srcResults, outputPath) + writeHelpers(outputPath, kernelHelperObjs, KERNEL_HELPER_FILENAME_CPP, KERNEL_HELPER_FILENAME_H) + codeObjectFiles += buildSourceCodeObjectFiles(srcToolchain, srcKernelFiles, outputPath) Common.popWorkingPath() # build_tmp Common.popWorkingPath() # workingDir @@ -481,7 +489,7 @@ def splitExtraParameters(par): argParser.add_argument("--cmake-cxx-compiler", dest="CmakeCxxCompiler", action="store") argParser.add_argument("--offload-bundler", dest="OffloadBundler", action="store", default=ToolchainDefaults.OFFLOAD_BUNDLER) argParser.add_argument("--assembler", dest="Assembler", action="store", default=ToolchainDefaults.ASSEMBLER) - argParser.add_argument("--code-object-version", dest="CodeObjectVersion", choices=["default", "V4", "V5"], action="store") + argParser.add_argument("--code-object-version", dest="CodeObjectVersion", choices=["4", "5"], action="store", default="4", type=str) argParser.add_argument("--architecture", dest="Architecture", type=str, action="store", default="all", help="Supported archs: " + " ".join(architectureMap.keys())) argParser.add_argument("--short-file-names", dest="ShortNames", action="store_true") argParser.add_argument("--no-short-file-names", dest="ShortNames", action="store_false") @@ -526,6 +534,7 @@ def splitExtraParameters(par): " Example: gfx942/Equality/* for building equality of gfx942 only") args = argParser.parse_args() + args.CodeObjectVersion = "4" if args.CodeObjectVersion == "default" else args.CodeObjectVersion logicPath = args.LogicPath outputPath = args.OutputPath @@ -534,6 +543,8 @@ def splitExtraParameters(par): assembler = args.Assembler libraryFormat = args.LibraryFormat useCompression = not args.NoCompress + coVersion = args.CodeObjectVersion + print2("OutputPath: %s" % outputPath) ensurePath(outputPath) outputPath = os.path.abspath(outputPath) @@ -579,12 +590,15 @@ def splitExtraParameters(par): print1(f"# C Compiler: {cCompiler} (version {getVersion(cCompiler)})") print1(f"# Assembler: {assembler} (version {getVersion(assembler)})") print1(f"# Offload Bundler: {offloadBundler} (version {getVersion(offloadBundler)})") - print1(f"# Code Object Version: {arguments['CodeObjectVersion']}") + print1(f"# Code Object Version: {coVersion}") print1(f"# Architecture(s): {arguments['Architecture']}") print1(f"# Library Format: {libraryFormat}") assignGlobalParameters(arguments, cxxCompiler) + asmToolchain = AssemblyToolchain(assembler, offloadBundler, globalParameters["BuildIdKind"], coVersion) + srcToolchain = SourceToolchain(cxxCompiler, offloadBundler, globalParameters["BuildIdKind"], globalParameters["AsanBuild"], globalParameters["SaveTemps"]) + if not os.path.exists(logicPath): printExit("LogicPath %s doesn't exist" % logicPath) @@ -634,9 +648,7 @@ def validLogicFile(p: Path): # Parse logicData, solutions, and masterLibraries from logic files solutions, masterLibraries, fullMasterLibrary = generateLogicDataAndSolutions(logicFiles, args, cxxCompiler) - kernels, kernelHelperObjs, _ = generateKernelObjectsFromSolutions(solutions) - # if any kernels are assembly, append every ISA supported kernelWriterAssembly, kernelMinNaming, _ = getSolutionAndKernelWriters(solutions, kernels, assembler) @@ -651,7 +663,7 @@ def validLogicFile(p: Path): outputPath ) # write solutions and kernels - codeObjectFiles, numKernels = writeSolutionsAndKernels(outputPath, cxxCompiler, assembler, offloadBundler, solutions, + codeObjectFiles, numKernels = writeSolutionsAndKernels(outputPath, asmToolchain, srcToolchain, solutions, kernels, kernelHelperObjs, kernelWriterAssembly, compress=useCompression) archs = [getGfxName(arch) for arch in globalParameters['SupportedISA'] \ diff --git a/tensilelite/Tensile/TensileInstructions/Base.py b/tensilelite/Tensile/TensileInstructions/Base.py index 1c755d570f..8a8215d6f4 100644 --- a/tensilelite/Tensile/TensileInstructions/Base.py +++ b/tensilelite/Tensile/TensileInstructions/Base.py @@ -190,13 +190,6 @@ def getSlcBitName(hasGLCModifier): return "slc" return "sc1" -def getCOVFromParam(versionString): - if versionString == "default" or versionString == "V4": - return 4 - elif versionString == "V5": - return 5 - printExit("Unknown CodeObjectVersion %s" % (versionString)) - def _removeIdent(isaDict) -> list: ids = [th.ident for th in threading.enumerate()] isaDict = [id for id in isaDict if id in ids] diff --git a/tensilelite/Tensile/TensileInstructions/Code.py b/tensilelite/Tensile/TensileInstructions/Code.py index 8cbd202753..85413ff05c 100644 --- a/tensilelite/Tensile/TensileInstructions/Code.py +++ b/tensilelite/Tensile/TensileInstructions/Code.py @@ -751,7 +751,7 @@ def __init__(self, name, kernArgsVersion, groupSegSize, flatWgSize, codeObjectVe self.kernArgsVersion = kernArgsVersion self.groupSegSize = groupSegSize self.flatWgSize = flatWgSize - self.codeObjectVersion = codeObjectVersion + self.codeObjectVersion = str(codeObjectVersion) self.totalVgprs = totalVgprs self.totalSgprs = totalSgprs self.offset = 0 @@ -770,9 +770,9 @@ def __str__(self): kStr += " KernArgsVersion: %d\n"%self.kernArgsVersion kStr += "amdhsa.version:\n" kStr += " - 1\n" - if self.codeObjectVersion == 4: + if self.codeObjectVersion == "4" or self.codeObjectVersion == "default": kStr += " - 1\n" - elif self.codeObjectVersion == 5: + elif self.codeObjectVersion == "5": kStr += " - 2\n" kStr += "amdhsa.kernels:\n" kStr += " - .name: %s\n" % self.name diff --git a/tensilelite/Tensile/TensileInstructions/Utils.py b/tensilelite/Tensile/TensileInstructions/Utils.py index d2740b225d..a5c35a8c36 100644 --- a/tensilelite/Tensile/TensileInstructions/Utils.py +++ b/tensilelite/Tensile/TensileInstructions/Utils.py @@ -20,7 +20,8 @@ # CTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ################################################################################ -from .Base import getGfxName, getCOVFromParam +import warnings +from .Base import getGfxName from .Code import Module from .Containers import HolderContainer, RegisterContainer, RegName from .DataType import DataType @@ -235,33 +236,3 @@ def replaceHolder(module, dst): return module -def getAsmCompileArgs(assemblerPath: str, codeObjectVersion: str, \ - isa: Tuple[int, int, int], wavefrontSize: int, \ - sourceFileName: str, objectFileName: str, *moreArgs, debug: bool=False): - launcher = shlex.split(os.environ.get('Tensile_ASM_COMPILER_LAUNCHER', '')) - rv = launcher + [assemblerPath, '-x', 'assembler', '-target', 'amdgcn-amd-amdhsa'] - - rv += ['-mcode-object-version=%s'% getCOVFromParam(codeObjectVersion)] - - rv += ['-mcpu=' + getGfxName(isa)] - - if wavefrontSize == 64: - rv += ['-mwavefrontsize64'] - else: - rv += ['-mno-wavefrontsize64'] - - rv += moreArgs - - if debug: - rv += ['-g',] - - rv += ['-c', '-o', objectFileName, sourceFileName] - return rv - -def getAsmLinkCodeObjectArgs(assemblerPath: str, objectFileNames: List[str], \ - coFileName: str, buildIdKind: str, *moreArgs): - rv = [assemblerPath, '-target', 'amdgcn-amd-amdhsa'] - rv += ["-Xlinker", "--build-id=%s"%(buildIdKind)] - rv += moreArgs - rv += ['-o', coFileName] + objectFileNames - return rv From d6808eca1cb3b252a9fdf23dacbb9d75e667d932 Mon Sep 17 00:00:00 2001 From: David Dixon Date: Wed, 8 Jan 2025 15:50:14 +0000 Subject: [PATCH 05/10] Improvements to build algorithm --- tensilelite/Tensile/TensileCreateLibrary.py | 178 +++++++++----------- tensilelite/Tensile/Toolchain/Source.py | 30 ++-- 2 files changed, 92 insertions(+), 116 deletions(-) diff --git a/tensilelite/Tensile/TensileCreateLibrary.py b/tensilelite/Tensile/TensileCreateLibrary.py index 070c26b930..2671f7f248 100644 --- a/tensilelite/Tensile/TensileCreateLibrary.py +++ b/tensilelite/Tensile/TensileCreateLibrary.py @@ -36,7 +36,7 @@ from . import LibraryIO from . import Utils from .Toolchain.Assembly import AssemblyToolchain, buildAssemblyCodeObjectFiles -from .Toolchain.Source import SourceToolchain, buildSourceCodeObjectFiles +from .Toolchain.Source import SourceToolchain, buildSourceCodeObjectFile from .Toolchain.Validators import validateToolchain, getVersion, ToolchainDefaults from .TensileInstructions import getGfxName, TensileInstructions from .Common import globalParameters, HR, print1, print2, printExit, ensurePath, \ @@ -83,7 +83,7 @@ class KernelCodeGenResult(NamedTuple): wavefrontSize: int -def processKernelSource(kernel, kernelWriterAssembly, ti) -> KernelCodeGenResult: +def processKernelSource(kernelWriterAssembly, ti, kernel) -> KernelCodeGenResult: """ Generate source for a single kernel. Returns (error, source, header, kernelName). @@ -100,71 +100,6 @@ def processKernelSource(kernel, kernelWriterAssembly, ti) -> KernelCodeGenResult return KernelCodeGenResult(err, src, header, asmFilename, objFilename, tuple(kernel["ISA"]), kernel["WavefrontSize"]) -def buildKernelSourceAndHeaderFiles(results, outputPath): - """ - Logs errors and writes appropriate info to kernelSourceFile and kernelHeaderFile. - - Arguments: - results: list of (err, src, header, kernelName, filename) - outputPath: path to source directory - kernelSourceFile: File to write source data to - kernelHeaderFile: File to write header data to - - Returns: - sourceFilenames: Array containing source kernel filenames - """ - - # Find kernels to write - kernelsToWrite = [] - filesToWrite = collections.defaultdict(list) - validKernelCount = 0 - for (err,src,header,kernelName, filename) in results: - - # Don't create a file for empty kernels - if len(src.strip()) == 0: - continue - - kernelsToWrite.append((err, src, header, kernelName)) - - # Create list of files - if filename: - filesToWrite[os.path.join(os.path.normcase(outputPath),filename)].append((err, src, header, kernelName)) - else: - kernelSuffix = "" - filesToWrite[os.path.join(os.path.normcase(outputPath), "Kernels"+kernelSuffix)]\ - .append((err, src, header, kernelName)) - - validKernelCount += 1 - - #Ensure there's at least one kernel file for helper kernels - if globalParameters["LazyLibraryLoading"] or not kernelsToWrite: - kernelSuffix = "" - filesToWrite[os.path.join(os.path.normcase(outputPath), "Kernels"+kernelSuffix)] = [] - - - # Write kernel data to files - #Parse list of files and write kernels - for filename, kernelList in filesToWrite.items(): - with open(filename+".h", "w", encoding="utf-8") as kernelHeaderFile, \ - open(filename+".cpp", "w", encoding="utf-8") as kernelSourceFile: - - kernelSourceFile.write(CHeader) - kernelHeaderFile.write(CHeader) - kernelSourceFile.write("#include \"{}.h\"\n".format(filename)) - kernelHeaderFile.write("#pragma once\n") - if globalParameters["RuntimeLanguage"] == "HIP": - kernelHeaderFile.write("#include \n") - kernelHeaderFile.write("#include \n\n") - kernelHeaderFile.write("#include \"KernelHeader.h\"\n\n") - - for err,src,header,kernelName in kernelList: - kernelSourceFile.write(src) - kernelHeaderFile.write(header) - - sourceFilenames = [filePrefix+".cpp" for filePrefix in filesToWrite] - - return sourceFilenames - def removeInvalidSolutionsAndKernels(results, kernels, solutions, errorTolerant, globalParameters): removeKernels = [] removeKernelNames = [] @@ -206,37 +141,42 @@ def writeAssembly(asmPath: Union[Path, str], result: KernelCodeGenResult): if result.err: printExit(f"Failed to build kernel {result.name} because it has error code {result.err}") path = Path(asmPath) / f"{result.name}.s" + isa = result.isa + wfsize = result.wavefrontSize with open(path, "w", encoding="utf-8") as f: f.write(result.src) - - return path, result.isa, result.wavefrontSize + del result # result.src is very large so let gc know to clean up asap + + return path, isa, wfsize def writeHelpers(outputPath, kernelHelperObjs, KERNEL_HELPER_FILENAME_CPP, KERNEL_HELPER_FILENAME_H): kernelSourceFilename = os.path.join(os.path.normcase(outputPath), KERNEL_HELPER_FILENAME_CPP) kernelHeaderFilename = os.path.join(os.path.normcase(outputPath), KERNEL_HELPER_FILENAME_H) - kernelSourceFile = open(kernelSourceFilename, "a", encoding="utf-8") - kernelHeaderFile = open(kernelHeaderFilename, "a", encoding="utf-8") - HeaderText = "" - # handle helper kernel function - for ko in kernelHelperObjs: - kernelName = ko.getKernelName() - - (err, src) = ko.getSourceFileString() - kernelSourceFile.write(src) - if err: - print("*** warning: invalid kernel#%u" % kernelName) - - HeaderText += ko.getHeaderFileString() - - # write kernel.h in one shot - kernelHeaderFile.write(HeaderText) - - if kernelSourceFile: - kernelSourceFile.close() - if kernelHeaderFile: - kernelHeaderFile.close() + with open(kernelHeaderFilename, "w", encoding="utf-8") as kernelHeaderFile, \ + open(kernelSourceFilename, "w", encoding="utf-8") as kernelSourceFile: + kernelSourceFile.write(CHeader) + kernelHeaderFile.write(CHeader) + kernelSourceFile.write("#include \"Kernels.h\"\n") + kernelHeaderFile.write("#pragma once\n") + if globalParameters["RuntimeLanguage"] == "HIP": + kernelHeaderFile.write("#include \n") + kernelHeaderFile.write("#include \n\n") + kernelHeaderFile.write("#include \"KernelHeader.h\"\n\n") + + HeaderText = "" + for ko in kernelHelperObjs: + kernelName = ko.getKernelName() + + (err, src) = ko.getSourceFileString() + kernelSourceFile.write(src) + if err: + print("*** warning: invalid kernel#%u" % kernelName) + + HeaderText += ko.getHeaderFileString() + + kernelHeaderFile.write(HeaderText) ################################################################################ @@ -261,10 +201,15 @@ def writeSolutionsAndKernels(outputPath, asmToolchain, srcToolchain, solutions, # Mark duplicate kernels to avoid race condition # @TODO improve organization so this problem doesn't appear visited = set() + duplicates = 0 for k in asmKernels: base = kernelWriterAssembly.getKernelFileBase(k) k.duplicate = True if base in visited else False + duplicates += k.duplicate + print2(f"Duplicate: {base}") visited.add(base) + + print1(f"Number of duplicates: {duplicates}") numAsmKernels = len(asmKernels) numKernels = len(asmKernels) @@ -281,11 +226,9 @@ def writeSolutionsAndKernels(outputPath, asmToolchain, srcToolchain, solutions, srcKernels = [k for k in kernels if k['KernelLanguage'] != 'Assembly'] if srcKernels: raise ValueError(f"Non-helper HIP source kernels are not supported Tensilelite, found {len(srcKernels)}") - srcIter = zip(srcKernels, itertools.repeat(kernelWriterAssembly), itertools.repeat(TensileInstructions())) - srcResults = Common.ParallelMap2(processKernelSource, srcIter, "Generating source kernels") - srcKernelFiles = buildKernelSourceAndHeaderFiles(srcResults, outputPath) writeHelpers(outputPath, kernelHelperObjs, KERNEL_HELPER_FILENAME_CPP, KERNEL_HELPER_FILENAME_H) - codeObjectFiles += buildSourceCodeObjectFiles(srcToolchain, srcKernelFiles, outputPath) + srcKernelFile = Path(outputPath) / "Kernels.cpp" + buildSourceCodeObjectFile(srcToolchain, outputPath, srcKernelFile) Common.popWorkingPath() # build_tmp Common.popWorkingPath() # workingDir @@ -293,6 +236,49 @@ def writeSolutionsAndKernels(outputPath, asmToolchain, srcToolchain, solutions, return codeObjectFiles, numKernels +def writeSolutionsAndKernelsTCL(outputPath, asmToolchain, srcToolchain, kernels, kernelHelperObjs, \ + kernelWriterAssembly, compress=True): + + Common.pushWorkingPath('build_tmp') + Common.pushWorkingPath(os.path.basename(outputPath).upper()) + asmPath = ensurePath(os.path.join(globalParameters["WorkingPath"], "assembly")) + + asmKernels = [k for k in kernels if k['KernelLanguage'] == 'Assembly'] + + visited = set() + duplicates = 0 + for k in asmKernels: + base = kernelWriterAssembly.getKernelFileBase(k) + k.duplicate = True if base in visited else False + duplicates += k.duplicate + print2(f"Duplicate: {base}") + visited.add(base) + + print1(f"Number of duplicates: {duplicates}") + + numAsmKernels = len(asmKernels) + numKernels = len(kernels) + assert numKernels == numAsmKernels, "Only assembly kernels are supported in TensileLite" + + def assemble(ret): + p, isa, wavefrontsize = ret + asmToolchain.assemble(str(p), str(p.with_suffix(".o")), getGfxName(isa), wavefrontsize) + unaryProcessKernelSource = functools.partial(processKernelSource, kernelWriterAssembly, TensileInstructions()) + unaryWriteAssembly = functools.partial(writeAssembly, asmPath) + compose = lambda *F: functools.reduce(lambda f, g: lambda x: f(g(x)), F) + ret = Common.ParallelMap2(compose(assemble, unaryWriteAssembly, unaryProcessKernelSource), asmKernels, "Generating assembly kernels", multiArg=False) + buildAssemblyCodeObjectFiles(asmToolchain, asmKernels, kernelWriterAssembly, outputPath, compress) + + writeHelpers(outputPath, kernelHelperObjs, KERNEL_HELPER_FILENAME_CPP, KERNEL_HELPER_FILENAME_H) + srcKernelFile = Path(outputPath) / "Kernels.cpp" + buildSourceCodeObjectFile(srcToolchain, outputPath, srcKernelFile) + + Common.popWorkingPath() # build_tmp + Common.popWorkingPath() # workingDir + + return numKernels + + ############################################################################## # Min Naming / Solution and Kernel Writers ############################################################################## @@ -663,8 +649,8 @@ def validLogicFile(p: Path): outputPath ) # write solutions and kernels - codeObjectFiles, numKernels = writeSolutionsAndKernels(outputPath, asmToolchain, srcToolchain, solutions, - kernels, kernelHelperObjs, kernelWriterAssembly, compress=useCompression) + numKernels = writeSolutionsAndKernelsTCL(outputPath, asmToolchain, srcToolchain, kernels, + kernelHelperObjs, kernelWriterAssembly, compress=useCompression) archs = [getGfxName(arch) for arch in globalParameters['SupportedISA'] \ if globalParameters["AsmCaps"][arch]["SupportedISA"]] diff --git a/tensilelite/Tensile/Toolchain/Source.py b/tensilelite/Tensile/Toolchain/Source.py index 7d3e32ae63..1e4c72459f 100644 --- a/tensilelite/Tensile/Toolchain/Source.py +++ b/tensilelite/Tensile/Toolchain/Source.py @@ -22,16 +22,17 @@ # ################################################################################ -import itertools import os import re import shlex import shutil import subprocess + from pathlib import Path -from typing import Iterable, List, Union +from timeit import default_timer as timer +from typing import List, Union -from ..Common import globalParameters, print2, ensurePath, ParallelMap2, splitArchs +from ..Common import globalParameters, print1, print2, ensurePath, splitArchs class SourceToolchain: def __init__(self, compiler: str, bundler: str, buildIdKind: str, asanBuild: bool=False, saveTemps: bool=False): @@ -163,7 +164,7 @@ def _computeSourceCodeObjectFilename(target: str, base: str, buildPath: Union[Pa return coPath -def _buildSourceCodeObjectFile(toolchain: SourceToolchain, outputPath: Union[Path, str], kernelPath: Union[Path, str]) -> List[str]: +def buildSourceCodeObjectFile(toolchain: SourceToolchain, outputPath: Union[Path, str], kernelPath: Union[Path, str]) -> List[str]: """Compiles a HIP source code file into a code object file. Args: @@ -175,6 +176,8 @@ def _buildSourceCodeObjectFile(toolchain: SourceToolchain, outputPath: Union[Pat Returns: List of paths to the created code objects. """ + start = timer() + buildPath = Path(ensurePath(os.path.join(globalParameters['WorkingPath'], 'code_object_tmp'))) destPath = Path(ensurePath(os.path.join(outputPath, 'library'))) kernelPath = Path(kernelPath) @@ -206,20 +209,7 @@ def _buildSourceCodeObjectFile(toolchain: SourceToolchain, outputPath: Union[Pat for src, dst in zip(coPathsRaw, coPaths): shutil.move(src, dst) - return coPaths - -def buildSourceCodeObjectFiles(toolchain: SourceToolchain, kernelFiles: List[Path], outputPath: Path) -> Iterable[str]: - """Compiles HIP source code files into code object files. - - Args: - cxxCompiler: The C++ compiler to use. - kernelFiles: List of paths to the kernel source files. - outputPath: The output directory path where code objects will be placed. - removeTemporaries: Whether to clean up temporary files. + stop = timer() + print1(f"buildSourceCodeObjectFile time (s): {(stop-start):3.2f}") - Returns: - List of paths to the created code objects. - """ - args = zip(itertools.repeat(toolchain), itertools.repeat(outputPath), kernelFiles) - coFiles = ParallelMap2(_buildSourceCodeObjectFile, args, "Compiling source kernels") - return itertools.chain.from_iterable(coFiles) + return coPaths From 4d4fbe404cece9ff0bdffc71e4f39a074a45ed86 Mon Sep 17 00:00:00 2001 From: David Dixon Date: Thu, 9 Jan 2025 18:52:39 +0000 Subject: [PATCH 06/10] Don't build duplicates --- tensilelite/Tensile/TensileCreateLibrary.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tensilelite/Tensile/TensileCreateLibrary.py b/tensilelite/Tensile/TensileCreateLibrary.py index 2671f7f248..a108ce37a6 100644 --- a/tensilelite/Tensile/TensileCreateLibrary.py +++ b/tensilelite/Tensile/TensileCreateLibrary.py @@ -256,6 +256,8 @@ def writeSolutionsAndKernelsTCL(outputPath, asmToolchain, srcToolchain, kernels, print1(f"Number of duplicates: {duplicates}") + uniqueAsmKernels = [k for k in asmKernels if not k.duplicate] + numAsmKernels = len(asmKernels) numKernels = len(kernels) assert numKernels == numAsmKernels, "Only assembly kernels are supported in TensileLite" @@ -266,8 +268,8 @@ def assemble(ret): unaryProcessKernelSource = functools.partial(processKernelSource, kernelWriterAssembly, TensileInstructions()) unaryWriteAssembly = functools.partial(writeAssembly, asmPath) compose = lambda *F: functools.reduce(lambda f, g: lambda x: f(g(x)), F) - ret = Common.ParallelMap2(compose(assemble, unaryWriteAssembly, unaryProcessKernelSource), asmKernels, "Generating assembly kernels", multiArg=False) - buildAssemblyCodeObjectFiles(asmToolchain, asmKernels, kernelWriterAssembly, outputPath, compress) + ret = Common.ParallelMap2(compose(assemble, unaryWriteAssembly, unaryProcessKernelSource), uniqueAsmKernels, "Generating assembly kernels", multiArg=False) + buildAssemblyCodeObjectFiles(asmToolchain, uniqueAsmKernels, kernelWriterAssembly, outputPath, compress) writeHelpers(outputPath, kernelHelperObjs, KERNEL_HELPER_FILENAME_CPP, KERNEL_HELPER_FILENAME_H) srcKernelFile = Path(outputPath) / "Kernels.cpp" From 0c31eaf4aa6d1897eae31e88a91ec2b7a93ffb0b Mon Sep 17 00:00:00 2001 From: David Dixon Date: Thu, 9 Jan 2025 19:05:40 +0000 Subject: [PATCH 07/10] Don't use unique kernel list when building co files --- tensilelite/Tensile/TensileCreateLibrary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensilelite/Tensile/TensileCreateLibrary.py b/tensilelite/Tensile/TensileCreateLibrary.py index a108ce37a6..69b4a1b3c1 100644 --- a/tensilelite/Tensile/TensileCreateLibrary.py +++ b/tensilelite/Tensile/TensileCreateLibrary.py @@ -269,7 +269,7 @@ def assemble(ret): unaryWriteAssembly = functools.partial(writeAssembly, asmPath) compose = lambda *F: functools.reduce(lambda f, g: lambda x: f(g(x)), F) ret = Common.ParallelMap2(compose(assemble, unaryWriteAssembly, unaryProcessKernelSource), uniqueAsmKernels, "Generating assembly kernels", multiArg=False) - buildAssemblyCodeObjectFiles(asmToolchain, uniqueAsmKernels, kernelWriterAssembly, outputPath, compress) + buildAssemblyCodeObjectFiles(asmToolchain, asmKernels, kernelWriterAssembly, outputPath, compress) writeHelpers(outputPath, kernelHelperObjs, KERNEL_HELPER_FILENAME_CPP, KERNEL_HELPER_FILENAME_H) srcKernelFile = Path(outputPath) / "Kernels.cpp" From 8c43660541e50bb86520b6f76ce6e2f3f748b6e9 Mon Sep 17 00:00:00 2001 From: David Dixon Date: Fri, 10 Jan 2025 03:41:41 +0000 Subject: [PATCH 08/10] Copyright --- tensilelite/Tensile/BenchmarkProblems.py | 2 +- tensilelite/Tensile/Common.py | 2 +- tensilelite/Tensile/Components/Signature.py | 2 +- tensilelite/Tensile/KernelWriter.py | 2 +- tensilelite/Tensile/KernelWriterAssembly.py | 2 +- tensilelite/Tensile/KernelWriterBase.py | 2 +- tensilelite/Tensile/Ops/AMaxGenerator.py | 2 +- tensilelite/Tensile/Ops/LayerNormGenerator.py | 2 +- tensilelite/Tensile/Ops/SoftmaxGenerator.py | 2 +- tensilelite/Tensile/Parallel.py | 2 +- tensilelite/Tensile/SolutionStructs.py | 2 +- tensilelite/Tensile/TensileCreateLibrary.py | 9 ++++---- .../Tensile/TensileInstructions/Base.py | 2 +- .../Tensile/TensileInstructions/Code.py | 2 +- .../Tensile/TensileInstructions/Utils.py | 2 +- tensilelite/Tensile/Toolchain/Assembly.py | 2 +- tensilelite/Tensile/Toolchain/Source.py | 2 +- tensilelite/Tensile/Toolchain/Validators.py | 2 +- tensilelite/Tensile/Toolchain/__init__.py | 23 +++++++++++++++++++ 19 files changed, 45 insertions(+), 21 deletions(-) diff --git a/tensilelite/Tensile/BenchmarkProblems.py b/tensilelite/Tensile/BenchmarkProblems.py index 29b49ec915..24c89e23d0 100644 --- a/tensilelite/Tensile/BenchmarkProblems.py +++ b/tensilelite/Tensile/BenchmarkProblems.py @@ -1,6 +1,6 @@ ################################################################################ # -# Copyright (C) 2022-2024 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (C) 2022-2025 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 diff --git a/tensilelite/Tensile/Common.py b/tensilelite/Tensile/Common.py index 8325ba2d93..115eeffd8f 100644 --- a/tensilelite/Tensile/Common.py +++ b/tensilelite/Tensile/Common.py @@ -1,6 +1,6 @@ ################################################################################ # -# Copyright (C) 2022-2024 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (C) 2022-2025 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 diff --git a/tensilelite/Tensile/Components/Signature.py b/tensilelite/Tensile/Components/Signature.py index 9c1b8c4b2b..89dc4d30d7 100644 --- a/tensilelite/Tensile/Components/Signature.py +++ b/tensilelite/Tensile/Components/Signature.py @@ -1,6 +1,6 @@ ################################################################################ # -# Copyright (C) 2022-2024 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (C) 2022-2025 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 diff --git a/tensilelite/Tensile/KernelWriter.py b/tensilelite/Tensile/KernelWriter.py index 3ac5e61da7..378d47cc36 100644 --- a/tensilelite/Tensile/KernelWriter.py +++ b/tensilelite/Tensile/KernelWriter.py @@ -1,6 +1,6 @@ ################################################################################ # -# Copyright (C) 2022-2024 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (C) 2022-2025 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 diff --git a/tensilelite/Tensile/KernelWriterAssembly.py b/tensilelite/Tensile/KernelWriterAssembly.py index 538b7849df..4f9188de54 100644 --- a/tensilelite/Tensile/KernelWriterAssembly.py +++ b/tensilelite/Tensile/KernelWriterAssembly.py @@ -1,6 +1,6 @@ ################################################################################ # -# Copyright (C) 2022-2024 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (C) 2022-2025 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 diff --git a/tensilelite/Tensile/KernelWriterBase.py b/tensilelite/Tensile/KernelWriterBase.py index 85e3fdca0b..bcff0a688d 100644 --- a/tensilelite/Tensile/KernelWriterBase.py +++ b/tensilelite/Tensile/KernelWriterBase.py @@ -1,6 +1,6 @@ ################################################################################ # -# Copyright (C) 2022 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (C) 2022-2025 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 diff --git a/tensilelite/Tensile/Ops/AMaxGenerator.py b/tensilelite/Tensile/Ops/AMaxGenerator.py index d118293820..a579db0f3d 100644 --- a/tensilelite/Tensile/Ops/AMaxGenerator.py +++ b/tensilelite/Tensile/Ops/AMaxGenerator.py @@ -1,6 +1,6 @@ ################################################################################ # -# Copyright (C) 2023-2024 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (C) 2023-2025 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 diff --git a/tensilelite/Tensile/Ops/LayerNormGenerator.py b/tensilelite/Tensile/Ops/LayerNormGenerator.py index e452b797f0..093a437544 100644 --- a/tensilelite/Tensile/Ops/LayerNormGenerator.py +++ b/tensilelite/Tensile/Ops/LayerNormGenerator.py @@ -1,6 +1,6 @@ ################################################################################ # -# Copyright (C) 2023-2024 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (C) 2023-2025 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 diff --git a/tensilelite/Tensile/Ops/SoftmaxGenerator.py b/tensilelite/Tensile/Ops/SoftmaxGenerator.py index 077487673f..1078615d84 100644 --- a/tensilelite/Tensile/Ops/SoftmaxGenerator.py +++ b/tensilelite/Tensile/Ops/SoftmaxGenerator.py @@ -1,6 +1,6 @@ ################################################################################ # -# Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (C) 2023-2025 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 diff --git a/tensilelite/Tensile/Parallel.py b/tensilelite/Tensile/Parallel.py index 2bdd45f45e..99950aba04 100644 --- a/tensilelite/Tensile/Parallel.py +++ b/tensilelite/Tensile/Parallel.py @@ -1,6 +1,6 @@ ################################################################################ # -# Copyright (C) 2016-2024 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (C) 2016-2025 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 diff --git a/tensilelite/Tensile/SolutionStructs.py b/tensilelite/Tensile/SolutionStructs.py index d66a2a5b2d..d92a8248dd 100644 --- a/tensilelite/Tensile/SolutionStructs.py +++ b/tensilelite/Tensile/SolutionStructs.py @@ -1,6 +1,6 @@ ################################################################################ # -# Copyright (C) 2022-2024 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (C) 2022-2025 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 diff --git a/tensilelite/Tensile/TensileCreateLibrary.py b/tensilelite/Tensile/TensileCreateLibrary.py index 69b4a1b3c1..585fa55ccc 100644 --- a/tensilelite/Tensile/TensileCreateLibrary.py +++ b/tensilelite/Tensile/TensileCreateLibrary.py @@ -214,13 +214,14 @@ def writeSolutionsAndKernels(outputPath, asmToolchain, srcToolchain, solutions, numAsmKernels = len(asmKernels) numKernels = len(asmKernels) assert numKernels == numAsmKernels, "Only assembly kernels are supported in TensileLite" - asmIter = zip(asmKernels, itertools.repeat(kernelWriterAssembly), itertools.repeat(TensileInstructions())) + asmIter = zip(itertools.repeat(kernelWriterAssembly), itertools.repeat(TensileInstructions()), asmKernels) asmResults = Common.ParallelMap2(processKernelSource, asmIter, "Generating assembly kernels") removeInvalidSolutionsAndKernels(asmResults, asmKernels, solutions, errorTolerant, globalParameters) - fn = functools.partial(writeAssembly, asmPath) - ret = Common.ParallelMap2(fn, asmResults, "Writing assembly kernels", return_as="list", multiArg=False) - for p, isa, wavefrontsize in ret: + def assemble(ret): + p, isa, wavefrontsize = ret asmToolchain.assemble(str(p), str(p.with_suffix(".o")), getGfxName(isa), wavefrontsize) + unaryWriteAssembly = functools.partial(writeAssembly, asmPath) + ret = Common.ParallelMap2(compose(assemble, unaryWriteAssembly), asmResults, "Writing assembly kernels", return_as="list", multiArg=False) codeObjectFiles += buildAssemblyCodeObjectFiles(asmToolchain, asmKernels, kernelWriterAssembly, outputPath, compress) srcKernels = [k for k in kernels if k['KernelLanguage'] != 'Assembly'] diff --git a/tensilelite/Tensile/TensileInstructions/Base.py b/tensilelite/Tensile/TensileInstructions/Base.py index 8a8215d6f4..231d6c54c1 100644 --- a/tensilelite/Tensile/TensileInstructions/Base.py +++ b/tensilelite/Tensile/TensileInstructions/Base.py @@ -1,6 +1,6 @@ ################################################################################ # -# Copyright (C) 2022-2024 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (C) 2022-2025 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 diff --git a/tensilelite/Tensile/TensileInstructions/Code.py b/tensilelite/Tensile/TensileInstructions/Code.py index 85413ff05c..808bc810d0 100644 --- a/tensilelite/Tensile/TensileInstructions/Code.py +++ b/tensilelite/Tensile/TensileInstructions/Code.py @@ -1,6 +1,6 @@ ################################################################################ # -# Copyright (C) 2022-2024 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (C) 2022-2025 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 diff --git a/tensilelite/Tensile/TensileInstructions/Utils.py b/tensilelite/Tensile/TensileInstructions/Utils.py index a5c35a8c36..ae3620bd3d 100644 --- a/tensilelite/Tensile/TensileInstructions/Utils.py +++ b/tensilelite/Tensile/TensileInstructions/Utils.py @@ -1,6 +1,6 @@ ################################################################################ # -# Copyright (C) 2022-2024 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (C) 2022-2025 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 diff --git a/tensilelite/Tensile/Toolchain/Assembly.py b/tensilelite/Tensile/Toolchain/Assembly.py index d21db27ce4..0b44f36dff 100644 --- a/tensilelite/Tensile/Toolchain/Assembly.py +++ b/tensilelite/Tensile/Toolchain/Assembly.py @@ -1,6 +1,6 @@ ################################################################################ # -# Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (C) 2025 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 diff --git a/tensilelite/Tensile/Toolchain/Source.py b/tensilelite/Tensile/Toolchain/Source.py index 1e4c72459f..ab0eba5220 100644 --- a/tensilelite/Tensile/Toolchain/Source.py +++ b/tensilelite/Tensile/Toolchain/Source.py @@ -1,6 +1,6 @@ ################################################################################ # -# Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (C) 2025 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 diff --git a/tensilelite/Tensile/Toolchain/Validators.py b/tensilelite/Tensile/Toolchain/Validators.py index a19440e054..86c3ff074b 100644 --- a/tensilelite/Tensile/Toolchain/Validators.py +++ b/tensilelite/Tensile/Toolchain/Validators.py @@ -1,6 +1,6 @@ ################################################################################ # -# Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (C) 2025 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 diff --git a/tensilelite/Tensile/Toolchain/__init__.py b/tensilelite/Tensile/Toolchain/__init__.py index e69de29bb2..5bd724068a 100644 --- a/tensilelite/Tensile/Toolchain/__init__.py +++ b/tensilelite/Tensile/Toolchain/__init__.py @@ -0,0 +1,23 @@ +################################################################################ +# +# Copyright (C) 2025 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 965815c33b1020402fbd5653c6debff71d90d3d0 Mon Sep 17 00:00:00 2001 From: David Dixon Date: Fri, 10 Jan 2025 03:43:53 +0000 Subject: [PATCH 09/10] Copyright --- tensilelite/Tensile/Tensile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensilelite/Tensile/Tensile.py b/tensilelite/Tensile/Tensile.py index 7a4d36319b..3b35ba8b06 100644 --- a/tensilelite/Tensile/Tensile.py +++ b/tensilelite/Tensile/Tensile.py @@ -1,6 +1,6 @@ ################################################################################ # -# Copyright (C) 2022-2024 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (C) 2022-2025 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 From 75044858b450a97c2050d3e3d3287b801b362bde Mon Sep 17 00:00:00 2001 From: David Dixon Date: Fri, 10 Jan 2025 13:47:04 +0000 Subject: [PATCH 10/10] Add missing compose function --- tensilelite/Tensile/TensileCreateLibrary.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tensilelite/Tensile/TensileCreateLibrary.py b/tensilelite/Tensile/TensileCreateLibrary.py index 585fa55ccc..33fe838b86 100644 --- a/tensilelite/Tensile/TensileCreateLibrary.py +++ b/tensilelite/Tensile/TensileCreateLibrary.py @@ -221,6 +221,7 @@ def assemble(ret): p, isa, wavefrontsize = ret asmToolchain.assemble(str(p), str(p.with_suffix(".o")), getGfxName(isa), wavefrontsize) unaryWriteAssembly = functools.partial(writeAssembly, asmPath) + compose = lambda *F: functools.reduce(lambda f, g: lambda x: f(g(x)), F) ret = Common.ParallelMap2(compose(assemble, unaryWriteAssembly), asmResults, "Writing assembly kernels", return_as="list", multiArg=False) codeObjectFiles += buildAssemblyCodeObjectFiles(asmToolchain, asmKernels, kernelWriterAssembly, outputPath, compress)