Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Find non-default HIP SDK on Windows #2080

Merged
merged 7 commits into from
Jan 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Tensile/BuildCommands/AssemblyCommands.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ def buildAssemblyCodeObjectFiles(
maxLineLength = (
8191 if os.name == "nt" else int(subprocess.check_output(["getconf", "ARG_MAX"]).strip())
)
tPrint(0, f"Maximum command line length: {maxLineLength}")

assemblyKernels = [k for k in kernels if k["KernelLanguage"] == "Assembly"]
if len(assemblyKernels) == 0:
Expand Down
2 changes: 1 addition & 1 deletion Tensile/Common.py
Original file line number Diff line number Diff line change
Expand Up @@ -2424,7 +2424,7 @@ def assignGlobalParameters( config, capabilitiesCache: Optional[dict] = None ):
if "TENSILE_ROCM_PATH" in os.environ:
globalParameters["ROCmPath"] = os.environ.get("TENSILE_ROCM_PATH")
if os.name == "nt" and "HIP_DIR" in os.environ:
globalParameters["ROCmPath"] = os.environ.get("HIP_DIR") # windows has no ROCM
globalParameters["ROCmPath"] = os.environ.get("HIP_PATH") # windows has no ROCM
globalParameters["CmakeCxxCompiler"] = None
if "CMAKE_CXX_COMPILER" in os.environ:
globalParameters["CmakeCxxCompiler"] = os.environ.get("CMAKE_CXX_COMPILER")
Expand Down
3 changes: 2 additions & 1 deletion Tensile/TensileCreateLib/ParseArguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ def parseArguments(input: Optional[List[str]] = None) -> Dict[str, Any]:
default=ToolchainDefaults.CXX_COMPILER,
type=str,
help="C++ compiler used when generating binaries."
" On linux, amdclang++ (default) or hipcc. On Windows clang++ (default) or hipcc.",
" On linux, amdclang++ (default) or hipcc. On Windows clang++.exe (default) or hipcc. "
"If clang++.exe is specified, it must include the extension `.exe`.",
bstefanuk marked this conversation as resolved.
Show resolved Hide resolved
)
parser.add_argument(
"--c-compiler",
Expand Down
151 changes: 100 additions & 51 deletions Tensile/Utilities/Toolchain.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,91 @@
import os
import re
from pathlib import Path
from subprocess import PIPE, run
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")

DEFAULT_ROCM_BIN_PATH_POSIX = Path("/opt/rocm/bin")
DEFAULT_ROCM_LLVM_BIN_PATH_POSIX = Path("/opt/rocm/lib/llvm/bin")
DEFAULT_ROCM_BIN_PATH_WINDOWS = Path("C:/Program Files/AMD/ROCm")


osSelect = lambda linux, windows: linux if os.name != "nt" else windows


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"


def _windowsSearchPaths() -> List[Path]:
defaultPath = DEFAULT_ROCM_BIN_PATH_WINDOWS
searchPaths = []

if os.environ.get("HIP_PATH"):
hipPaths = [Path(p) / "bin" for p in os.environ["HIP_PATH"].split(os.pathsep)]
searchPaths.extend(hipPaths)

if Path(defaultPath).exists():
searchPaths.append(_windowsLatestRocmBin(defaultPath))

if os.environ.get("PATH"):
envPath = [Path(p) for p in os.environ["PATH"].split(os.pathsep)]
searchPaths.extend(envPath)

return searchPaths


def _posixSearchPaths() -> List[Path]:

searchPaths = []

if os.environ.get("ROCM_PATH"):
for p in os.environ["ROCM_PATH"].split(os.pathsep):
searchPaths.append(Path(p) / "bin")
searchPaths.append(Path(p) / "lib" / "llvm" / "bin")

searchPaths.extend(
[
DEFAULT_ROCM_BIN_PATH_POSIX,
DEFAULT_ROCM_LLVM_BIN_PATH_POSIX,
]
)

if os.environ.get("PATH"):
envPath = [Path(p) for p in os.environ["PATH"].split(os.pathsep)]
searchPaths.extend(envPath)

return searchPaths


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")
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")
DEVICE_ENUMERATOR= osSelect(linux="rocm_agent_enumerator", windows="hipinfo.exe")
DEVICE_ENUMERATOR = osSelect(linux="rocm_agent_enumerator", windows="hipinfo.exe")


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])
isSupported = any([component == t for t in targets]) or any(
[Path(component).name == t for t in targets]
)
return isSupported


Expand Down Expand Up @@ -119,7 +160,7 @@ def _exeExists(file: Path) -> bool:
"""
if os.access(file, os.X_OK):
if "rocm" not in map(str.lower, file.parts):
warn(f"Found non-ROCm install of `{file.name}`: {file}")
warn(f"Found `{file.name}` but in a non-default ROCm location: {file}")
return True
return False

Expand All @@ -134,48 +175,56 @@ def _validateExecutable(file: str, searchPaths: List[Path]) -> str:
Returns:
The validated executable with an absolute path.
"""
if not any((
supportedCxxCompiler(file),
supportedCCompiler(file),
supportedOffloadBundler(file),
supportedHip(file),
supportedDeviceEnumerator(file)
)):
if not any(
(
supportedCxxCompiler(file),
supportedCCompiler(file),
supportedOffloadBundler(file),
supportedHip(file),
supportedDeviceEnumerator(file),
)
):
raise ValueError(f"{file} is not a supported toolchain component for OS: {os.name}")

if _exeExists(Path(file)): return file
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))}")
path /= file
if _exeExists(path):
return str(path)
raise FileNotFoundError(
f"`{file}` either not found or not executable in any search path: "
f"{':'.join(map(str, searchPaths))}\n"
"Please refer to the troubleshooting section of the Tensile documentation at "
"https://rocm.docs.amd.com/projects/Tensile/en/latest/src/#"
)


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)]
searchPaths = _windowsSearchPaths() if os.name == "nt" else _posixSearchPaths()

out = (_validateExecutable(x, searchPaths) for x in args)
return next(out) if len(args) == 1 else tuple(out)
return next(out) if len(args) == 1 else tuple(out)


def getVersion(executable: str, versionFlag: str="--version", regex: str=r'version\s+([\d.]+)') -> str:
def getVersion(
executable: str, versionFlag: str = "--version", regex: str = r"version\s+([\d.]+)"
) -> str:
"""Print the version of a toolchain component.

Args:
Expand Down
4 changes: 4 additions & 0 deletions docs/sphinx/_toc.yml.in
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ subtrees:
- file: src/how-to/programmers-guide
- file: src/how-to/contribution-guidelines

- caption: Support
entries:
- file: src/support/troubleshooting

- caption: About
entries:
- file: license.rst
2 changes: 2 additions & 0 deletions docs/src/install/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
Installation
********************************************************************

.. _install-rocm:

Install ROCm
============

Expand Down
38 changes: 38 additions & 0 deletions docs/src/support/troubleshooting.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.. meta::
:description: Tensile is a tool for creating a benchmark-driven backend library for GEMM
:keywords: Tensile developers guide, Tensile contributors guide, Tensile programmers guide, GEMM, Tensor
.. highlight:: none

.. _troubleshooting:

********************************************************************
Troubleshooting
********************************************************************

This topic provides information for programmers and users on how to resolve common issues in Tensile.

============================
Missing toolchain components
============================

.. code-block::

FileNotFoundError: `amdclang++` either not found or not executable in any search path

In this situation, Tensile cannot locate one or more binaries required for proper program execution. This includes compilers, assemblers, linkers, and bundlers.

.. note::

On Linux, the default installation location is */opt/rocm*.

.. note::

On Windows, the default installation location is *C:\\Program Files\\AMD\\ROCm\\X.Y*, where *X.Y* identifies the major and minor version of the current ROCm installation.
When the HIP SDK is installed on Windows, the variable ``HIP_PATH`` is set to the installation location of the HIP SDK.
bstefanuk marked this conversation as resolved.
Show resolved Hide resolved

There are two possible reasons for this:

1. ROCm is not installed on the system. To resolve this issue, install ROCm by following the instructions at :ref:`install-rocm`.
2. ROCm is installed, but in a non-default location and the binaries cannot be found in the system PATH.
In this case, add the installation location to the ROCM_PATH on Linux, HIP_PATH on Windows, or the system PATH on either.
For example, on Linux use ``export ROCM_PATH=<path_to_rocm>/bin`` or on Windows PowerShell use ``$env:HIP_PATH = "<path_to_rocm>\bin"``.
2 changes: 2 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ commands =
{toxinidir}/Tensile/TensileCreateLibrary.py \
{toxinidir}/Tensile/TensileCreateLib/ \
{toxinidir}/Tensile/BuildCommands/ \
{toxinidir}/Tensile/Utilities/Toolchain.py \
{toxinidir}/Tensile/Tests/unit/test_TensileCreateLibrary.py \
{toxinidir}/Tensile/Tests/unit/test_KernelFileContext.py \
{toxinidir}/Tensile/Tests/unit/test_AsmRegisterPool.py \
Expand All @@ -88,6 +89,7 @@ commands =
{toxinidir}/Tensile/TensileCreateLibrary.py \
{toxinidir}/Tensile/TensileCreateLib/ \
{toxinidir}/Tensile/BuildCommands/ \
{toxinidir}/Tensile/Utilities/Toolchain.py \
{toxinidir}/Tensile/Tests/unit/test_TensileCreateLibrary.py \
{toxinidir}/Tensile/Tests/unit/test_KernelFileContext.py \
{toxinidir}/Tensile/Tests/unit/test_AsmRegisterPool.py \
Expand Down