Repository navigation
fix(rocke): never pass a compiler option the loaded compiler does not know #14218
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
base: develop
Are you sure you want to change the base?
Changes from all commits
1fefb99
6d16d3b
52a872c
5281e4d
4015706
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| # Copyright (c) Advanced Micro Devices, Inc., or its affiliates. | ||
| # SPDX-License-Identifier: MIT | ||
|
|
||
| """The shared skip policy for library tests that compile a kernel through COMGR. | ||
|
|
||
| Such a test depends on the host: COMGR and the code-object tools must be | ||
| installed, and the loaded compiler must accept every ``-mllvm`` option the | ||
| kernel needs. A test that uses this policy skips when one of those is missing, | ||
| because that is a fact about the host, not a defect in the kernel. A run that | ||
| sets ``ROCKE_REQUIRE_COMPILER_TOOLS=1`` (the CI lane that supplies the tools) | ||
| fails instead: a gate that skips everywhere looks green and checks nothing. | ||
|
|
||
| * :func:`require_tools_or_skip`: skip (or fail under the variable) for a | ||
| stated reason. | ||
| * :func:`skip_where_compiler_refuses_option`: apply that policy when the body | ||
| raises ``CompilerOptionNotAccepted``. | ||
|
|
||
| A skip is raised as ``unittest.SkipTest``, which pytest test functions and a | ||
| ``unittest`` ``setUpClass`` both report as a skip. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import os | ||
| import unittest | ||
| from collections.abc import Iterator | ||
| from contextlib import contextmanager | ||
| from typing import NoReturn | ||
|
|
||
| from rocke.runtime import CompilerOptionNotAccepted | ||
|
|
||
| __all__ = [ | ||
| "REQUIRE_COMPILER_TOOLS_ENV", | ||
| "require_tools_or_skip", | ||
| "skip_where_compiler_refuses_option", | ||
| ] | ||
|
|
||
| # platform/CMakeLists.txt sets it to "1" only for the installed ctest entries | ||
| # whose tests consume it, where ROCM_PATH supplies the tools (Linux); see | ||
| # TESTING.md section 5.1.4. | ||
| REQUIRE_COMPILER_TOOLS_ENV = "ROCKE_REQUIRE_COMPILER_TOOLS" | ||
|
|
||
|
|
||
| def require_tools_or_skip(reason: str) -> NoReturn: | ||
| """Skip the running test for ``reason``, or fail it with ``AssertionError`` | ||
| when ``ROCKE_REQUIRE_COMPILER_TOOLS=1`` says the tools must be there.""" | ||
| if os.environ.get(REQUIRE_COMPILER_TOOLS_ENV) == "1": | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [HIGH] §D.1 ★ ★ Knobs/features on the dnn-providers/hip-kernel-provider/rocke/library/tests/_compiler_test_utils.py:47 reads the environment Fix: Put the knob on the KernelSpec and delete the environment read. |
||
| raise AssertionError(f"{REQUIRE_COMPILER_TOOLS_ENV}=1 but {reason}") | ||
| raise unittest.SkipTest(reason) | ||
|
|
||
|
|
||
| @contextmanager | ||
| def skip_where_compiler_refuses_option() -> Iterator[None]: | ||
| """Apply :func:`require_tools_or_skip` if the body raises | ||
| ``CompilerOptionNotAccepted``. | ||
|
|
||
| That error means the loaded compiler does not accept a ``-mllvm`` option | ||
| the kernel needs (see ``rocke.runtime.compiler_options``). Every other | ||
| error propagates unchanged. | ||
| """ | ||
| try: | ||
| yield | ||
| except CompilerOptionNotAccepted as exc: | ||
| require_tools_or_skip(f"loaded compiler cannot build this kernel: {exc}") | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| # Copyright (c) Advanced Micro Devices, Inc., or its affiliates. | ||
| # SPDX-License-Identifier: MIT | ||
|
|
||
| """The skip policy every compile test shares (``_compiler_test_utils``). | ||
|
|
||
| CPU only. Each error is a real one from the committed option table: a | ||
| compiler whose LLVM major the table records as refusing the option | ||
| (``CompilerOptionNotAccepted``), or one the table has no answer for (a plain | ||
| ``ValueError``). These tests pin that a refusal skips, that it fails when | ||
| ``ROCKE_REQUIRE_COMPILER_TOOLS=1`` requires the tools, and that no other error | ||
| is turned into a skip. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import unittest | ||
|
|
||
| import pytest | ||
| from rocke.runtime.comgr import CompilerInfo | ||
| from rocke.runtime.compiler_options import measured_answers, require_accepted | ||
|
|
||
| from ._compiler_test_utils import ( | ||
| REQUIRE_COMPILER_TOOLS_ENV, | ||
| skip_where_compiler_refuses_option, | ||
| ) | ||
|
|
||
| OPTION = "-amdgpu-mfma-vgpr-form" | ||
| # How the refusal starts, and how the policy introduces it. | ||
| REFUSAL = f"kernel 'probe' needs the compiler option '-mllvm {OPTION}'" | ||
| SKIP_REASON = f"loaded compiler cannot build this kernel: {REFUSAL}" | ||
|
|
||
|
|
||
| def _compiler(llvm_major: int) -> CompilerInfo: | ||
| """A compiler identity as ``loaded_compiler_info`` reports one.""" | ||
| return CompilerInfo( | ||
| llvm_version=(llvm_major, 0, 0), | ||
| source="LLVMGetVersion", | ||
| requested_comgr="libamd_comgr.so", | ||
| comgr_path=None, | ||
| query_library_path=None, | ||
| ) | ||
|
|
||
|
|
||
| def _compile_on(llvm_major: int) -> None: | ||
| """Ask the committed table whether LLVM ``llvm_major`` accepts ``OPTION``, | ||
| as ``compile_kernel`` does before compiling a kernel that needs it.""" | ||
| require_accepted( | ||
| ["-O3", "-mllvm", OPTION], | ||
| compiler=_compiler(llvm_major), | ||
| needed_by="kernel 'probe'", | ||
| ) | ||
|
|
||
|
|
||
| def _refusing_major() -> int: | ||
| """An LLVM major the table records as refusing ``OPTION``.""" | ||
| return next( | ||
| major | ||
| for major, answer in measured_answers()[OPTION].items() | ||
| if not answer.accepted | ||
| ) | ||
|
|
||
|
|
||
| def _unmeasured_major() -> int: | ||
| """An LLVM major newer than every one the table has an answer for.""" | ||
| return 1 + max(measured_answers()[OPTION]) | ||
|
|
||
|
|
||
| def test_refused_option_skips(monkeypatch): | ||
| monkeypatch.delenv(REQUIRE_COMPILER_TOOLS_ENV, raising=False) | ||
| with ( | ||
| pytest.raises(unittest.SkipTest, match=f"^{SKIP_REASON}"), | ||
| skip_where_compiler_refuses_option(), | ||
| ): | ||
| _compile_on(_refusing_major()) | ||
|
|
||
|
|
||
| def test_refused_option_fails_where_tools_are_required(monkeypatch): | ||
| monkeypatch.setenv(REQUIRE_COMPILER_TOOLS_ENV, "1") | ||
| required = f"^{REQUIRE_COMPILER_TOOLS_ENV}=1 but {SKIP_REASON}" | ||
| with ( | ||
| pytest.raises(AssertionError, match=required), | ||
| skip_where_compiler_refuses_option(), | ||
| ): | ||
| _compile_on(_refusing_major()) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("required", ["0", "1"]) | ||
| def test_other_errors_propagate(monkeypatch, required): | ||
| # A compiler the table has no answer for raises a plain ValueError (the | ||
| # base class of the refusal): that is a missing measurement, not a skip. | ||
| monkeypatch.setenv(REQUIRE_COMPILER_TOOLS_ENV, required) | ||
| with ( | ||
| pytest.raises( | ||
| ValueError, match="which has not been measured on LLVM" | ||
| ) as caught, | ||
| skip_where_compiler_refuses_option(), | ||
| ): | ||
| _compile_on(_unmeasured_major()) | ||
| assert type(caught.value) is ValueError |
Uh oh!
There was an error while loading. Please reload this page.