Coverage for cuda/core/_linker.pyx: 80.05%

391 statements  

« prev     ^ index     » next       coverage.py v7.16.0, created at 2026-09-03 02:41 +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. 

5  

6This module provides :class:`Linker` for linking one or more 

7:class:`~cuda.core.ObjectCode` objects, with :class:`LinkerOptions` for 

8configuration. 

9""" 

10  

11from __future__ import annotations 

12  

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 

18  

19from ._resource_handles 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 

26  

27import sys 

28from dataclasses import dataclass 

29from typing import TYPE_CHECKING, Union 

30from warnings import warn 

31  

32from cuda.pathfinder import DynamicLibNotFoundError 

33from cuda.pathfinder._optional_cuda_import import _optional_cuda_import 

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 

44  

45if TYPE_CHECKING: 

46 import cuda.bindings.driver # no-cython-lint 

47 import cuda.bindings.nvjitlink # no-cython-lint 

48  

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" 

54  

55ctypedef const char* const_char_ptr 

56  

57__all__ = ["Linker", "LinkerOptions"] 

58  

59LinkerHandleT = Union["cuda.bindings.nvjitlink.nvJitLinkHandle", "cuda.bindings.driver.CUlinkState"] 

60  

61  

62# ============================================================================= 

63# Principal class 

64# ============================================================================= 

65  

66  

67cdef inline int Linker_check_open(Linker self) except -1: 

68 if self.is_closed: 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFmnocadefghbi

69 raise RuntimeError("Linker has been closed") 1j

70 return 0 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFmnocadefghbi

71  

72  

73cdef class Linker: 

74 """Represent a linking machinery to link one or more object codes into 

75 :class:`~cuda.core.ObjectCode`. 

76  

77 This object provides a unified interface to multiple underlying 

78 linker libraries (such as nvJitLink or cuLink* from the CUDA driver). 

79  

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 """ 

87  

88 def __init__(self, *object_codes: ObjectCode, options: LinkerOptions | None = None): 

89 Linker_init(self, object_codes, options) 1j@TuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDltEFVmnocadefghbi

90  

91 @property 

92 def is_closed(self) -> bool: 

93 """Whether this linker has been closed.""" 

94 if self._use_nvjitlink: 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFmnocadefghbi

95 return self._nvjitlink_handle.get() == NULL 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFmnocadefghbi

96 return self._culink_handle.get() == NULL 

97  

98 def link(self, target_type: ObjectCodeFormatType | str) -> ObjectCode: 

99 """Link the provided object codes into a single output of the specified target type. 

100  

101 Parameters 

102 ---------- 

103 target_type : ObjectCodeFormatType | str 

104 The type of the target output. Must be either "cubin" or "ptx". 

105  

106 Returns 

107 ------- 

108 :class:`~cuda.core.ObjectCode` 

109 The linked object code of the specified target type. 

110  

111 .. note:: 

112  

113 Ensure that input object codes were compiled with appropriate 

114 flags for linking (e.g., relocatable device code enabled). 

115 """ 

116 Linker_check_open(self) 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFmnocadefghbi

117 return Linker_link(self, str(target_type)) 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFmnocadefghbi

118  

119 def get_error_log(self) -> str: 

120 """Get the error log generated by the linker. 

121  

122 Returns 

123 ------- 

124 str 

125 The error log. 

126 """ 

127 # After link(), the decoded log is cached here. 

128 if self._error_log is not None: 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

129 return self._error_log 1jt

130 Linker_check_open(self) 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

131 cdef cynvjitlink.nvJitLinkHandle c_h 

132 cdef size_t c_log_size = 0 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

133 cdef char* c_log_ptr 

134 if self._use_nvjitlink: 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

135 c_h = as_cu(self._nvjitlink_handle) 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

136 HANDLE_RETURN_NVJITLINK(c_h, cynvjitlink.nvJitLinkGetErrorLogSize(c_h, &c_log_size)) 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

137 log = bytearray(c_log_size) 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

138 if c_log_size > 0: 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

139 c_log_ptr = <char*>(<bytearray>log) 1T

140 HANDLE_RETURN_NVJITLINK(c_h, cynvjitlink.nvJitLinkGetErrorLog(c_h, c_log_ptr)) 1T

141 return log.decode("utf-8", errors="backslashreplace") 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

142 else: 

143 return (<bytearray>self._drv_log_bufs[2]).decode( 

144 "utf-8", errors="backslashreplace").rstrip('\x00') 

145  

146 def get_info_log(self) -> str: 

147 """Get the info log generated by the linker. 

148  

149 Returns 

150 ------- 

151 str 

152 The info log. 

153 """ 

154 # After link(), the decoded log is cached here. 

155 if self._info_log is not None: 1juRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

156 return self._info_log 1jut

157 Linker_check_open(self) 1juRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

158 cdef cynvjitlink.nvJitLinkHandle c_h 

159 cdef size_t c_log_size = 0 1juRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

160 cdef char* c_log_ptr 

161 if self._use_nvjitlink: 1juRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

162 c_h = as_cu(self._nvjitlink_handle) 1juRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

163 HANDLE_RETURN_NVJITLINK(c_h, cynvjitlink.nvJitLinkGetInfoLogSize(c_h, &c_log_size)) 1juRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

164 log = bytearray(c_log_size) 1juRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

165 if c_log_size > 0: 1juRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

166 c_log_ptr = <char*>(<bytearray>log) 1vwxpklab

167 HANDLE_RETURN_NVJITLINK(c_h, cynvjitlink.nvJitLinkGetInfoLog(c_h, c_log_ptr)) 1vwxpklab

168 return log.decode("utf-8", errors="backslashreplace") 1juRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

169 else: 

170 return (<bytearray>self._drv_log_bufs[0]).decode( 

171 "utf-8", errors="backslashreplace").rstrip('\x00') 

172  

173 def close(self) -> None: 

174 """Destroy this linker.""" 

175 cdef vector[cydriver.CUjit_option] empty_keys 

176 cdef vector[void*] empty_values 

177 if self._use_nvjitlink: 1jVcadefghbi

178 self._nvjitlink_handle.reset() 1jVcadefghbi

179 else: 

180 if self._drv_log_bufs is not None: 

181 if self._info_log is None: 

182 self._info_log = self.get_info_log() 

183 if self._error_log is None: 

184 self._error_log = self.get_error_log() 

185 # Destroy the CUlinkState before releasing storage referenced by it. 

186 self._culink_handle.reset() 

187 self._drv_jit_keys.swap(empty_keys) 

188 self._drv_jit_values.swap(empty_values) 

189 self._drv_log_bufs = None 

190  

191 @property 

192 def handle(self) -> LinkerHandleT: 

193 """Return the underlying handle object. 

194  

195 .. note:: 

196  

197 The type of the returned object depends on the backend. 

198  

199 .. caution:: 

200  

201 This handle is a Python object. To get the memory address of the underlying C 

202 handle, call ``int(Linker.handle)``. 

203 """ 

204 if self._use_nvjitlink: 1WV

205 return as_py(self._nvjitlink_handle) 1WV

206 else: 

207 return as_py(self._culink_handle) 

208  

209 @classmethod 

210 def which_backend(cls) -> CompilerBackendType: 

211 """Return which linking backend will be used. 

212  

213 Returns :attr:`~CompilerBackendType.NVJITLINK` when the nvJitLink 

214 library is available and meets the minimum version requirement, 

215 otherwise :attr:`~CompilerBackendType.DRIVER`. 

216  

217 .. note:: 

218  

219 Prefer letting :class:`Linker` decide. Query ``which_backend()`` 

220 only when you need to dispatch based on input format (for 

221 example: choose PTX vs. LTOIR before constructing a 

222 ``Linker``). The returned value names an implementation 

223 detail whose support matrix may shift across CTK releases. 

224 """ 

225 return CompilerBackendType.DRIVER if _decide_nvjitlink_or_driver() else CompilerBackendType.NVJITLINK 2+ * vb[ ] R v G H w I x J p k q K L M N O P y r z A s B Q V m n o c a d e f g h b i

226  

227  

228# ============================================================================= 

229# Supporting classes 

230# ============================================================================= 

231  

232@dataclass 

233class LinkerOptions: 

234 """Customizable options for configuring :class:`Linker`. 

235  

236 Since the linker may choose to use nvJitLink or the driver APIs as the linking backend, 

237 not all options are applicable. When the system's installed nvJitLink is too old (<12.3), 

238 or not installed, the driver APIs (cuLink) will be used instead. 

239  

240 Attributes 

241 ---------- 

242 name : str, optional 

243 Name of the linker. If the linking succeeds, the name is passed down to the generated :class:`ObjectCode`. 

244 arch : str, optional 

245 Pass the SM architecture value, such as ``sm_<CC>`` (for generating CUBIN) or 

246 ``compute_<CC>`` (for generating PTX). If not provided, the current device's architecture 

247 will be used. 

248 max_register_count : int, optional 

249 Maximum register count. 

250 time : bool, optional 

251 Print timing information to the info log. 

252 Default: False. 

253 verbose : bool, optional 

254 Print verbose messages to the info log. 

255 Default: False. 

256 link_time_optimization : bool, optional 

257 Perform link time optimization. 

258 Default: False. 

259 ptx : bool, optional 

260 Emit PTX after linking instead of CUBIN; only supported with ``link_time_optimization=True``. 

261 Default: False. 

262 optimization_level : int, optional 

263 Set optimization level. Only 0 and 3 are accepted. 

264 debug : bool, optional 

265 Generate debug information. 

266 Default: False. 

267 lineinfo : bool, optional 

268 Generate line information. 

269 Default: False. 

270 ftz : bool, optional 

271 Flush denormal values to zero. 

272 Default: False. 

273 prec_div : bool, optional 

274 Use precise division. 

275 Default: True. 

276 prec_sqrt : bool, optional 

277 Use precise square root. 

278 Default: True. 

279 fma : bool, optional 

280 Use fast multiply-add. 

281 Default: True. 

282 kernels_used : [str | tuple[str] | list[str]], optional 

283 Pass a kernel or sequence of kernels that are used; any not in the list can be removed. 

284 variables_used : [str | tuple[str] | list[str]], optional 

285 Pass a variable or sequence of variables that are used; any not in the list can be removed. 

286 optimize_unused_variables : bool, optional 

287 Assume that if a variable is not referenced in device code, it can be removed. 

288 Default: False. 

289 ptxas_options : [str | tuple[str] | list[str]], optional 

290 Pass options to PTXAS. 

291 split_compile : int, optional 

292 Split compilation maximum thread count. Use 0 to use all available processors. Value of 1 disables split 

293 compilation (default). 

294 Default: 1. 

295 split_compile_extended : int, optional 

296 A more aggressive form of split compilation available in LTO mode only. Accepts a maximum thread count value. 

297 Use 0 to use all available processors. Value of 1 disables extended split compilation (default). Note: This 

298 option can potentially impact performance of the compiled binary. 

299 Default: 1. 

300 no_cache : bool, optional 

301 Do not cache the intermediate steps of nvJitLink. 

302 Default: False. 

303 numba_debug : bool, optional 

304 Non-functional. ``numba_debug`` is an NVVM/NVRTC *compiler* option; 

305 neither nvJitLink nor the driver's cuLink API recognizes it, so no 

306 linking backend can honor it and the value is ignored. 

307 Default: None. 

308  

309 .. deprecated:: 1.2.0 

310 Setting this option emits a :class:`DeprecationWarning`. It has never 

311 had an effect on any linking backend and will be removed in 

312 ``cuda.core`` 2.0.0. Use 

313 :attr:`ProgramOptions.numba_debug` on an NVVM or NVRTC compilation 

314 path instead. 

315 """ 

316  

317 name: str | None = "<default linker>" 

318 arch: str | None = None 

319 max_register_count: int | None = None 

320 time: bool | None = None 

321 verbose: bool | None = None 

322 link_time_optimization: bool | None = None 

323 ptx: bool | None = None 

324 optimization_level: int | None = None 

325 debug: bool | None = None 

326 lineinfo: bool | None = None 

327 ftz: bool | None = None 

328 prec_div: bool | None = None 

329 prec_sqrt: bool | None = None 

330 fma: bool | None = None 

331 kernels_used: str | tuple[str] | list[str] | None = None 

332 variables_used: str | tuple[str] | list[str] | None = None 

333 optimize_unused_variables: bool | None = None 

334 ptxas_options: str | tuple[str] | list[str] | None = None 

335 split_compile: int | None = None 

336 split_compile_extended: int | None = None 

337 no_cache: bool | None = None 

338 numba_debug: bool | None = None 

339  

340 def __post_init__(self) -> None: 

341 _lazy_init() 1S:jTuWYCUDlt=?0%Z'!#X1234567)($98EFVmnocadefghbi

342 self._name = self.name.encode() 1S:jTuWYCUDlt=?0%Z'!#X1234567)($98EFVmnocadefghbi

343 # No linking backend reads ``numba_debug``, so warn where the value is 

344 # supplied rather than in the option builders -- the user learns once, 

345 # at the call site that set it, instead of once per link. The gate is 

346 # ``is not None`` (unlike the ignore-warning on the PTX compile path): 

347 # it is the *field* that is going away, so any explicit value earns the 

348 # notice, including ``False``. 

349 if self.numba_debug is not None: 1S:jTuWYCUDlt=?0%Z'!#X1234567)($98EFVmnocadefghbi

350 warn( 1Z!#

351 "numba_debug is not supported by any linking backend and is ignored. " 

352 "LinkerOptions.numba_debug is deprecated and will be removed in " 

353 "cuda.core 2.0.0; use ProgramOptions.numba_debug on an NVVM or NVRTC " 

354 "compilation path instead.", 

355 DeprecationWarning, 1Z!#

356 stacklevel=3, 

357 ) 

358  

359 def _prepare_nvjitlink_options(self, as_bytes: bool = False) -> list[bytes] | list[str]: 

360 options = [] 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

361  

362 if self.arch is not None: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

363 options.append(f"-arch={self.arch}") 1jTuWvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

364 else: 

365 options.append("-arch=sm_" + "".join(f"{i}" for i in Device().compute_capability)) 1R

366 if self.max_register_count is not None: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

367 options.append(f"-maxrregcount={self.max_register_count}") 1G0c

368 if self.time is not None: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

369 options.append("-time") 1xb

370 if self.verbose: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

371 options.append("-verbose") 1v

372 if self.link_time_optimization: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

373 options.append("-lto") 1l

374 if self.ptx: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

375 options.append("-ptx") 1Yl

376 if self.optimization_level is not None: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

377 options.append(f"-O{self.optimization_level}") 1H

378 if self.debug: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

379 options.append("-g") 1w0%'!#a

380 if self.lineinfo: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

381 options.append("-lineinfo") 1I%d

382 if self.ftz is not None: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

383 options.append(f"-ftz={'true' if self.ftz else 'false'}") 1M0e

384 if self.prec_div is not None: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

385 options.append(f"-prec-div={'true' if self.prec_div else 'false'}") 1Nf

386 if self.prec_sqrt is not None: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

387 options.append(f"-prec-sqrt={'true' if self.prec_sqrt else 'false'}") 1Og

388 if self.fma is not None: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

389 options.append(f"-fma={'true' if self.fma else 'false'}") 1Ph

390 if self.kernels_used is not None: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

391 if isinstance(self.kernels_used, str): 1yrz

392 options.append(f"-kernels-used={self.kernels_used}") 1y

393 elif isinstance(self.kernels_used, list): 1rz

394 for kernel in self.kernels_used: 1r

395 options.append(f"-kernels-used={kernel}") 1r

396 if self.variables_used is not None: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

397 if isinstance(self.variables_used, str): 1AsB

398 options.append(f"-variables-used={self.variables_used}") 1A

399 elif isinstance(self.variables_used, list): 1sB

400 for variable in self.variables_used: 1s

401 options.append(f"-variables-used={variable}") 1s

402 if self.optimize_unused_variables is not None: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

403 options.append("-optimize-unused-variables") 1J

404 if self.ptxas_options is not None: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

405 if isinstance(self.ptxas_options, str): 1pkq

406 options.append(f"-Xptxas={self.ptxas_options}") 1p

407 elif is_sequence(self.ptxas_options): 1kq

408 for opt in self.ptxas_options: 1kq

409 options.append(f"-Xptxas={opt}") 1kq

410 if self.split_compile is not None: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

411 options.append(f"-split-compile={self.split_compile}") 1Ki

412 if self.split_compile_extended is not None: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

413 options.append(f"-split-compile-extended={self.split_compile_extended}") 1L

414 if self.no_cache is True: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

415 options.append("-no-cache") 1Q

416  

417 if as_bytes: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0%'!#EFVmnocadefghbi

418 return [o.encode() for o in options] 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDlt0'!#EFVmnocadefghbi

419 else: 

420 return options 1%

421  

422 def _prepare_driver_options(self) -> tuple[list[object], list[object]]: 

423 formatted_options = [] 1ZX1234567)($98

424 option_keys = [] 1ZX1234567)($98

425  

426 # allocate a fixed-sized buffer for each info/error log 

427 size = 4194304 1ZX1234567)($98

428 formatted_options.extend((bytearray(size), size, bytearray(size), size)) 1ZX1234567)($98

429 option_keys.extend( 1ZX1234567)($98

430 ( 

431 _driver.CUjit_option.CU_JIT_INFO_LOG_BUFFER, 1ZX1234567)($98

432 _driver.CUjit_option.CU_JIT_INFO_LOG_BUFFER_SIZE_BYTES, 1ZX1234567)($98

433 _driver.CUjit_option.CU_JIT_ERROR_LOG_BUFFER, 1ZX1234567)($98

434 _driver.CUjit_option.CU_JIT_ERROR_LOG_BUFFER_SIZE_BYTES, 1ZX1234567)($98

435 ) 

436 ) 

437  

438 if self.arch is not None: 1ZX1234567)($98

439 arch = self.arch.split("_")[-1].upper() 1ZX

440 formatted_options.append(getattr(_driver.CUjit_target, f"CU_TARGET_COMPUTE_{arch}")) 1ZX

441 option_keys.append(_driver.CUjit_option.CU_JIT_TARGET) 1ZX

442 if self.max_register_count is not None: 1ZX1234567)($98

443 formatted_options.append(self.max_register_count) 1X

444 option_keys.append(_driver.CUjit_option.CU_JIT_MAX_REGISTERS) 1X

445 if self.time is not None: 1ZX1234567)($98

446 raise ValueError("time option is not supported by the driver API") 1)

447 if self.verbose: 1ZX1234567($98

448 formatted_options.append(1) 1X

449 option_keys.append(_driver.CUjit_option.CU_JIT_LOG_VERBOSE) 1X

450 if self.link_time_optimization: 1ZX1234567($98

451 formatted_options.append(1) 1X

452 option_keys.append(_driver.CUjit_option.CU_JIT_LTO) 1X

453 if self.ptx: 1ZX1234567($98

454 raise ValueError("ptx option is not supported by the driver API") 1(

455 if self.optimization_level is not None: 1ZX1234567$98

456 formatted_options.append(self.optimization_level) 1X

457 option_keys.append(_driver.CUjit_option.CU_JIT_OPTIMIZATION_LEVEL) 1X

458 if self.debug: 1ZX1234567$98

459 formatted_options.append(1) 1X

460 option_keys.append(_driver.CUjit_option.CU_JIT_GENERATE_DEBUG_INFO) 1X

461 if self.lineinfo: 1ZX1234567$98

462 formatted_options.append(1) 1X

463 option_keys.append(_driver.CUjit_option.CU_JIT_GENERATE_LINE_INFO) 1X

464 if self.ftz is not None: 1ZX1234567$98

465 warn("ftz option is deprecated in the driver API", DeprecationWarning, stacklevel=3) 11

466 if self.prec_div is not None: 1ZX1234567$98

467 warn("prec_div option is deprecated in the driver API", DeprecationWarning, stacklevel=3) 12

468 if self.prec_sqrt is not None: 1ZX1234567$98

469 warn("prec_sqrt option is deprecated in the driver API", DeprecationWarning, stacklevel=3) 13

470 if self.fma is not None: 1ZX1234567$98

471 warn("fma options is deprecated in the driver API", DeprecationWarning, stacklevel=3) 14

472 if self.kernels_used is not None: 1ZX1234567$98

473 warn("kernels_used is deprecated in the driver API", DeprecationWarning, stacklevel=3) 15

474 if self.variables_used is not None: 1ZX1234567$98

475 warn("variables_used is deprecated in the driver API", DeprecationWarning, stacklevel=3) 16

476 if self.optimize_unused_variables is not None: 1ZX1234567$98

477 warn("optimize_unused_variables is deprecated in the driver API", DeprecationWarning, stacklevel=3) 17

478 if self.ptxas_options is not None: 1ZX1234567$98

479 raise ValueError("ptxas_options option is not supported by the driver API") 1$

480 if self.split_compile is not None: 1ZX123456798

481 raise ValueError("split_compile option is not supported by the driver API") 19

482 if self.split_compile_extended is not None: 1ZX12345678

483 raise ValueError("split_compile_extended option is not supported by the driver API") 18

484 if self.no_cache is True: 1ZX1234567

485 formatted_options.append(_driver.CUjit_cacheMode.CU_JIT_CACHE_OPTION_NONE) 1X

486 option_keys.append(_driver.CUjit_option.CU_JIT_CACHE_MODE) 1X

487  

488 return formatted_options, option_keys 1ZX1234567

489  

490 def as_bytes(self, backend: str = "nvjitlink") -> list[bytes]: 

491 """Convert linker options to bytes format for the nvjitlink backend. 

492  

493 Parameters 

494 ---------- 

495 backend : str, optional 

496 The linker backend. Only "nvjitlink" is supported. Default is "nvjitlink". 

497  

498 Returns 

499 ------- 

500 list[bytes] 

501 List of option strings encoded as bytes. 

502  

503 Raises 

504 ------ 

505 ValueError 

506 If an unsupported backend is specified. 

507 RuntimeError 

508 If nvJitLink backend is not available. 

509 """ 

510 backend = backend.lower() 1:=?0

511 if backend != "nvjitlink": 1:=?0

512 raise ValueError(f"as_bytes() only supports 'nvjitlink' backend, got '{backend}'") 1=?

513 if not _use_nvjitlink_backend: 1:0

514 raise RuntimeError("nvJitLink backend is not available") 1:

515 return self._prepare_nvjitlink_options(as_bytes=True) 10

516  

517  

518# ============================================================================= 

519# Private implementation: cdef inline helpers 

520# ============================================================================= 

521  

522cdef inline int Linker_init(Linker self, tuple object_codes, object options) except -1: 

523 """Initialize a Linker instance.""" 

524 if len(object_codes) == 0: 1j@TuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDltEFVmnocadefghbi

525 raise ValueError("At least one ObjectCode object must be provided") 1@

526  

527 cdef cynvjitlink.nvJitLinkHandle c_raw_nvjitlink 

528 cdef cydriver.CUlinkState c_raw_culink 

529 cdef Py_ssize_t c_num_opts, i 

530 cdef vector[const_char_ptr] c_str_opts 

531 cdef cydriver.CUjit_option* c_drv_jit_keys_ptr 

532 cdef void** c_drv_jit_values_ptr 

533  

534 self._options = options = check_or_create_options(LinkerOptions, options, "Linker options") 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDltEFVmnocadefghbi

535  

536 if _use_nvjitlink_backend: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDltEFVmnocadefghbi

537 self._use_nvjitlink = True 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDltEFVmnocadefghbi

538 options_bytes = options._prepare_nvjitlink_options(as_bytes=True) 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDltEFVmnocadefghbi

539 c_num_opts = len(options_bytes) 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDltEFVmnocadefghbi

540 c_str_opts.resize(c_num_opts) 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDltEFVmnocadefghbi

541 for i in range(c_num_opts): 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDltEFVmnocadefghbi

542 c_str_opts[i] = <const char*>(<bytes>options_bytes[i]) 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDltEFVmnocadefghbi

543 with nogil: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDltEFVmnocadefghbi

544 HANDLE_RETURN_NVJITLINK(NULL, cynvjitlink.nvJitLinkCreate( 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQYCUDltEFVmnocadefghbi

545 &c_raw_nvjitlink, <uint32_t>c_num_opts, c_str_opts.data())) 

546 self._nvjitlink_handle = create_nvjitlink_handle(c_raw_nvjitlink) 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

547 else: 

548 self._use_nvjitlink = False 

549 formatted_options, option_keys = options._prepare_driver_options() 

550 # Keep the formatted_options list alive: it contains bytearrays that 

551 # the driver writes into via raw pointers during linking operations. 

552 self._drv_log_bufs = formatted_options 

553 c_num_opts = len(option_keys) 

554 self._drv_jit_keys.resize(c_num_opts) 

555 self._drv_jit_values.resize(c_num_opts) 

556 for i in range(c_num_opts): 

557 self._drv_jit_keys[i] = <cydriver.CUjit_option><int>option_keys[i] 

558 val = formatted_options[i] 

559 if isinstance(val, bytearray): 

560 self._drv_jit_values[i] = <void*>PyByteArray_AS_STRING(val) 

561 else: 

562 self._drv_jit_values[i] = <void*><intptr_t>int(val) 

563 c_drv_jit_keys_ptr = self._drv_jit_keys.data() 

564 c_drv_jit_values_ptr = self._drv_jit_values.data() 

565 try: 

566 with nogil: 

567 HANDLE_RETURN(cydriver.cuLinkCreate( 

568 <unsigned int>c_num_opts, 

569 c_drv_jit_keys_ptr, 

570 c_drv_jit_values_ptr, 

571 &c_raw_culink)) 

572 except CUDAError as e: 

573 Linker_annotate_error_log(self, e) 

574 raise 

575 self._culink_handle = create_culink_handle(c_raw_culink) 

576  

577 for code in object_codes: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

578 assert_type(code, ObjectCode) 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

579 Linker_add_code_object(self, code) 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

580 return 0 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

581  

582  

583cdef inline void Linker_add_code_object(Linker self, object object_code) except *: 

584 """Add a single ObjectCode to the linker.""" 

585 data = object_code.code 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

586 cdef cynvjitlink.nvJitLinkHandle c_nvjitlink_h 

587 cdef cydriver.CUlinkState c_culink_state 

588 cdef cynvjitlink.nvJitLinkInputType c_nv_input_type 

589 cdef cydriver.CUjitInputType c_drv_input_type 

590 cdef const char* c_data_ptr 

591 cdef size_t c_data_size 

592 cdef const char* c_file_ptr 

593  

594 name_bytes = f"{object_code.name}".encode() 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

595 cdef const char* c_name_ptr = <const char*>name_bytes 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

596  

597 input_types = _nvjitlink_input_types if self._use_nvjitlink else _driver_input_types 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

598 py_input_type = input_types.get(object_code.code_type) 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

599 if py_input_type is None: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

600 raise ValueError(f"Unknown code_type associated with ObjectCode: {object_code.code_type}") 

601  

602 if self._use_nvjitlink: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

603 c_nvjitlink_h = as_cu(self._nvjitlink_handle) 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

604 c_nv_input_type = <cynvjitlink.nvJitLinkInputType><int>py_input_type 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

605 if isinstance(data, bytes): 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

606 c_data_ptr = <const char*>(<bytes>data) 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

607 c_data_size = len(data) 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

608 with nogil: 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

609 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, cynvjitlink.nvJitLinkAddData( 1jTuWRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFVmnocadefghbi

610 c_nvjitlink_h, c_nv_input_type, <const void*>c_data_ptr, c_data_size, c_name_ptr)) 

611 elif isinstance(data, str): 

612 file_bytes = data.encode() 

613 c_file_ptr = <const char*>file_bytes 

614 with nogil: 

615 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, cynvjitlink.nvJitLinkAddFile( 

616 c_nvjitlink_h, c_nv_input_type, c_file_ptr)) 

617 else: 

618 raise TypeError(f"Expected bytes or str, but got {type(data).__name__}") 

619 else: 

620 c_culink_state = as_cu(self._culink_handle) 

621 c_drv_input_type = <cydriver.CUjitInputType><int>py_input_type 

622 try: 

623 if isinstance(data, bytes): 

624 c_data_ptr = <const char*>(<bytes>data) 

625 c_data_size = len(data) 

626 with nogil: 

627 HANDLE_RETURN(cydriver.cuLinkAddData( 

628 c_culink_state, c_drv_input_type, <void*>c_data_ptr, c_data_size, c_name_ptr, 

629 0, NULL, NULL)) 

630 elif isinstance(data, str): 

631 file_bytes = data.encode() 

632 c_file_ptr = <const char*>file_bytes 

633 with nogil: 

634 HANDLE_RETURN(cydriver.cuLinkAddFile( 

635 c_culink_state, c_drv_input_type, c_file_ptr, 0, NULL, NULL)) 

636 else: 

637 raise TypeError(f"Expected bytes or str, but got {type(data).__name__}") 

638 except CUDAError as e: 

639 Linker_annotate_error_log(self, e) 

640 raise 

641  

642  

643cdef inline object Linker_link(Linker self, str target_type): 

644 """Complete linking and return the result as ObjectCode.""" 

645 if target_type not in ("cubin", "ptx"): 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCUDltEFmnocadefghbi

646 raise ValueError(f"Unsupported target type: {target_type}") 1U

647  

648 cdef cynvjitlink.nvJitLinkHandle c_nvjitlink_h 

649 cdef cydriver.CUlinkState c_culink_state 

650 cdef size_t c_output_size = 0 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

651 cdef char* c_code_ptr 

652 cdef void* c_cubin_out = NULL 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

653  

654 if self._use_nvjitlink: 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

655 c_nvjitlink_h = as_cu(self._nvjitlink_handle) 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

656 with nogil: 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

657 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, cynvjitlink.nvJitLinkComplete(c_nvjitlink_h)) 1jTuRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

658 if target_type == "cubin": 1juRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

659 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, 1juRvGHwIxJpkqKLMNOPyrzAsBQCDtEFmnocadefghbi

660 cynvjitlink.nvJitLinkGetLinkedCubinSize(c_nvjitlink_h, &c_output_size)) 1juRvGHwIxJpkqKLMNOPyrzAsBQCDtEFmnocadefghbi

661 code = bytearray(c_output_size) 1juRvGHwIxJpkqKLMNOPyrzAsBQCDtEFmnocadefghbi

662 c_code_ptr = <char*>(<bytearray>code) 1juRvGHwIxJpkqKLMNOPyrzAsBQCDtEFmnocadefghbi

663 with nogil: 1juRvGHwIxJpkqKLMNOPyrzAsBQCDtEFmnocadefghbi

664 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, 1juRvGHwIxJpkqKLMNOPyrzAsBQCDtEFmnocadefghbi

665 cynvjitlink.nvJitLinkGetLinkedCubin(c_nvjitlink_h, c_code_ptr)) 1juRvGHwIxJpkqKLMNOPyrzAsBQCDtEFmnocadefghbi

666 else: 

667 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, 1l

668 cynvjitlink.nvJitLinkGetLinkedPtxSize(c_nvjitlink_h, &c_output_size)) 1l

669 code = bytearray(c_output_size) 1l

670 c_code_ptr = <char*>(<bytearray>code) 1l

671 with nogil: 1l

672 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, 1l

673 cynvjitlink.nvJitLinkGetLinkedPtx(c_nvjitlink_h, c_code_ptr)) 1l

674 else: 

675 c_culink_state = as_cu(self._culink_handle) 

676 try: 

677 with nogil: 

678 HANDLE_RETURN(cydriver.cuLinkComplete(c_culink_state, &c_cubin_out, &c_output_size)) 

679 except CUDAError as e: 

680 Linker_annotate_error_log(self, e) 

681 raise 

682 code = (<char*>c_cubin_out)[:c_output_size] 

683  

684 # Linking is complete; cache the decoded logs. cuLinkDestroy may still 

685 # dereference the raw log-buffer pointers, so retain them until close(). 

686 self._info_log = self.get_info_log() 1juRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

687 self._error_log = self.get_error_log() 1juRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

688  

689 return ObjectCode._init(bytes(code), target_type, name=self._options.name) 1juRvGHwIxJpkqKLMNOPyrzAsBQCDltEFmnocadefghbi

690  

691  

692cdef inline void Linker_annotate_error_log(Linker self, object e): 

693 """Annotate a CUDAError with the driver linker error log.""" 

694 error_log = self.get_error_log() 

695 if error_log: 

696 e.args = (e.args[0] + f"\nLinker error log: {error_log}", *e.args[1:]) 

697  

698  

699# ============================================================================= 

700# Private implementation: module-level state and initialization 

701# ============================================================================= 

702  

703# TODO: revisit this treatment for py313t builds 

704_driver = None # populated if nvJitLink cannot be used 

705_inited = False 

706_use_nvjitlink_backend = None # set by _decide_nvjitlink_or_driver() 

707  

708# Input type mappings populated by _lazy_init() with C-level enum ints. 

709_nvjitlink_input_types = None 

710_driver_input_types = None 

711  

712  

713def _nvjitlink_has_version_symbol(nvjitlink) -> bool: 

714 # This condition is equivalent to testing for version >= 12.3 

715 return bool(nvjitlink._inspect_function_pointer("__nvJitLinkVersion")) 

716  

717  

718# Note: this function is reused in the tests 

719def _decide_nvjitlink_or_driver() -> bool: 

720 """Return True if falling back to the cuLink* driver APIs.""" 

721 global _driver, _use_nvjitlink_backend 

722 if _use_nvjitlink_backend is not None: 2S + * [ ] R v G H w I x J p k q K L M N O P y r z A s B Q . - ; , ^ / V m n o c a d e f g h b i _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbub

723 return not _use_nvjitlink_backend 2S [ ] R v G H w I x J p k q K L M N O P y r z A s B Q V m n o c a d e f g h b i _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbub

724  

725 warn_txt_common = ( 

726 "the driver APIs will be used instead, which do not support" 1S+*.-;,^/

727 " minor version compatibility or linking LTO IRs." 

728 " For best results, consider upgrading to a recent version of" 

729 ) 

730  

731 nvjitlink_module = _optional_cuda_import("cuda.bindings.nvjitlink") 1S+*.-;,^/

732 if nvjitlink_module is None: 1S+*.-;,/

733 warn_txt = f"cuda.bindings.nvjitlink is not available, therefore {warn_txt_common} cuda-bindings." 1;

734 else: 

735 from cuda.bindings._internal import nvjitlink 1S+*.-,/

736  

737 try: 1S+*.-,/

738 has_version_symbol = _nvjitlink_has_version_symbol(nvjitlink) 1S+*.-,/

739 except DynamicLibNotFoundError: 1+-

740 warn_txt = ( 

741 f"cuda.bindings.nvjitlink is not available, therefore {warn_txt_common} cuda-bindings." 1+-

742 ) 

743 else: 

744 if has_version_symbol: 1S*.,/

745 _use_nvjitlink_backend = True 1S./

746 return False # Use nvjitlink 1S./

747 warn_txt = ( 

748 f"{'nvJitLink*.dll' if sys.platform == 'win32' else 'libnvJitLink.so*'} is too old (<12.3)." 1*,

749 f" Therefore cuda.bindings.nvjitlink is not usable and {warn_txt_common} nvJitLink." 1*,

750 ) 

751  

752 warn(warn_txt, stacklevel=2, category=RuntimeWarning) 1+*-;,

753 _driver = driver 1+*-;,

754 _use_nvjitlink_backend = False 1+*-;,

755 return True 1+*-;,

756  

757  

758def _lazy_init() -> None: 

759 global _inited, _nvjitlink_input_types, _driver_input_types 

760 if _inited: 1S:jTuWYCUDlt=?0%Z'!#X1234567)($98EFVmnocadefghbi

761 return 1S:jTuWYCUDlt=?0%Z'!#X1234567)($98EFVmnocadefghbi

762  

763 _decide_nvjitlink_or_driver() 

764 if _use_nvjitlink_backend: 

765 _nvjitlink_input_types = { 

766 "ptx": <int>cynvjitlink.NVJITLINK_INPUT_PTX, 

767 "cubin": <int>cynvjitlink.NVJITLINK_INPUT_CUBIN, 

768 "fatbin": <int>cynvjitlink.NVJITLINK_INPUT_FATBIN, 

769 "ltoir": <int>cynvjitlink.NVJITLINK_INPUT_LTOIR, 

770 "object": <int>cynvjitlink.NVJITLINK_INPUT_OBJECT, 

771 "library": <int>cynvjitlink.NVJITLINK_INPUT_LIBRARY, 

772 } 

773 else: 

774 _driver_input_types = { 

775 "ptx": <int>cydriver.CU_JIT_INPUT_PTX, 

776 "cubin": <int>cydriver.CU_JIT_INPUT_CUBIN, 

777 "fatbin": <int>cydriver.CU_JIT_INPUT_FATBINARY, 

778 "object": <int>cydriver.CU_JIT_INPUT_OBJECT, 

779 "library": <int>cydriver.CU_JIT_INPUT_LIBRARY, 

780 } 

781 _inited = True