Skip to content

fix(core): reject use_libdevice on the NVRTC and PTX backends - #2573

Open
LeSingh1 wants to merge 1 commit into
NVIDIA:mainfrom
LeSingh1:program-use-libdevice-guard
Open

fix(core): reject use_libdevice on the NVRTC and PTX backends#2573
LeSingh1 wants to merge 1 commit into
NVIDIA:mainfrom
LeSingh1:program-use-libdevice-guard

Conversation

@LeSingh1

@LeSingh1 LeSingh1 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Problem

ProgramOptions.use_libdevice is documented as "Only supported for the NVVM backend" (_program.pyx:465-468). The guard that was supposed to enforce that sits in Program_init's final else — the branch reached only when code_type is not a backend at all:

    else:
        supported_code_types = tuple(x.value for x in SourceCodeType)
        if options.use_libdevice:
            raise ValueError("use_libdevice is only supported by the NVVM backend")
        raise RuntimeError(f"Unsupported {code_type=} ({supported_code_types=})")

Two wrong behaviours follow:

1. The two real non-NVVM backends accept the option silently. Program(src, "c++", ProgramOptions(use_libdevice=True)) and the "ptx" equivalent construct fine. self._use_libdevice is initialised to False (_program.pyx:765) and only flipped inside the nvvm branch, so libdevice is never loaded — the caller finds out via undefined-symbol errors at link time instead of via the documented up-front ValueError.

2. A typo'd code_type reports the wrong error. Program(src, "bogus", ProgramOptions(use_libdevice=True)) raises "use_libdevice is only supported by the NVVM backend" and never mentions that code_type is the actual problem.

The sibling option shows the intended shape: extra_sources has the same "NVVM only" contract, and its guard is duplicated into each real branch — _program.pyx:772-773 (c++) and :790-791 (ptx).

Fix

Move the use_libdevice check next to those extra_sources guards in the c++ and ptx branches, and drop it from the else so an unrecognised code_type reports itself. No behaviour changes for code_type="nvvm".

This is an error-path-only change: any program that compiled before still compiles, and any program that is newly rejected was already producing a build the user did not ask for.

Tests

Three cases in cuda_core/tests/test_program.py, placed next to the existing extra_sources tests they mirror:

  • test_cpp_program_with_use_libdevice — NVRTC rejects it.
  • test_ptx_program_use_libdevice_unsupported — the PTX/linker path rejects it.
  • test_program_init_invalid_code_type_reports_the_code_typeuse_libdevice=True no longer shadows the unrecognised-code_type error.

What I ran

Environment: macOS, no CUDA driver and no CUDA toolkit, so cuda.core cannot be built or imported here.

  • Did not run: the three new tests, or anything else in cuda_core/tests/ — they need a built cuda.core and a GPU. They are written against the existing extra_sources tests immediately adjacent to them, which use the same fixtures.
  • Ran: a reduction of Program_init's dispatch with the CUDA calls removed and the guard placement kept verbatim, before and after the change:
--- before ---
  code_type='c++'      use_libdevice=True -> accepted, _use_libdevice=False
  code_type='ptx'      use_libdevice=True -> accepted, _use_libdevice=False
  code_type='nvvm'     use_libdevice=True -> accepted, _use_libdevice=True
  code_type='fortran'  use_libdevice=True -> ValueError: use_libdevice is only supported by the NVVM backend
--- after ---
  code_type='c++'      use_libdevice=True -> ValueError: use_libdevice is not supported by the NVRTC backend (C++ code_type)
  code_type='ptx'      use_libdevice=True -> ValueError: use_libdevice is not supported by the PTX backend.
  code_type='nvvm'     use_libdevice=True -> accepted, _use_libdevice=True
  code_type='fortran'  use_libdevice=True -> RuntimeError: Unsupported code_type='fortran' (SUPPORTED=('c++', 'ptx', 'nvvm'))
  • Ran: ruff check / ruff format --check on cuda_core/tests/test_program.py — clean, no new findings against a main baseline for that file.
  • Checked: use_libdevice appears in _program.pyx only at the two sites above (the nvvm set and this guard), so nothing else depended on the old placement. test_program_init_invalid_code_type still passes unchanged because its ProgramOptions leaves use_libdevice at its False default.

`ProgramOptions.use_libdevice` is documented "Only supported for the NVVM
backend", but the guard enforcing that was written in `Program_init`'s final
`else` -- the branch reached only when `code_type` is not a backend at all:

    else:
        supported_code_types = tuple(x.value for x in SourceCodeType)
        if options.use_libdevice:
            raise ValueError("use_libdevice is only supported by the NVVM backend")
        raise RuntimeError(f"Unsupported {code_type=} ({supported_code_types=})")

So it never fires for the two real non-NVVM backends, and it fires for the
wrong reason on a typo:

* `Program(src, "c++", ProgramOptions(use_libdevice=True))` and the "ptx"
  equivalent are accepted silently. `self._use_libdevice` is initialised to
  False and only set inside the `nvvm` branch, so libdevice is never loaded
  and the caller learns about it from undefined-symbol errors at link time
  instead of from the documented up-front ValueError.
* `Program(src, "bogus", ProgramOptions(use_libdevice=True))` raises
  "use_libdevice is only supported by the NVVM backend", which says nothing
  about the code_type that is the actual problem.

Move the check next to the per-branch `extra_sources` guards, which are the
sibling option with the same "NVVM only" contract and are already duplicated
into the "c++" and "ptx" branches, and drop it from the `else` so an
unrecognised code_type reports itself.
@copy-pr-bot

copy-pr-bot Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions github-actions Bot added the cuda.core Everything related to the cuda.core module label Aug 9, 2026
mentioned that the code_type was the actual problem.
"""
with pytest.raises(RuntimeError, match=r"^Unsupported code_type='fortran'"):
Program("goto 100", "FORTRAN", ProgramOptions(arch="sm_80", use_libdevice=True))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Regex can be :

match=r"^Unsupported code_type='fortran' \(supported_code_types=\('c\+\+', 'ptx', 'nvvm'\)\)$"

if options.extra_sources is not None:
raise ValueError("extra_sources is not supported by the PTX backend.")
if options.use_libdevice:
raise ValueError("use_libdevice is not supported by the PTX backend.")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It seems deprecation is a much more stable option than directly removing .

if code_type == "c++":
    assert_type(code, str)
    if options.extra_sources is not None:
        raise ValueError("extra_sources is not supported by the NVRTC backend (C++ code_type)")
    if options.use_libdevice:
        warnings.warn(
            "use_libdevice is only supported by the NVVM backend; it is ignored "
            "on the NVRTC and PTX backend and will raise ValueError in a future release.",
            DeprecationWarning,
            stacklevel=3,
        )

@leofang @rwgk what do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

codex (in addition to the findings posted under this comment):

Good point. Although the documented NVVM-only contract makes an immediate error defensible as a bug fix, I agree that warning is the safer behavior for 1.x. The ignored behavior was explicitly codified before 1.0 by test_make_program_cache_key_use_libdevice_ignored_for_non_nvvm.

I’d mirror #2658: emit a visible UserWarning for both code_type="c++" and "ptx" and continue ignoring the option. I would not use DeprecationWarning, because ProgramOptions.use_libdevice itself is not deprecated—it remains supported for NVVM. We should still remove the misplaced check from the unknown-code_type branch so the invalid code type remains the primary error.

If we decide these combinations should eventually raise, our support policy puts that change at 2.0.0, and we should state that version explicitly. Also, agreed on tightening the regex to the full expected message.

@rwgk

rwgk commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

I asked codex gpt-5.6-sol ultra for a full review, this is what it found:

Current HEAD commit: 69df389

The dispatch fix is sensible, but immediate rejection conflicts with released behavior and misses another public API surface.

Findings

  • P1 — Compatibility/cache mismatch: The new errors at cuda_core/cuda/core/_program.pyx:774 and :794 turn previously successful 1.x calls into failures. More importantly, cuda_core/tests/test_program_cache.py:468 explicitly codified the ignored behavior before 1.0, while make_program_cache_key still accepts it. Either warn and preserve behavior, or also update both cache validators and their tests. The support policy reserves breaking changes for major releases at cuda_core/docs/source/support.rst:12.
  • P2 — The failure rationale is overstated: cuda_core/tests/test_program.py:737 and the release note claim the ignored option leaves callers with undefined-symbol errors. That depends on the input; the new empty C++ kernel contains no libdevice symbols and previously compiled. NVIDIA documents libdevice as NVVM bitcode intended for NVVM IR compilation, so “the option was inapplicable and ignored” is the accurate statement. NVIDIA libdevice guide
  • P3 — The new invalid-code-type test weakens an existing assertion: cuda_core/tests/test_program.py:348 duplicates the exact test immediately above but matches only the prefix. Abhilash’s full regex is correct; preferably parameterize the existing test over default options and use_libdevice=True.
  • P3 — Test setup can be tighter: For immediate-error tests, use arch="sm_80" to avoid an incidental Device() query, and the PTX case does not need ptx_code_object because it raises before parsing/linking. If changed to warning-and-continue, retain a valid fixture and assert construction still succeeds.
  • P3 — Release-note polish: cuda_core/docs/source/release/1.2.0-notes.rst:76 should link PR fix(core): reject use_libdevice on the NVRTC and PTX backends #2573 when its conflict is resolved.

Current main now provides the strongest precedent: PR #2658 emits UserWarning and continues ignoring ProgramOptions.numba_debug=True on PTX. It deliberately does not use DeprecationWarning, because the option remains supported on other backends. The same reasoning applies here.

@rwgk rwgk added the P1 Medium priority - Should do label Sep 1, 2026
@rwgk rwgk added this to the cuda.core next milestone Sep 1, 2026
@rwgk rwgk added the bug Something isn't working label Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working cuda.core Everything related to the cuda.core module P1 Medium priority - Should do

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants