Skip to content
Open
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
3 changes: 1 addition & 2 deletions cuda_core/cuda/core/_linker.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@ class LinkerOptions:

Since the linker may choose to use nvJitLink or the driver APIs as the linking backend,
not all options are applicable. When the system's installed nvJitLink is too old (<12.3),
not installed, or older than the CUDA driver major version, the driver APIs (cuLink)
will be used instead.
or not installed, the driver APIs (cuLink) will be used instead.

Attributes
----------
Expand Down
26 changes: 7 additions & 19 deletions cuda_core/cuda/core/_linker.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ from cuda.core._utils.cuda_utils import (
driver,
is_sequence,
)
from cuda.core._utils.version import driver_version
from cuda.core.typing import CompilerBackendType, ObjectCodeFormatType

if TYPE_CHECKING:
Expand Down Expand Up @@ -218,8 +217,7 @@ class LinkerOptions:

Since the linker may choose to use nvJitLink or the driver APIs as the linking backend,
not all options are applicable. When the system's installed nvJitLink is too old (<12.3),
not installed, or older than the CUDA driver major version, the driver APIs (cuLink)
will be used instead.
or not installed, the driver APIs (cuLink) will be used instead.

Attributes
----------
Expand Down Expand Up @@ -696,22 +694,12 @@ def _decide_nvjitlink_or_driver() -> bool:
from cuda.bindings._internal import nvjitlink

if _nvjitlink_has_version_symbol(nvjitlink):
nvjitlink_version = nvjitlink_module.version()
driver_major = driver_version()[0]
if driver_major <= nvjitlink_version[0]:
_use_nvjitlink_backend = True
return False # Use nvjitlink

warn_txt = (
f"CUDA driver major version {driver_major} is newer than "
f"nvJitLink major version {nvjitlink_version[0]}; therefore "
f"{warn_txt_common} nvJitLink."
)
else:
warn_txt = (
f"{'nvJitLink*.dll' if sys.platform == 'win32' else 'libnvJitLink.so*'} is too old (<12.3)."
f" Therefore cuda.bindings.nvjitlink is not usable and {warn_txt_common} nvJitLink."
)
_use_nvjitlink_backend = True
return False # Use nvjitlink
warn_txt = (
f"{'nvJitLink*.dll' if sys.platform == 'win32' else 'libnvJitLink.so*'} is too old (<12.3)."
f" Therefore cuda.bindings.nvjitlink is not usable and {warn_txt_common} nvJitLink."
)

warn(warn_txt, stacklevel=2, category=RuntimeWarning)
_use_nvjitlink_backend = False
Expand Down
48 changes: 0 additions & 48 deletions cuda_core/tests/test_optional_dependency_imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,6 @@
from cuda.core import _linker, _program


class FakeNvJitLinkModule:
def __init__(self, version):
self._version = version

def version(self):
return self._version


@pytest.fixture(autouse=True)
def restore_optional_import_state():
saved_nvvm_module = _program._nvvm_module
Expand Down Expand Up @@ -111,43 +103,3 @@ def fake__optional_cuda_import(modname, probe_function=None):

assert use_driver_backend is True
assert _linker._use_nvjitlink_backend is False


@pytest.mark.agent_authored(model="gpt-5")
@pytest.mark.parametrize(
("driver_version", "nvjitlink_version"),
[
((12, 8, 0), (12, 9)),
((12, 9, 0), (13, 0)),
],
)
def test_decide_nvjitlink_or_driver_uses_nvjitlink_when_driver_is_not_newer(
monkeypatch, driver_version, nvjitlink_version
):
monkeypatch.setattr(
_linker,
"_optional_cuda_import",
lambda *_args, **_kwargs: FakeNvJitLinkModule(nvjitlink_version),
)
monkeypatch.setattr(_linker, "_nvjitlink_has_version_symbol", lambda _nvjitlink: True)
monkeypatch.setattr(_linker, "driver_version", lambda: driver_version)

assert _linker._decide_nvjitlink_or_driver() is False
assert _linker._use_nvjitlink_backend is True


@pytest.mark.agent_authored(model="gpt-5")
def test_decide_nvjitlink_or_driver_falls_back_when_driver_is_newer(monkeypatch):
monkeypatch.setattr(
_linker,
"_optional_cuda_import",
lambda *_args, **_kwargs: FakeNvJitLinkModule((12, 9)),
)
monkeypatch.setattr(_linker, "_nvjitlink_has_version_symbol", lambda _nvjitlink: True)
monkeypatch.setattr(_linker, "driver_version", lambda: (13, 0, 0))

with pytest.warns(RuntimeWarning, match="is newer than nvJitLink major version"):
use_driver_backend = _linker._decide_nvjitlink_or_driver()

assert use_driver_backend is True
assert _linker._use_nvjitlink_backend is False
25 changes: 11 additions & 14 deletions cuda_core/tests/test_program_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,14 @@ def test_make_program_cache_key_ignores_name_expressions_for_non_nvrtc(code_type
{"link_time_optimization": None},
id="lto_false_eq_none",
),
# ``time`` is a presence gate: the linker emits ``-time`` for any
# non-None value, so True / "path" produce the same flag.
pytest.param(
{"time": True},
{"time": "timing.csv"},
id="time_true_eq_path",
marks=pytest.mark.skipif(is_culink_backend, reason="-time is not supported in cuLink"),
),
# ``no_cache`` has an ``is True`` gate; False and None equivalent.
pytest.param({"no_cache": False}, {"no_cache": None}, id="no_cache_false_eq_none"),
],
Expand All @@ -393,18 +401,6 @@ def test_make_program_cache_key_ptx_linker_equivalent_options_hash_same(a, b, mo
assert k_a == k_b


@pytest.mark.skipif(is_culink_backend, reason="test requires nvJitLink backend")
@pytest.mark.agent_authored(model="gpt-5")
def test_make_program_cache_key_ptx_nvjitlink_time_values_hash_same(monkeypatch):
"""nvJitLink emits ``-time`` for any non-None value."""
from cuda.core.utils import _program_cache

monkeypatch.setattr(_program_cache._keys, "_linker_backend_and_version", lambda _use_driver: ("nvJitLink", "12030"))
k_true = _make_key(code=".version 7.0", code_type="ptx", options=_opts(time=True))
k_path = _make_key(code=".version 7.0", code_type="ptx", options=_opts(time="timing.csv"))
assert k_true == k_path


@pytest.mark.parametrize(
"field, a, b",
[
Expand Down Expand Up @@ -1014,9 +1010,10 @@ def test_make_program_cache_key_rejects_side_effect_options_nvrtc(option_kw, ext
pytest.param({"time": "whatever.csv"}, id="time_path"),
],
)
@pytest.mark.skipif(is_culink_backend, reason="test requires nvJitLink backend")
@pytest.mark.skipif(is_culink_backend, reason="-time is not supported in cuLink")
def test_make_program_cache_key_accepts_side_effect_options_for_ptx(option_kw):
"""nvJitLink ``-time`` writes only to its info log, not the filesystem."""
"""The side-effect guard is NVRTC-specific: PTX (linker) and NVVM must
not be blocked by options whose side effects only apply under NVRTC."""
_make_key(code=".version 7.0", code_type="ptx", options=_opts(**option_kw)) # no raise
Comment on lines 1014 to 1017

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: same as the comment above — without the skipif, time_true/time_path hit the driver-unsupported-options rejection on cuLink hosts. Pinning the backend avoids both the failure and the skip:

Suggested change
def test_make_program_cache_key_accepts_side_effect_options_for_ptx(option_kw):
"""nvJitLink ``-time`` writes only to its info log, not the filesystem."""
"""The side-effect guard is NVRTC-specific: PTX (linker) and NVVM must
not be blocked by options whose side effects only apply under NVRTC."""
_make_key(code=".version 7.0", code_type="ptx", options=_opts(**option_kw)) # no raise
def test_make_program_cache_key_accepts_side_effect_options_for_ptx(option_kw, monkeypatch):
"""The side-effect guard is NVRTC-specific: PTX (linker) and NVVM must
not be blocked by options whose side effects only apply under NVRTC."""
from cuda.core import _linker
monkeypatch.setattr(_linker, "_decide_nvjitlink_or_driver", lambda: False) # nvJitLink
_make_key(code=".version 7.0", code_type="ptx", options=_opts(**option_kw)) # no raise

@isVoid isVoid Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kinda feel like instead of explicitly pinning, we should mark the test with time argument with skipif when the backend is driver. Even though currently we don't have a setup in the CI matrix that touches this test, in the future if we augment the matrix and this path hits, we can at least see the skip reason in the test, which means the un-reverted change is better.



Expand Down
Loading