From 0e5afde06f96b118ea7f67f5919652bc5098b759 Mon Sep 17 00:00:00 2001 From: Jinfeng Date: Fri, 16 Oct 2026 00:03:15 +0000 Subject: [PATCH 1/3] fix: use nvJitLink-compatible 0/1 values for boolean options (nvbug 6621758) --- cuda_core/cuda/core/_linker.pyx | 15 +++++++++------ cuda_core/tests/test_linker.py | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/cuda_core/cuda/core/_linker.pyx b/cuda_core/cuda/core/_linker.pyx index e5deeb07b10..fa045535991 100644 --- a/cuda_core/cuda/core/_linker.pyx +++ b/cuda_core/cuda/core/_linker.pyx @@ -400,13 +400,13 @@ class LinkerOptions: if self.lineinfo: options.append("-lineinfo") if self.ftz is not None: - options.append(f"-ftz={'true' if self.ftz else 'false'}") + options.append(f"-ftz={'1' if self.ftz else '0'}") if self.prec_div is not None: - options.append(f"-prec-div={'true' if self.prec_div else 'false'}") + options.append(f"-prec-div={'1' if self.prec_div else '0'}") if self.prec_sqrt is not None: - options.append(f"-prec-sqrt={'true' if self.prec_sqrt else 'false'}") + options.append(f"-prec-sqrt={'1' if self.prec_sqrt else '0'}") if self.fma is not None: - options.append(f"-fma={'true' if self.fma else 'false'}") + options.append(f"-fma={'1' if self.fma else '0'}") if self.kernels_used is not None: if isinstance(self.kernels_used, str): options.append(f"-kernels-used={self.kernels_used}") @@ -428,9 +428,12 @@ class LinkerOptions: for opt in self.ptxas_options: options.append(f"-Xptxas={opt}") if self.split_compile is not None: - options.append(f"-split-compile={self.split_compile}") + # nvJitLink requires a non-negative integer; coerce bool (a subtype of + # int) so that e.g. ``split_compile=True`` is sent as ``1`` rather + # than the literal string "True", which nvJitLink rejects. + options.append(f"-split-compile={int(self.split_compile)}") if self.split_compile_extended is not None: - options.append(f"-split-compile-extended={self.split_compile_extended}") + options.append(f"-split-compile-extended={int(self.split_compile_extended)}") if self.no_cache is True: options.append("-no-cache") diff --git a/cuda_core/tests/test_linker.py b/cuda_core/tests/test_linker.py index e25578c3dd1..ba9455d799e 100644 --- a/cuda_core/tests/test_linker.py +++ b/cuda_core/tests/test_linker.py @@ -206,7 +206,7 @@ def test_linker_options_as_bytes_nvjitlink(): options_str = [opt.decode() for opt in nvjitlink_options] assert "-arch=sm_80" in options_str assert "-g" in options_str - assert "-ftz=true" in options_str + assert "-ftz=1" in options_str assert "-maxrregcount=32" in options_str From c539cc2c788943dd9846f20d424268ce0c1dd830 Mon Sep 17 00:00:00 2001 From: Jinfeng Date: Fri, 16 Oct 2026 00:19:16 +0000 Subject: [PATCH 2/3] refactor: drop explanatory comment on split_compile int coercion --- cuda_core/cuda/core/_linker.pyx | 3 --- 1 file changed, 3 deletions(-) diff --git a/cuda_core/cuda/core/_linker.pyx b/cuda_core/cuda/core/_linker.pyx index fa045535991..6a077aafa2a 100644 --- a/cuda_core/cuda/core/_linker.pyx +++ b/cuda_core/cuda/core/_linker.pyx @@ -428,9 +428,6 @@ class LinkerOptions: for opt in self.ptxas_options: options.append(f"-Xptxas={opt}") if self.split_compile is not None: - # nvJitLink requires a non-negative integer; coerce bool (a subtype of - # int) so that e.g. ``split_compile=True`` is sent as ``1`` rather - # than the literal string "True", which nvJitLink rejects. options.append(f"-split-compile={int(self.split_compile)}") if self.split_compile_extended is not None: options.append(f"-split-compile-extended={int(self.split_compile_extended)}") From 746df99dc8bccc42cb3995173b9dbd81da2c7490 Mon Sep 17 00:00:00 2001 From: Jinfeng Date: Mon, 19 Oct 2026 22:03:40 +0000 Subject: [PATCH 3/3] add lto enabled runtime test for ftz/prec_div/prec_sqrt/fma --- cuda_core/tests/test_linker.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/cuda_core/tests/test_linker.py b/cuda_core/tests/test_linker.py index ba9455d799e..5a7994f9110 100644 --- a/cuda_core/tests/test_linker.py +++ b/cuda_core/tests/test_linker.py @@ -210,6 +210,32 @@ def test_linker_options_as_bytes_nvjitlink(): assert "-maxrregcount=32" in options_str +@pytest.mark.agent_authored(model="claude-sonnet-5") +@pytest.mark.skipif(is_culink_backend, reason="ftz/prec_div/prec_sqrt/fma are nvjitlink-only options") +def test_linker_lto_enabled_runtime_accepts_bool_options(compile_ltoir_functions): + """Regression test for nvbug 6621758: actual LTO-enabled link, not just option setter. + + With ``link_time_optimization=True``, ``-ftz``/``-prec-div``/``-prec-sqrt``/``-fma`` + are forwarded to libnvvm and actually validated at link time, unlike in a non-LTO + link where legacy ``true``/``false`` values are silently ignored. Only ``0``/``1`` + reliably takes effect here on any CUDA version. + + The test checks that Python ``True``/``False`` values are correctly + mapped to ``1``/``0`` and accepted by the nvJitLink backend during an LTO-enabled link. + """ + options = LinkerOptions( + arch=ARCH, + link_time_optimization=True, + ftz=True, + prec_div=True, + prec_sqrt=True, + fma=True, + ) + linker = Linker(*compile_ltoir_functions, options=options) + linked_code = linker.link("cubin") + assert isinstance(linked_code, ObjectCode) + + @pytest.mark.agent_authored(model="gpt-5.6") @pytest.mark.skipif(is_culink_backend, reason="as_bytes() only supported for nvjitlink backend") @pytest.mark.parametrize("value,expected_count", [(None, 0), (False, 0), (True, 1)])