Coverage for cuda/core/_linker.pyx: 66.67%
426 statements
« prev ^ index » next coverage.py v7.17.2, created at 2026-11-05 01:11 +0000
« prev ^ index » next coverage.py v7.17.2, created at 2026-11-05 01:11 +0000
1# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2#
3# SPDX-License-Identifier: Apache-2.0
4"""Linking machinery for combining object codes.
6This module provides :class:`Linker` for linking one or more
7:class:`~cuda.core.ObjectCode` objects, with :class:`LinkerOptions` for
8configuration.
9"""
11from __future__ import annotations
13from cpython.bytearray cimport PyByteArray_AS_STRING
14from libc.stdint cimport intptr_t, uint32_t
15from libcpp.vector cimport vector
16from cuda.bindings cimport cydriver
17from cuda.bindings cimport cynvjitlink
19from ._rt cimport (
20 as_cu,
21 as_py,
22 create_culink_handle,
23 create_nvjitlink_handle,
24)
25from cuda.core._utils.cuda_utils cimport HANDLE_RETURN, HANDLE_RETURN_NVJITLINK
27import sys
28from dataclasses import dataclass
29from typing import TYPE_CHECKING, Union
30from warnings import warn
32from cuda.pathfinder import DynamicLibNotFoundError
33from cuda.bindings import nvjitlink as _nvjitlink_bindings
34from cuda.core._device import Device
35from cuda.core._module import ObjectCode
36from cuda.core._utils.clear_error_support import assert_type
37from cuda.core._utils.cuda_utils import (
38 CUDAError,
39 check_or_create_options,
40 driver,
41 is_sequence,
42)
43from cuda.core.typing import CompilerBackendType, ObjectCodeFormatType
45if TYPE_CHECKING:
46 import cuda.bindings.driver # no-cython-lint
47 import cuda.bindings.nvjitlink # no-cython-lint
49# Module-level annotations to ensure stubgen-pyx keeps the above imports in
50# the generated `.pyi` so that the LinkerHandleT forward references resolve.
51# These names are not assigned, so they only affect __annotations__.
52_keep_driver_in_stub: "cuda.bindings.driver.CUlinkState"
53_keep_nvjitlink_in_stub: "cuda.bindings.nvjitlink.nvJitLinkHandle"
55ctypedef const char* const_char_ptr
57__all__ = ["Linker", "LinkerOptions"]
59LinkerHandleT = Union["cuda.bindings.nvjitlink.nvJitLinkHandle", "cuda.bindings.driver.CUlinkState"]
62# =============================================================================
63# Principal class
64# =============================================================================
67cdef inline int Linker_check_open(Linker self) except -1:
68 if self.is_closed: 1nbq0eaXzVALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JKrstgchijkldm
69 raise RuntimeError("Linker has been closed") 1n
70 return 0 1nbq0eaXzVALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JKrstgchijkldm
73cdef class Linker:
74 """Represent a linking machinery to link one or more object codes into
75 :class:`~cuda.core.ObjectCode`.
77 This object provides a unified interface to multiple underlying
78 linker libraries (such as nvJitLink or cuLink* from the CUDA driver).
80 Parameters
81 ----------
82 object_codes : :class:`~cuda.core.ObjectCode`
83 One or more ObjectCode objects to be linked.
84 options : :class:`LinkerOptions`, optional
85 Options for the linker. If not provided, default options will be used.
86 """
88 def __init__(self, *object_codes: ObjectCode, options: LinkerOptions | None = None):
89 Linker_init(self, object_codes, options) 2n b q Tb0 e a 1bX z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f Y Z 1 J K 3 r s t g c h i j k l d m
91 @property
92 def is_closed(self) -> bool:
93 """Whether this linker has been closed."""
94 if self._use_nvjitlink: 1nbq0eaXzVALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JKrstgchijkldm
95 return self._nvjitlink_handle.get() == NULL 1nbq0eaXzVALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JKrstgchijkldm
96 return self._culink_handle.get() == NULL
98 def link(self, target_type: ObjectCodeFormatType | str) -> ObjectCode:
99 """Link the provided object codes into a single output of the specified target type.
101 Parameters
102 ----------
103 target_type : ObjectCodeFormatType | str
104 The type of the target output. Must be "cubin", "ptx", or
105 "ltoir". Linked LTOIR output requires
106 ``link_time_optimization=True`` and nvJitLink 13.3 or newer.
108 Returns
109 -------
110 :class:`~cuda.core.ObjectCode`
111 The linked object code of the specified target type.
113 .. note::
115 Ensure that input object codes were compiled with appropriate
116 flags for linking (e.g., relocatable device code enabled).
118 A CUBIN produced with ``incremental=True`` can be passed directly
119 to another :class:`Linker`, but it can still contain unresolved
120 device references and should be finalized before execution.
122 ``"ltoir"`` output contains only the LTOIR carried by the inputs.
123 Direct PTX and CUBIN inputs are rejected because they carry no
124 LTOIR. FATBIN, host object, and library inputs are accepted but
125 may carry no LTOIR, in which case they contribute nothing to the
126 output.
127 """
128 Linker_check_open(self) 1nbq0eaXzVALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JKrstgchijkldm
129 return Linker_link(self, str(target_type)) 1nbq0eaXzVALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JKrstgchijkldm
131 def get_error_log(self) -> str:
132 """Get the error log generated by the linker.
134 Returns
135 -------
136 str
137 The error log.
138 """
139 # After link(), the decoded log is cached here.
140 if self._error_log is not None: 1nbqeaXzVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
141 return self._error_log 1ny
142 Linker_check_open(self) 1nbqeaXzVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
143 cdef cynvjitlink.nvJitLinkHandle c_h
144 cdef size_t c_log_size = 0 1nbqeaXzVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
145 cdef char* c_log_ptr
146 if self._use_nvjitlink: 1nbqeaXzVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
147 c_h = as_cu(self._nvjitlink_handle) 1nbqeaXzVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
148 HANDLE_RETURN_NVJITLINK(c_h, cynvjitlink.nvJitLinkGetErrorLogSize(c_h, &c_log_size)) 1nbqeaXzVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
149 log = bytearray(c_log_size) 1nbqeaXzVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
150 if c_log_size > 0: 1nbqeaXzVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
151 c_log_ptr = <char*>(<bytearray>log) 1X
152 HANDLE_RETURN_NVJITLINK(c_h, cynvjitlink.nvJitLinkGetErrorLog(c_h, c_log_ptr)) 1X
153 return log.decode("utf-8", errors="backslashreplace") 1nbqeaXzVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
154 else:
155 return (<bytearray>self._drv_log_bufs[2]).decode(
156 "utf-8", errors="backslashreplace").rstrip('\x00')
158 def get_info_log(self) -> str:
159 """Get the info log generated by the linker.
161 Returns
162 -------
163 str
164 The info log.
165 """
166 # After link(), the decoded log is cached here.
167 if self._info_log is not None: 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
168 return self._info_log 1nzy
169 Linker_check_open(self) 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
170 cdef cynvjitlink.nvJitLinkHandle c_h
171 cdef size_t c_log_size = 0 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
172 cdef char* c_log_ptr
173 if self._use_nvjitlink: 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
174 c_h = as_cu(self._nvjitlink_handle) 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
175 HANDLE_RETURN_NVJITLINK(c_h, cynvjitlink.nvJitLinkGetInfoLogSize(c_h, &c_log_size)) 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
176 log = bytearray(c_log_size) 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
177 if c_log_size > 0: 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
178 c_log_ptr = <char*>(<bytearray>log) 1beaABCuopfcd
179 HANDLE_RETURN_NVJITLINK(c_h, cynvjitlink.nvJitLinkGetInfoLog(c_h, c_log_ptr)) 1beaABCuopfcd
180 return log.decode("utf-8", errors="backslashreplace") 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
181 else:
182 return (<bytearray>self._drv_log_bufs[0]).decode(
183 "utf-8", errors="backslashreplace").rstrip('\x00')
185 def close(self) -> None:
186 """Destroy this linker."""
187 cdef vector[cydriver.CUjit_option] empty_keys
188 cdef vector[void*] empty_values
189 if self._use_nvjitlink: 1n3gchijkldm
190 self._nvjitlink_handle.reset() 1n3gchijkldm
191 else:
192 if self._drv_log_bufs is not None:
193 if self._info_log is None:
194 self._info_log = self.get_info_log()
195 if self._error_log is None:
196 self._error_log = self.get_error_log()
197 # Destroy the CUlinkState before releasing storage referenced by it.
198 self._culink_handle.reset()
199 self._drv_jit_keys.swap(empty_keys)
200 self._drv_jit_values.swap(empty_values)
201 self._drv_log_bufs = None
203 @property
204 def handle(self) -> LinkerHandleT:
205 """Return the underlying handle object.
207 .. note::
209 The type of the returned object depends on the backend.
211 .. caution::
213 This handle is a Python object. To get the memory address of the underlying C
214 handle, call ``int(Linker.handle)``.
215 """
216 if self._use_nvjitlink: 143
217 return as_py(self._nvjitlink_handle) 143
218 else:
219 return as_py(self._culink_handle)
221 @classmethod
222 def which_backend(cls) -> CompilerBackendType:
223 """Return which linking backend will be used.
225 Returns :attr:`~CompilerBackendType.NVJITLINK` when the nvJitLink
226 library is available and meets the minimum version requirement,
227 otherwise :attr:`~CompilerBackendType.DRIVER`.
229 .. note::
231 Prefer letting :class:`Linker` decide. Query ``which_backend()``
232 only when you need to dispatch based on input format (for
233 example: choose PTX vs. LTOIR before constructing a
234 ``Linker``). The returned value names an implementation
235 detail whose support matrix may shift across CTK releases.
236 """
237 return CompilerBackendType.DRIVER if _decide_nvjitlink_or_driver() else CompilerBackendType.NVJITLINK 2UbQbPb_b2b3bV A L M B N C O u v o P Q R S T U D w E F x G W 3 r s t g c h i j k l d m
240# =============================================================================
241# Supporting classes
242# =============================================================================
244@dataclass
245class LinkerOptions:
246 """Customizable options for configuring :class:`Linker`.
248 Since the linker may choose to use nvJitLink or the driver APIs as the linking backend,
249 not all options are applicable. When the system's installed nvJitLink is too old (<12.3),
250 or not installed, the driver APIs (cuLink) will be used instead.
252 Attributes
253 ----------
254 name : str, optional
255 Name of the linker. If the linking succeeds, the name is passed down to the generated :class:`ObjectCode`.
256 arch : str, optional
257 Pass the SM architecture value, such as ``sm_<CC>`` (for generating CUBIN) or
258 ``compute_<CC>`` (for generating PTX). If not provided, the current device's architecture
259 will be used.
260 max_register_count : int, optional
261 Maximum register count.
262 time : bool, optional
263 Print timing information to the info log.
264 Default: False.
265 verbose : bool, optional
266 Print verbose messages to the info log.
267 Default: False.
268 link_time_optimization : bool, optional
269 Perform link time optimization.
270 Default: False.
271 incremental : bool, optional
272 Perform an incremental link. The result can be passed
273 directly to a later :class:`Linker`. Requires nvJitLink 13.2 or newer
274 and is not supported by the driver linker backend. Intermediate
275 caching is disabled on nvJitLink versions older than 13.6 when combined
276 with link-time optimization, to work around an nvJitLink bug.
277 Default: False.
278 ptx : bool, optional
279 Emit PTX after linking instead of CUBIN; only supported with ``link_time_optimization=True``.
280 Default: False.
281 optimization_level : int, optional
282 Set optimization level. Only 0 and 3 are accepted.
283 debug : bool, optional
284 Generate debug information.
285 Default: False.
286 lineinfo : bool, optional
287 Generate line information.
288 Default: False.
289 ftz : bool, optional
290 Flush denormal values to zero.
291 Default: False.
292 prec_div : bool, optional
293 Use precise division.
294 Default: True.
295 prec_sqrt : bool, optional
296 Use precise square root.
297 Default: True.
298 fma : bool, optional
299 Use fast multiply-add.
300 Default: True.
301 kernels_used : [str | tuple[str] | list[str]], optional
302 Pass a kernel or sequence of kernels that are used; any not in the list can be removed.
303 variables_used : [str | tuple[str] | list[str]], optional
304 Pass a variable or sequence of variables that are used; any not in the list can be removed.
305 optimize_unused_variables : bool, optional
306 Assume that if a variable is not referenced in device code, it can be removed.
307 Default: False.
308 ptxas_options : [str | tuple[str] | list[str]], optional
309 Pass options to PTXAS.
310 split_compile : int, optional
311 Split compilation maximum thread count. Use 0 to use all available processors. Value of 1 disables split
312 compilation (default).
313 Default: 1.
314 split_compile_extended : int, optional
315 A more aggressive form of split compilation available in LTO mode only. Accepts a maximum thread count value.
316 Use 0 to use all available processors. Value of 1 disables extended split compilation (default). Note: This
317 option can potentially impact performance of the compiled binary.
318 Default: 1.
319 no_cache : bool, optional
320 Do not cache the intermediate steps of nvJitLink.
321 Caching is also disabled automatically for incremental links that use
322 link-time optimization on nvJitLink versions older than 13.6, to work
323 around an nvJitLink bug.
324 Default: False.
325 numba_debug : bool, optional
326 Non-functional. ``numba_debug`` is an NVVM/NVRTC *compiler* option;
327 neither nvJitLink nor the driver's cuLink API recognizes it, so no
328 linking backend can honor it and the value is ignored.
329 Default: None.
331 .. deprecated:: 1.2.0
332 Setting this option emits a :class:`DeprecationWarning`. It has never
333 had an effect on any linking backend and will be removed in
334 ``cuda.core`` 2.0.0. Use
335 :attr:`ProgramOptions.numba_debug` on an NVVM or NVRTC compilation
336 path instead.
337 """
339 name: str | None = "<default linker>"
340 arch: str | None = None
341 max_register_count: int | None = None
342 time: bool | None = None
343 verbose: bool | None = None
344 link_time_optimization: bool | None = None
345 ptx: bool | None = None
346 optimization_level: int | None = None
347 debug: bool | None = None
348 lineinfo: bool | None = None
349 ftz: bool | None = None
350 prec_div: bool | None = None
351 prec_sqrt: bool | None = None
352 fma: bool | None = None
353 kernels_used: str | tuple[str] | list[str] | None = None
354 variables_used: str | tuple[str] | list[str] | None = None
355 optimize_unused_variables: bool | None = None
356 ptxas_options: str | tuple[str] | list[str] | None = None
357 split_compile: int | None = None
358 split_compile_extended: int | None = None
359 no_cache: bool | None = None
360 numba_debug: bool | None = None
361 incremental: bool | None = None
363 def __post_init__(self) -> None:
364 _lazy_init() 2LbWbn b q Tb0 e a X z 4 6 H 2 I p y f YbZb8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 7 KbFbGb5 . / : ; = ? @ NbObMbJbHbEbJ K 3 r s t g c h i j k l d m
365 self._name = self.name.encode() 2LbWbn b q Tb0 e a X z 4 6 H 2 I p y f YbZb8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 7 KbFbGb5 . / : ; = ? @ NbObMbJbHbEbJ K 3 r s t g c h i j k l d m
366 # No linking backend reads ``numba_debug``, so warn where the value is
367 # supplied rather than in the option builders -- the user learns once,
368 # at the call site that set it, instead of once per link. The gate is
369 # ``is not None`` (unlike the ignore-warning on the PTX compile path):
370 # it is the *field* that is going away, so any explicit value earns the
371 # notice, including ``False``.
372 if self.numba_debug is not None: 2LbWbn b q Tb0 e a X z 4 6 H 2 I p y f YbZb8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 7 KbFbGb5 . / : ; = ? @ NbObMbJbHbEbJ K 3 r s t g c h i j k l d m
373 warn( 27 FbGb
374 "numba_debug is not supported by any linking backend and is ignored. "
375 "LinkerOptions.numba_debug is deprecated and will be removed in "
376 "cuda.core 2.0.0; use ProgramOptions.numba_debug on an NVVM or NVRTC "
377 "compilation path instead.",
378 DeprecationWarning, 27 FbGb
379 stacklevel=3,
380 )
382 def _prepare_nvjitlink_options(self, as_bytes: bool = False) -> list[bytes] | list[str]:
383 options = [] 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
385 if self.arch is not None: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
386 options.append(f"-arch={self.arch}") 2n b q 0 e a X z 4 A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
387 else:
388 options.append("-arch=sm_" + "".join(f"{i}" for i in Device().compute_capability)) 1V
389 if self.max_register_count is not None: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
390 options.append(f"-maxrregcount={self.max_register_count}") 1L8g
391 if self.time is not None: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
392 options.append("-time") 1Cd
393 if self.verbose: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
394 options.append("-verbose") 1A
395 if self.link_time_optimization: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
396 options.append("-lto") 2b e a p f ] ^ _ ` 9 ! % ' abbbcbdb# $ ( ) ibjbkblb* + , - Y Z
397 if self.incremental: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
398 options.append("-r") 2q 0 e a [ { | } ~ 9 ! % ' ebfbgbhb# $ ( ) mbnbobpb* + , - Y Z
399 if self.ptx: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
400 options.append("-ptx") 16p
401 if self.optimization_level is not None: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
402 options.append(f"-O{self.optimization_level}") 1M
403 if self.debug: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
404 options.append("-g") 2B 8 IbKbFbGbc
405 if self.lineinfo: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
406 options.append("-lineinfo") 2N Ibh
407 if self.ftz is not None: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
408 options.append(f"-ftz={'1' if self.ftz else '0'}") 1Rf8i
409 if self.prec_div is not None: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
410 options.append(f"-prec-div={'1' if self.prec_div else '0'}") 1Sfj
411 if self.prec_sqrt is not None: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
412 options.append(f"-prec-sqrt={'1' if self.prec_sqrt else '0'}") 1Tfk
413 if self.fma is not None: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
414 options.append(f"-fma={'1' if self.fma else '0'}") 1Ufl
415 if self.kernels_used is not None: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
416 if isinstance(self.kernels_used, str): 1DwE
417 options.append(f"-kernels-used={self.kernels_used}") 1D
418 elif isinstance(self.kernels_used, list): 1wE
419 for kernel in self.kernels_used: 1w
420 options.append(f"-kernels-used={kernel}") 1w
421 if self.variables_used is not None: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
422 if isinstance(self.variables_used, str): 1FxG
423 options.append(f"-variables-used={self.variables_used}") 1F
424 elif isinstance(self.variables_used, list): 1xG
425 for variable in self.variables_used: 1x
426 options.append(f"-variables-used={variable}") 1x
427 if self.optimize_unused_variables is not None: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
428 options.append("-optimize-unused-variables") 1O
429 if self.ptxas_options is not None: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
430 if isinstance(self.ptxas_options, str): 1uvo
431 options.append(f"-Xptxas={self.ptxas_options}") 1u
432 elif is_sequence(self.ptxas_options): 1vo
433 for opt in self.ptxas_options: 1vo
434 options.append(f"-Xptxas={opt}") 1vo
435 if self.split_compile is not None: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
436 options.append(f"-split-compile={int(self.split_compile)}") 1Pm
437 if self.split_compile_extended is not None: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
438 options.append(f"-split-compile-extended={int(self.split_compile_extended)}") 1Q
439 # nvbugs 6659652
440 if self.no_cache is True or ( 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
441 _nvjitlink_version < (13, 6) and self.incremental and self.link_time_optimization 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
442 ):
443 options.append("-no-cache") 2e a W 9 ! # $ AbBbCbDbibjbkblbmbnbobpb* + , - Y Z
445 if as_bytes: 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
446 return [o.encode() for o in options] 2n b q 0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f 8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - Y Z 1 KbFbGbJ K 3 r s t g c h i j k l d m
447 else:
448 return options 2Ib
450 def _prepare_driver_options(self) -> tuple[list[object], list[object]]:
451 formatted_options = [] 27 5 . / : ; = ? @ NbObMbJbHbEb
452 option_keys = [] 27 5 . / : ; = ? @ NbObMbJbHbEb
454 # allocate a fixed-sized buffer for each info/error log
455 size = 4194304 27 5 . / : ; = ? @ NbObMbJbHbEb
456 formatted_options.extend((bytearray(size), size, bytearray(size), size)) 27 5 . / : ; = ? @ NbObMbJbHbEb
457 option_keys.extend( 27 5 . / : ; = ? @ NbObMbJbHbEb
458 (
459 _driver.CUjit_option.CU_JIT_INFO_LOG_BUFFER, 27 5 . / : ; = ? @ NbObMbJbHbEb
460 _driver.CUjit_option.CU_JIT_INFO_LOG_BUFFER_SIZE_BYTES, 27 5 . / : ; = ? @ NbObMbJbHbEb
461 _driver.CUjit_option.CU_JIT_ERROR_LOG_BUFFER, 27 5 . / : ; = ? @ NbObMbJbHbEb
462 _driver.CUjit_option.CU_JIT_ERROR_LOG_BUFFER_SIZE_BYTES, 27 5 . / : ; = ? @ NbObMbJbHbEb
463 )
464 )
466 if self.arch is not None: 27 5 . / : ; = ? @ NbObMbJbHbEb
467 arch = self.arch.split("_")[-1].upper() 175
468 formatted_options.append(getattr(_driver.CUjit_target, f"CU_TARGET_COMPUTE_{arch}")) 175
469 option_keys.append(_driver.CUjit_option.CU_JIT_TARGET) 175
470 if self.max_register_count is not None: 27 5 . / : ; = ? @ NbObMbJbHbEb
471 formatted_options.append(self.max_register_count) 15
472 option_keys.append(_driver.CUjit_option.CU_JIT_MAX_REGISTERS) 15
473 if self.time is not None: 27 5 . / : ; = ? @ NbObMbJbHbEb
474 raise ValueError("time option is not supported by the driver API") 2Ob
475 if self.verbose: 27 5 . / : ; = ? @ NbMbJbHbEb
476 formatted_options.append(1) 15
477 option_keys.append(_driver.CUjit_option.CU_JIT_LOG_VERBOSE) 15
478 if self.link_time_optimization: 27 5 . / : ; = ? @ NbMbJbHbEb
479 formatted_options.append(1) 15
480 option_keys.append(_driver.CUjit_option.CU_JIT_LTO) 15
481 if self.incremental: 27 5 . / : ; = ? @ NbMbJbHbEb
482 raise ValueError("incremental option is not supported by the driver API") 2Nb
483 if self.ptx: 27 5 . / : ; = ? @ MbJbHbEb
484 raise ValueError("ptx option is not supported by the driver API") 2Mb
485 if self.optimization_level is not None: 27 5 . / : ; = ? @ JbHbEb
486 formatted_options.append(self.optimization_level) 15
487 option_keys.append(_driver.CUjit_option.CU_JIT_OPTIMIZATION_LEVEL) 15
488 if self.debug: 27 5 . / : ; = ? @ JbHbEb
489 formatted_options.append(1) 15
490 option_keys.append(_driver.CUjit_option.CU_JIT_GENERATE_DEBUG_INFO) 15
491 if self.lineinfo: 27 5 . / : ; = ? @ JbHbEb
492 formatted_options.append(1) 15
493 option_keys.append(_driver.CUjit_option.CU_JIT_GENERATE_LINE_INFO) 15
494 if self.ftz is not None: 27 5 . / : ; = ? @ JbHbEb
495 warn("ftz option is deprecated in the driver API", DeprecationWarning, stacklevel=3) 1.
496 if self.prec_div is not None: 27 5 . / : ; = ? @ JbHbEb
497 warn("prec_div option is deprecated in the driver API", DeprecationWarning, stacklevel=3) 1/
498 if self.prec_sqrt is not None: 27 5 . / : ; = ? @ JbHbEb
499 warn("prec_sqrt option is deprecated in the driver API", DeprecationWarning, stacklevel=3) 1:
500 if self.fma is not None: 27 5 . / : ; = ? @ JbHbEb
501 warn("fma options is deprecated in the driver API", DeprecationWarning, stacklevel=3) 1;
502 if self.kernels_used is not None: 27 5 . / : ; = ? @ JbHbEb
503 warn("kernels_used is deprecated in the driver API", DeprecationWarning, stacklevel=3) 1=
504 if self.variables_used is not None: 27 5 . / : ; = ? @ JbHbEb
505 warn("variables_used is deprecated in the driver API", DeprecationWarning, stacklevel=3) 1?
506 if self.optimize_unused_variables is not None: 27 5 . / : ; = ? @ JbHbEb
507 warn("optimize_unused_variables is deprecated in the driver API", DeprecationWarning, stacklevel=3) 1@
508 if self.ptxas_options is not None: 27 5 . / : ; = ? @ JbHbEb
509 raise ValueError("ptxas_options option is not supported by the driver API") 2Jb
510 if self.split_compile is not None: 27 5 . / : ; = ? @ HbEb
511 raise ValueError("split_compile option is not supported by the driver API") 2Hb
512 if self.split_compile_extended is not None: 27 5 . / : ; = ? @ Eb
513 raise ValueError("split_compile_extended option is not supported by the driver API") 2Eb
514 if self.no_cache is True: 175./:;=?@
515 formatted_options.append(_driver.CUjit_cacheMode.CU_JIT_CACHE_OPTION_NONE) 15
516 option_keys.append(_driver.CUjit_option.CU_JIT_CACHE_MODE) 15
518 return formatted_options, option_keys 175./:;=?@
520 def as_bytes(self, backend: str = "nvjitlink") -> list[bytes]:
521 """Convert linker options to bytes format for the nvjitlink backend.
523 Parameters
524 ----------
525 backend : str, optional
526 The linker backend. Only "nvjitlink" is supported. Default is "nvjitlink".
528 Returns
529 -------
530 list[bytes]
531 List of option strings encoded as bytes.
533 Raises
534 ------
535 ValueError
536 If an unsupported backend is specified.
537 RuntimeError
538 If nvJitLink backend is not available.
539 """
540 backend = backend.lower() 2WbYbZb8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , -
541 if backend != "nvjitlink": 2WbYbZb8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , -
542 raise ValueError(f"as_bytes() only supports 'nvjitlink' backend, got '{backend}'") 2YbZb
543 if not _use_nvjitlink_backend: 2Wb8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , -
544 raise RuntimeError("nvJitLink backend is not available") 2Wb
545 return self._prepare_nvjitlink_options(as_bytes=True) 28 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , -
548# =============================================================================
549# Private implementation: cdef inline helpers
550# =============================================================================
552cdef inline int Linker_init(Linker self, tuple object_codes, object options) except -1:
553 """Initialize a Linker instance."""
554 if len(object_codes) == 0: 2n b q Tb0 e a 1bX z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f Y Z 1 J K 3 r s t g c h i j k l d m
555 raise ValueError("At least one ObjectCode object must be provided") 21b
557 cdef cynvjitlink.nvJitLinkHandle c_raw_nvjitlink
558 cdef cydriver.CUlinkState c_raw_culink
559 cdef Py_ssize_t c_num_opts, i
560 cdef vector[const_char_ptr] c_str_opts
561 cdef cydriver.CUjit_option* c_drv_jit_keys_ptr
562 cdef void** c_drv_jit_values_ptr
564 self._options = options = check_or_create_options(LinkerOptions, options, "Linker options") 2n b q Tb0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f Y Z 1 J K 3 r s t g c h i j k l d m
565 self._has_ptx_or_cubin_input = False 2n b q Tb0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f Y Z 1 J K 3 r s t g c h i j k l d m
566 if options.incremental and options.ptx: 2n b q Tb0 e a X z 4 V A L M B N C O u v o P Q R S T U D w E F x G W 6 H 2 I p y f Y Z 1 J K 3 r s t g c h i j k l d m
567 raise ValueError("incremental and ptx output options cannot be used together") 2Tb
569 if _use_nvjitlink_backend: 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGW6H2IpyfYZ1JK3rstgchijkldm
570 if options.incremental: 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGW6H2IpyfYZ1JK3rstgchijkldm
571 _require_nvjitlink_version((13, 2), "incremental linking") 1q0eaYZ
572 self._use_nvjitlink = True 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGW6H2IpyfYZ1JK3rstgchijkldm
573 options_bytes = options._prepare_nvjitlink_options(as_bytes=True) 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGW6H2IpyfYZ1JK3rstgchijkldm
574 c_num_opts = len(options_bytes) 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGW6H2IpyfYZ1JK3rstgchijkldm
575 c_str_opts.resize(c_num_opts) 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGW6H2IpyfYZ1JK3rstgchijkldm
576 for i in range(c_num_opts): 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGW6H2IpyfYZ1JK3rstgchijkldm
577 c_str_opts[i] = <const char*>(<bytes>options_bytes[i]) 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGW6H2IpyfYZ1JK3rstgchijkldm
578 with nogil: 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGW6H2IpyfYZ1JK3rstgchijkldm
579 HANDLE_RETURN_NVJITLINK(NULL, cynvjitlink.nvJitLinkCreate( 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGW6H2IpyfYZ1JK3rstgchijkldm
580 &c_raw_nvjitlink, <uint32_t>c_num_opts, c_str_opts.data()))
581 self._nvjitlink_handle = create_nvjitlink_handle(c_raw_nvjitlink) 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
582 else:
583 self._use_nvjitlink = False
584 formatted_options, option_keys = options._prepare_driver_options()
585 # Keep the formatted_options list alive: it contains bytearrays that
586 # the driver writes into via raw pointers during linking operations.
587 self._drv_log_bufs = formatted_options
588 c_num_opts = len(option_keys)
589 self._drv_jit_keys.resize(c_num_opts)
590 self._drv_jit_values.resize(c_num_opts)
591 for i in range(c_num_opts):
592 self._drv_jit_keys[i] = <cydriver.CUjit_option><int>option_keys[i]
593 val = formatted_options[i]
594 if isinstance(val, bytearray):
595 self._drv_jit_values[i] = <void*>PyByteArray_AS_STRING(val)
596 else:
597 self._drv_jit_values[i] = <void*><intptr_t>int(val)
598 c_drv_jit_keys_ptr = self._drv_jit_keys.data()
599 c_drv_jit_values_ptr = self._drv_jit_values.data()
600 try:
601 with nogil:
602 HANDLE_RETURN(cydriver.cuLinkCreate(
603 <unsigned int>c_num_opts,
604 c_drv_jit_keys_ptr,
605 c_drv_jit_values_ptr,
606 &c_raw_culink))
607 except CUDAError as e:
608 Linker_annotate_error_log(self, e)
609 raise
610 self._culink_handle = create_culink_handle(c_raw_culink)
612 for code in object_codes: 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
613 assert_type(code, ObjectCode) 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
614 Linker_add_code_object(self, code) 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
615 return 0 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
618cdef inline void Linker_add_code_object(Linker self, object object_code) except *:
619 """Add a single ObjectCode to the linker."""
620 data = object_code.code 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
621 cdef cynvjitlink.nvJitLinkHandle c_nvjitlink_h
622 cdef cydriver.CUlinkState c_culink_state
623 cdef cynvjitlink.nvJitLinkInputType c_nv_input_type
624 cdef cydriver.CUjitInputType c_drv_input_type
625 cdef const char* c_data_ptr
626 cdef size_t c_data_size
627 cdef const char* c_file_ptr
629 name_bytes = f"{object_code.name}".encode() 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
630 cdef const char* c_name_ptr = <const char*>name_bytes 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
632 input_types = _nvjitlink_input_types if self._use_nvjitlink else _driver_input_types 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
633 py_input_type = input_types.get(object_code.code_type) 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
634 if py_input_type is None: 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
635 raise ValueError(f"Unknown code_type associated with ObjectCode: {object_code.code_type}")
637 if self._use_nvjitlink: 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
638 c_nvjitlink_h = as_cu(self._nvjitlink_handle) 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
639 c_nv_input_type = <cynvjitlink.nvJitLinkInputType><int>py_input_type 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
640 if isinstance(data, bytes): 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
641 c_data_ptr = <const char*>(<bytes>data) 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
642 c_data_size = len(data) 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
643 with nogil: 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
644 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, cynvjitlink.nvJitLinkAddData( 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
645 c_nvjitlink_h, c_nv_input_type, <const void*>c_data_ptr, c_data_size, c_name_ptr))
646 elif isinstance(data, str):
647 file_bytes = data.encode()
648 c_file_ptr = <const char*>file_bytes
649 with nogil:
650 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, cynvjitlink.nvJitLinkAddFile(
651 c_nvjitlink_h, c_nv_input_type, c_file_ptr))
652 else:
653 raise TypeError(f"Expected bytes or str, but got {type(data).__name__}")
654 else:
655 c_culink_state = as_cu(self._culink_handle)
656 c_drv_input_type = <cydriver.CUjitInputType><int>py_input_type
657 try:
658 if isinstance(data, bytes):
659 c_data_ptr = <const char*>(<bytes>data)
660 c_data_size = len(data)
661 with nogil:
662 HANDLE_RETURN(cydriver.cuLinkAddData(
663 c_culink_state, c_drv_input_type, <void*>c_data_ptr, c_data_size, c_name_ptr,
664 0, NULL, NULL))
665 elif isinstance(data, str):
666 file_bytes = data.encode()
667 c_file_ptr = <const char*>file_bytes
668 with nogil:
669 HANDLE_RETURN(cydriver.cuLinkAddFile(
670 c_culink_state, c_drv_input_type, c_file_ptr, 0, NULL, NULL))
671 else:
672 raise TypeError(f"Expected bytes or str, but got {type(data).__name__}")
673 except CUDAError as e:
674 Linker_annotate_error_log(self, e)
675 raise
677 if object_code.code_type in ("ptx", "cubin"): 1nbq0eaXz4VALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JK3rstgchijkldm
678 self._has_ptx_or_cubin_input = True 1nq0eXz4VALMBNCOuvoPQRSTUDwEFxGWH2IyYZ1JK3rstgchijkldm
681cdef inline object Linker_link(Linker self, str target_type):
682 """Complete linking and return the result as ObjectCode."""
683 if target_type not in ("cubin", "ptx", "ltoir"): 1nbq0eaXzVALMBNCOuvoPQRSTUDwEFxGWH2IpyfYZ1JKrstgchijkldm
684 raise ValueError(f"Unsupported target type: {target_type}") 12
685 if self._options.incremental and target_type == "ptx": 1nbq0eaXzVALMBNCOuvoPQRSTUDwEFxGWHIpyfYZ1JKrstgchijkldm
686 raise ValueError("PTX output is not supported for incremental linking") 10
687 if target_type == "ltoir": 1nbqeaXzVALMBNCOuvoPQRSTUDwEFxGWHIpyfYZ1JKrstgchijkldm
688 if not self._use_nvjitlink: 1baYZ1
689 raise ValueError("LTOIR output is not supported by the driver API")
690 if not self._options.link_time_optimization: 1baYZ1
691 raise ValueError("LTOIR output requires link_time_optimization=True") 11
692 if self._has_ptx_or_cubin_input: 1baYZ
693 raise ValueError( 1YZ
694 'LTOIR output is not supported with "ptx" or "cubin" inputs; '
695 "they carry no LTOIR and would be omitted from the output"
696 )
697 _require_nvjitlink_version((13, 3), "LTOIR output") 1ba
699 cdef cynvjitlink.nvJitLinkHandle c_nvjitlink_h
700 cdef cydriver.CUlinkState c_culink_state
701 cdef size_t c_output_size = 0 1nbqeaXzVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
702 cdef char* c_code_ptr
703 cdef void* c_cubin_out = NULL 1nbqeaXzVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
705 if self._use_nvjitlink: 1nbqeaXzVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
706 c_nvjitlink_h = as_cu(self._nvjitlink_handle) 1nbqeaXzVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
707 with nogil: 1nbqeaXzVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
708 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, cynvjitlink.nvJitLinkComplete(c_nvjitlink_h)) 1nbqeaXzVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
709 if target_type == "cubin": 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
710 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIyfJKrstgchijkldm
711 cynvjitlink.nvJitLinkGetLinkedCubinSize(c_nvjitlink_h, &c_output_size)) 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIyfJKrstgchijkldm
712 code = bytearray(c_output_size) 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIyfJKrstgchijkldm
713 c_code_ptr = <char*>(<bytearray>code) 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIyfJKrstgchijkldm
714 with nogil: 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIyfJKrstgchijkldm
715 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIyfJKrstgchijkldm
716 cynvjitlink.nvJitLinkGetLinkedCubin(c_nvjitlink_h, c_code_ptr)) 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIyfJKrstgchijkldm
717 elif target_type == "ptx": 1bap
718 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, 1p
719 cynvjitlink.nvJitLinkGetLinkedPtxSize(c_nvjitlink_h, &c_output_size)) 1p
720 code = bytearray(c_output_size) 1p
721 c_code_ptr = <char*>(<bytearray>code) 1p
722 with nogil: 1p
723 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, 1p
724 cynvjitlink.nvJitLinkGetLinkedPtx(c_nvjitlink_h, c_code_ptr)) 1p
725 else:
726 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, 1ba
727 cynvjitlink.nvJitLinkGetLinkedLTOIRSize(c_nvjitlink_h, &c_output_size)) 1ba
728 code = bytearray(c_output_size) 1ba
729 c_code_ptr = <char*>(<bytearray>code) 1ba
730 with nogil: 1ba
731 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, 1ba
732 cynvjitlink.nvJitLinkGetLinkedLTOIR(c_nvjitlink_h, c_code_ptr)) 1ba
733 else:
734 c_culink_state = as_cu(self._culink_handle)
735 try:
736 with nogil:
737 HANDLE_RETURN(cydriver.cuLinkComplete(c_culink_state, &c_cubin_out, &c_output_size))
738 except CUDAError as e:
739 Linker_annotate_error_log(self, e)
740 raise
741 code = (<char*>c_cubin_out)[:c_output_size]
743 # Linking is complete; cache the decoded logs. cuLinkDestroy may still
744 # dereference the raw log-buffer pointers, so retain them until close().
745 self._info_log = self.get_info_log() 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
746 self._error_log = self.get_error_log() 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
748 return ObjectCode._init(bytes(code), target_type, name=self._options.name) 1nbqeazVALMBNCOuvoPQRSTUDwEFxGWHIpyfJKrstgchijkldm
751cdef inline void Linker_annotate_error_log(Linker self, object e):
752 """Annotate a CUDAError with the driver linker error log."""
753 error_log = self.get_error_log()
754 if error_log:
755 e.args = (e.args[0] + f"\nLinker error log: {error_log}", *e.args[1:])
758# =============================================================================
759# Private implementation: module-level state and initialization
760# =============================================================================
762# TODO: revisit this treatment for py313t builds
763_driver = None # populated if nvJitLink cannot be used
764_nvjitlink_version = None
765_inited = False
766_use_nvjitlink_backend = None # set by _decide_nvjitlink_or_driver()
768# Input type mappings populated by _lazy_init() with C-level enum ints.
769_nvjitlink_input_types = None
770_driver_input_types = None
773def _require_nvjitlink_version(minimum_version: tuple[int, int], feature: str) -> None:
774 """Check that the cached nvJitLink runtime meets a feature's requirement."""
775 if _nvjitlink_version < minimum_version: 2b q 0 e a Y Z `b0b
776 required = ".".join(str(component) for component in minimum_version) 20b
777 detected = ".".join(str(component) for component in _nvjitlink_version) 20b
778 raise RuntimeError(f"{feature} requires nvJitLink {required} or newer; found {detected}") 20b
781def _nvjitlink_has_version_symbol(nvjitlink) -> bool:
782 # This condition is equivalent to testing for version >= 12.3
783 return bool(nvjitlink._inspect_function_pointer("__nvJitLinkVersion"))
786# Note: this function is reused in the tests
787def _decide_nvjitlink_or_driver() -> bool:
788 """Return True if falling back to the cuLink* driver APIs."""
789 global _driver, _nvjitlink_version, _use_nvjitlink_backend
790 if _use_nvjitlink_backend is not None: 2LbUbQbPb2b3bV A L M B N C O u v o P Q R S T U D w E F x G W RbVbSbXb3 r s t g c h i j k l d m 4b5b6b7b8b9b!b#b$b%b'b(b)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b
791 return not _use_nvjitlink_backend 2LbUb2b3bV A L M B N C O u v o P Q R S T U D w E F x G W 3 r s t g c h i j k l d m 4b5b6b7b8b9b!b#b$b%b'b(b)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b
793 warn_txt_common = (
794 "the driver APIs will be used instead, which do not support" 2LbUbQbPbRbVbSbXb
795 " minor version compatibility or linking LTO IRs."
796 " For best results, consider upgrading to a recent version of"
797 )
799 # Every cuda-bindings that cuda.core accepts provides cuda.bindings.nvjitlink.
800 # Only the nvJitLink library itself can be missing or too old.
801 from cuda.bindings._internal import nvjitlink 2LbUbQbPbRbVbSbXb
803 try: 2LbUbQbPbRbVbSbXb
804 has_version_symbol = _nvjitlink_has_version_symbol(nvjitlink) 2LbUbQbPbRbVbSbXb
805 except DynamicLibNotFoundError: 2QbVb
806 warn_txt = (
807 f"cuda.bindings.nvjitlink is not available, therefore {warn_txt_common} cuda-bindings." 2QbVb
808 )
809 else:
810 if has_version_symbol: 2LbUbPbRbSbXb
811 _nvjitlink_version = _nvjitlink_bindings.version() 2LbUbXb
812 _use_nvjitlink_backend = True 2LbUbXb
813 return False # Use nvjitlink 2LbUbXb
814 warn_txt = (
815 f"{'nvJitLink*.dll' if sys.platform == 'win32' else 'libnvJitLink.so*'} is too old (<12.3)." 2PbRbSb
816 f" Therefore cuda.bindings.nvjitlink is not usable and {warn_txt_common} nvJitLink." 2PbRbSb
817 )
819 warn(warn_txt, stacklevel=2, category=RuntimeWarning) 2QbPbRbVbSb
820 _driver = driver 2QbPbRbVbSb
821 _use_nvjitlink_backend = False 2QbPbRbVbSb
822 return True 2QbPbRbVbSb
825def _lazy_init() -> None:
826 global _inited, _nvjitlink_input_types, _driver_input_types
827 if _inited: 2LbWbn b q Tb0 e a X z 4 6 H 2 I p y f YbZb8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 7 KbFbGb5 . / : ; = ? @ NbObMbJbHbEbJ K 3 r s t g c h i j k l d m
828 return 2LbWbn b q Tb0 e a X z 4 6 H 2 I p y f YbZb8 qbrb[ sbtbubvb] ^ _ ` { | } ~ 9 ! % ' wbxbybzbabbbcbdbebfbgbhb# $ ( ) AbBbCbDbibjbkblbmbnbobpb* + , - IbY Z 1 7 KbFbGb5 . / : ; = ? @ NbObMbJbHbEbJ K 3 r s t g c h i j k l d m
830 _decide_nvjitlink_or_driver()
831 if _use_nvjitlink_backend:
832 _nvjitlink_input_types = {
833 "ptx": <int>cynvjitlink.NVJITLINK_INPUT_PTX,
834 "cubin": <int>cynvjitlink.NVJITLINK_INPUT_CUBIN,
835 "fatbin": <int>cynvjitlink.NVJITLINK_INPUT_FATBIN,
836 "ltoir": <int>cynvjitlink.NVJITLINK_INPUT_LTOIR,
837 "object": <int>cynvjitlink.NVJITLINK_INPUT_OBJECT,
838 "library": <int>cynvjitlink.NVJITLINK_INPUT_LIBRARY,
839 }
840 else:
841 _driver_input_types = {
842 "ptx": <int>cydriver.CU_JIT_INPUT_PTX,
843 "cubin": <int>cydriver.CU_JIT_INPUT_CUBIN,
844 "fatbin": <int>cydriver.CU_JIT_INPUT_FATBINARY,
845 "object": <int>cydriver.CU_JIT_INPUT_OBJECT,
846 "library": <int>cydriver.CU_JIT_INPUT_LIBRARY,
847 }
848 _inited = True