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

374 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-07-29 01:38 +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._optional_cuda_import import _optional_cuda_import 

33from cuda.core._device import Device 

34from cuda.core._module import ObjectCode 

35from cuda.core._utils.clear_error_support import assert_type 

36from cuda.core._utils.cuda_utils import ( 

37 CUDAError, 

38 check_or_create_options, 

39 driver, 

40 is_sequence, 

41) 

42from cuda.core.typing import CompilerBackendType, ObjectCodeFormatType 

43  

44if TYPE_CHECKING: 

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

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

47  

48# Module-level annotations to ensure stubgen-pyx keeps the above imports in 

49# the generated `.pyi` so that the LinkerHandleT forward references resolve. 

50# These names are not assigned, so they only affect __annotations__. 

51_keep_driver_in_stub: "cuda.bindings.driver.CUlinkState" 

52_keep_nvjitlink_in_stub: "cuda.bindings.nvjitlink.nvJitLinkHandle" 

53  

54ctypedef const char* const_char_ptr 

55  

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

57  

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

59  

60  

61# ============================================================================= 

62# Principal class 

63# ============================================================================= 

64  

65cdef class Linker: 

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

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

68  

69 This object provides a unified interface to multiple underlying 

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

71  

72 Parameters 

73 ---------- 

74 object_codes : :class:`~cuda.core.ObjectCode` 

75 One or more ObjectCode objects to be linked. 

76 options : :class:`LinkerOptions`, optional 

77 Options for the linker. If not provided, default options will be used. 

78 """ 

79  

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

81 Linker_init(self, object_codes, options) 1(QuTOqzArBsCmknDEFGHIvowxpyJVKSLltMNRcdefghiabj

82  

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

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

85  

86 Parameters 

87 ---------- 

88 target_type : ObjectCodeFormatType | str 

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

90  

91 Returns 

92 ------- 

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

94 The linked object code of the specified target type. 

95  

96 .. note:: 

97  

98 Ensure that input object codes were compiled with appropriate 

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

100 """ 

101 return Linker_link(self, str(target_type)) 1QuOqzArBsCmknDEFGHIvowxpyJKSLltMNcdefghiabj

102  

103 def get_error_log(self) -> str: 

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

105  

106 Returns 

107 ------- 

108 str 

109 The error log. 

110 """ 

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

112 if self._error_log is not None: 1QuOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

113 return self._error_log 1t

114 cdef cynvjitlink.nvJitLinkHandle c_h 

115 cdef size_t c_log_size = 0 1PQuOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

116 cdef char* c_log_ptr 

117 if self._use_nvjitlink: 1QuOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

118 c_h = as_cu(self._nvjitlink_handle) 1QuOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

119 HANDLE_RETURN_NVJITLINK(c_h, cynvjitlink.nvJitLinkGetErrorLogSize(c_h, &c_log_size)) 1QuOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

120 log = bytearray(c_log_size) 1QuOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

121 if c_log_size > 0: 1QuOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

122 c_log_ptr = <char*>(<bytearray>log) 1Q

123 HANDLE_RETURN_NVJITLINK(c_h, cynvjitlink.nvJitLinkGetErrorLog(c_h, c_log_ptr)) 1Q

124 return log.decode("utf-8", errors="backslashreplace") 1QuOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

125 else: 

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

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

128  

129 def get_info_log(self) -> str: 

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

131  

132 Returns 

133 ------- 

134 str 

135 The info log. 

136 """ 

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

138 if self._info_log is not None: 1uOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

139 return self._info_log 1ut

140 cdef cynvjitlink.nvJitLinkHandle c_h 

141 cdef size_t c_log_size = 0 1uOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

142 cdef char* c_log_ptr 

143 if self._use_nvjitlink: 1uOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

144 c_h = as_cu(self._nvjitlink_handle) 1uOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

145 HANDLE_RETURN_NVJITLINK(c_h, cynvjitlink.nvJitLinkGetInfoLogSize(c_h, &c_log_size)) 1uOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

146 log = bytearray(c_log_size) 1uOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

147 if c_log_size > 0: 1uOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

148 c_log_ptr = <char*>(<bytearray>log) 1qrsmklab

149 HANDLE_RETURN_NVJITLINK(c_h, cynvjitlink.nvJitLinkGetInfoLog(c_h, c_log_ptr)) 1qrsmklab

150 return log.decode("utf-8", errors="backslashreplace") 1uOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

151 else: 

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

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

154  

155 def close(self) -> None: 

156 """Destroy this linker.""" 

157 cdef vector[cydriver.CUjit_option] empty_keys 

158 cdef vector[void*] empty_values 

159 if self._use_nvjitlink: 1Rcdefghiabj

160 self._nvjitlink_handle.reset() 1Rcdefghiabj

161 else: 

162 if self._drv_log_bufs is not None: 

163 if self._info_log is None: 

164 self._info_log = self.get_info_log() 

165 if self._error_log is None: 

166 self._error_log = self.get_error_log() 

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

168 self._culink_handle.reset() 

169 self._drv_jit_keys.swap(empty_keys) 

170 self._drv_jit_values.swap(empty_values) 

171 self._drv_log_bufs = None 

172  

173 @property 

174 def handle(self) -> LinkerHandleT: 

175 """Return the underlying handle object. 

176  

177 .. note:: 

178  

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

180  

181 .. caution:: 

182  

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

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

185 """ 

186 if self._use_nvjitlink: 1TR

187 return as_py(self._nvjitlink_handle) 1TR

188 else: 

189 return as_py(self._culink_handle) 

190  

191 @classmethod 

192 def which_backend(cls) -> CompilerBackendType: 

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

194  

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

196 library is available and meets the minimum version requirement, 

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

198  

199 .. note:: 

200  

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

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

203 example: choose PTX vs. LTOIR before constructing a 

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

205 detail whose support matrix may shift across CTK releases. 

206 """ 

207 return CompilerBackendType.DRIVER if _decide_nvjitlink_or_driver() else CompilerBackendType.NVJITLINK 2gb) * O q z A r B s C m k n D E F G H I v o w x p y J R c d e f g h i a b j

208  

209  

210# ============================================================================= 

211# Supporting classes 

212# ============================================================================= 

213  

214@dataclass 

215class LinkerOptions: 

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

217  

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

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

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

221  

222 Attributes 

223 ---------- 

224 name : str, optional 

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

226 arch : str, optional 

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

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

229 will be used. 

230 max_register_count : int, optional 

231 Maximum register count. 

232 time : bool, optional 

233 Print timing information to the info log. 

234 Default: False. 

235 verbose : bool, optional 

236 Print verbose messages to the info log. 

237 Default: False. 

238 link_time_optimization : bool, optional 

239 Perform link time optimization. 

240 Default: False. 

241 ptx : bool, optional 

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

243 Default: False. 

244 optimization_level : int, optional 

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

246 debug : bool, optional 

247 Generate debug information. 

248 Default: False. 

249 lineinfo : bool, optional 

250 Generate line information. 

251 Default: False. 

252 ftz : bool, optional 

253 Flush denormal values to zero. 

254 Default: False. 

255 prec_div : bool, optional 

256 Use precise division. 

257 Default: True. 

258 prec_sqrt : bool, optional 

259 Use precise square root. 

260 Default: True. 

261 fma : bool, optional 

262 Use fast multiply-add. 

263 Default: True. 

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

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

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

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

268 optimize_unused_variables : bool, optional 

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

270 Default: False. 

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

272 Pass options to PTXAS. 

273 split_compile : int, optional 

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

275 compilation (default). 

276 Default: 1. 

277 split_compile_extended : int, optional 

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

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

280 option can potentially impact performance of the compiled binary. 

281 Default: 1. 

282 no_cache : bool, optional 

283 Do not cache the intermediate steps of nvJitLink. 

284 Default: False. 

285 """ 

286  

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

288 arch: str | None = None 

289 max_register_count: int | None = None 

290 time: bool | None = None 

291 verbose: bool | None = None 

292 link_time_optimization: bool | None = None 

293 ptx: bool | None = None 

294 optimization_level: int | None = None 

295 debug: bool | None = None 

296 lineinfo: bool | None = None 

297 ftz: bool | None = None 

298 prec_div: bool | None = None 

299 prec_sqrt: bool | None = None 

300 fma: bool | None = None 

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

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

303 optimize_unused_variables: bool | None = None 

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

305 split_compile: int | None = None 

306 split_compile_extended: int | None = None 

307 no_cache: bool | None = None 

308 numba_debug: bool | None = None 

309  

310 def __post_init__(self) -> None: 

311 _lazy_init() 1P#QuTVKSLlt$%W7UXYZ012398654MNRcdefghiabj

312 self._name = self.name.encode() 1P#QuTVKSLlt$%W7UXYZ012398654MNRcdefghiabj

313  

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

315 options = [] 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

316  

317 if self.arch is not None: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

318 options.append(f"-arch={self.arch}") 1QuTqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

319 else: 

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

321 if self.max_register_count is not None: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

322 options.append(f"-maxrregcount={self.max_register_count}") 1zWc

323 if self.time is not None: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

324 options.append("-time") 1sb

325 if self.verbose: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

326 options.append("-verbose") 1q

327 if self.link_time_optimization: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

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

329 if self.ptx: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

330 options.append("-ptx") 1Vl

331 if self.optimization_level is not None: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

332 options.append(f"-O{self.optimization_level}") 1A

333 if self.debug: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

334 options.append("-g") 1rW7da

335 if self.lineinfo: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

336 options.append("-lineinfo") 1B7e

337 if self.ftz is not None: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

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

339 if self.prec_div is not None: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

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

341 if self.prec_sqrt is not None: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

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

343 if self.fma is not None: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

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

345 if self.kernels_used is not None: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

346 if isinstance(self.kernels_used, str): 1vow

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

348 elif isinstance(self.kernels_used, list): 1ow

349 for kernel in self.kernels_used: 1o

350 options.append(f"-kernels-used={kernel}") 1o

351 if self.variables_used is not None: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

352 if isinstance(self.variables_used, str): 1xpy

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

354 elif isinstance(self.variables_used, list): 1py

355 for variable in self.variables_used: 1p

356 options.append(f"-variables-used={variable}") 1p

357 if self.optimize_unused_variables is not None: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

358 options.append("-optimize-unused-variables") 1C

359 if self.ptxas_options is not None: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

360 if isinstance(self.ptxas_options, str): 1mkn

361 options.append(f"-Xptxas={self.ptxas_options}") 1m

362 elif is_sequence(self.ptxas_options): 1kn

363 for opt in self.ptxas_options: 1kn

364 options.append(f"-Xptxas={opt}") 1kn

365 if self.split_compile is not None: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

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

367 if self.split_compile_extended is not None: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

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

369 if self.no_cache is True: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

370 options.append("-no-cache") 1J

371  

372 if as_bytes: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltW7MNRcdefghiabj

373 return [o.encode() for o in options] 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltWMNRcdefghiabj

374 else: 

375 return options 17

376  

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

378 formatted_options = [] 1UXYZ012398654

379 option_keys = [] 1UXYZ012398654

380  

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

382 size = 4194304 1UXYZ012398654

383 formatted_options.extend((bytearray(size), size, bytearray(size), size)) 1UXYZ012398654

384 option_keys.extend( 1UXYZ012398654

385 ( 

386 _driver.CUjit_option.CU_JIT_INFO_LOG_BUFFER, 1UXYZ012398654

387 _driver.CUjit_option.CU_JIT_INFO_LOG_BUFFER_SIZE_BYTES, 1UXYZ012398654

388 _driver.CUjit_option.CU_JIT_ERROR_LOG_BUFFER, 1UXYZ012398654

389 _driver.CUjit_option.CU_JIT_ERROR_LOG_BUFFER_SIZE_BYTES, 1UXYZ012398654

390 ) 

391 ) 

392  

393 if self.arch is not None: 1UXYZ012398654

394 arch = self.arch.split("_")[-1].upper() 1U

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

396 option_keys.append(_driver.CUjit_option.CU_JIT_TARGET) 1U

397 if self.max_register_count is not None: 1UXYZ012398654

398 formatted_options.append(self.max_register_count) 1U

399 option_keys.append(_driver.CUjit_option.CU_JIT_MAX_REGISTERS) 1U

400 if self.time is not None: 1UXYZ012398654

401 raise ValueError("time option is not supported by the driver API") 19

402 if self.verbose: 1UXYZ01238654

403 formatted_options.append(1) 1U

404 option_keys.append(_driver.CUjit_option.CU_JIT_LOG_VERBOSE) 1U

405 if self.link_time_optimization: 1UXYZ01238654

406 formatted_options.append(1) 1U

407 option_keys.append(_driver.CUjit_option.CU_JIT_LTO) 1U

408 if self.ptx: 1UXYZ01238654

409 raise ValueError("ptx option is not supported by the driver API") 18

410 if self.optimization_level is not None: 1UXYZ0123654

411 formatted_options.append(self.optimization_level) 1U

412 option_keys.append(_driver.CUjit_option.CU_JIT_OPTIMIZATION_LEVEL) 1U

413 if self.debug: 1UXYZ0123654

414 formatted_options.append(1) 1U

415 option_keys.append(_driver.CUjit_option.CU_JIT_GENERATE_DEBUG_INFO) 1U

416 if self.lineinfo: 1UXYZ0123654

417 formatted_options.append(1) 1U

418 option_keys.append(_driver.CUjit_option.CU_JIT_GENERATE_LINE_INFO) 1U

419 if self.ftz is not None: 1UXYZ0123654

420 warn("ftz option is deprecated in the driver API", DeprecationWarning, stacklevel=3) 1X

421 if self.prec_div is not None: 1UXYZ0123654

422 warn("prec_div option is deprecated in the driver API", DeprecationWarning, stacklevel=3) 1Y

423 if self.prec_sqrt is not None: 1UXYZ0123654

424 warn("prec_sqrt option is deprecated in the driver API", DeprecationWarning, stacklevel=3) 1Z

425 if self.fma is not None: 1UXYZ0123654

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

427 if self.kernels_used is not None: 1UXYZ0123654

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

429 if self.variables_used is not None: 1UXYZ0123654

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

431 if self.optimize_unused_variables is not None: 1UXYZ0123654

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

433 if self.ptxas_options is not None: 1UXYZ0123654

434 raise ValueError("ptxas_options option is not supported by the driver API") 16

435 if self.split_compile is not None: 1UXYZ012354

436 raise ValueError("split_compile option is not supported by the driver API") 15

437 if self.split_compile_extended is not None: 1UXYZ01234

438 raise ValueError("split_compile_extended option is not supported by the driver API") 14

439 if self.no_cache is True: 1UXYZ0123

440 formatted_options.append(_driver.CUjit_cacheMode.CU_JIT_CACHE_OPTION_NONE) 1U

441 option_keys.append(_driver.CUjit_option.CU_JIT_CACHE_MODE) 1U

442  

443 return formatted_options, option_keys 1UXYZ0123

444  

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

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

447  

448 Parameters 

449 ---------- 

450 backend : str, optional 

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

452  

453 Returns 

454 ------- 

455 list[bytes] 

456 List of option strings encoded as bytes. 

457  

458 Raises 

459 ------ 

460 ValueError 

461 If an unsupported backend is specified. 

462 RuntimeError 

463 If nvJitLink backend is not available. 

464 """ 

465 backend = backend.lower() 1#$%W

466 if backend != "nvjitlink": 1#$%W

467 raise ValueError(f"as_bytes() only supports 'nvjitlink' backend, got '{backend}'") 1$%

468 if not _use_nvjitlink_backend: 1#W

469 raise RuntimeError("nvJitLink backend is not available") 1#

470 return self._prepare_nvjitlink_options(as_bytes=True) 1W

471  

472  

473# ============================================================================= 

474# Private implementation: cdef inline helpers 

475# ============================================================================= 

476  

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

478 """Initialize a Linker instance.""" 

479 if len(object_codes) == 0: 1(QuTOqzArBsCmknDEFGHIvowxpyJVKSLltMNRcdefghiabj

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

481  

482 cdef cynvjitlink.nvJitLinkHandle c_raw_nvjitlink 

483 cdef cydriver.CUlinkState c_raw_culink 

484 cdef Py_ssize_t c_num_opts, i 

485 cdef vector[const_char_ptr] c_str_opts 

486 cdef cydriver.CUjit_option* c_drv_jit_keys_ptr 

487 cdef void** c_drv_jit_values_ptr 

488  

489 self._options = options = check_or_create_options(LinkerOptions, options, "Linker options") 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltMNRcdefghiabj

490  

491 if _use_nvjitlink_backend: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltMNRcdefghiabj

492 self._use_nvjitlink = True 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltMNRcdefghiabj

493 options_bytes = options._prepare_nvjitlink_options(as_bytes=True) 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltMNRcdefghiabj

494 c_num_opts = len(options_bytes) 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltMNRcdefghiabj

495 c_str_opts.resize(c_num_opts) 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltMNRcdefghiabj

496 for i in range(c_num_opts): 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltMNRcdefghiabj

497 c_str_opts[i] = <const char*>(<bytes>options_bytes[i]) 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltMNRcdefghiabj

498 with nogil: 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltMNRcdefghiabj

499 HANDLE_RETURN_NVJITLINK(NULL, cynvjitlink.nvJitLinkCreate( 1QuTOqzArBsCmknDEFGHIvowxpyJVKSLltMNRcdefghiabj

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

501 self._nvjitlink_handle = create_nvjitlink_handle(c_raw_nvjitlink) 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

502 else: 

503 self._use_nvjitlink = False 

504 formatted_options, option_keys = options._prepare_driver_options() 

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

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

507 self._drv_log_bufs = formatted_options 

508 c_num_opts = len(option_keys) 

509 self._drv_jit_keys.resize(c_num_opts) 

510 self._drv_jit_values.resize(c_num_opts) 

511 for i in range(c_num_opts): 

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

513 val = formatted_options[i] 

514 if isinstance(val, bytearray): 

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

516 else: 

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

518 c_drv_jit_keys_ptr = self._drv_jit_keys.data() 

519 c_drv_jit_values_ptr = self._drv_jit_values.data() 

520 try: 

521 with nogil: 

522 HANDLE_RETURN(cydriver.cuLinkCreate( 

523 <unsigned int>c_num_opts, 

524 c_drv_jit_keys_ptr, 

525 c_drv_jit_values_ptr, 

526 &c_raw_culink)) 

527 except CUDAError as e: 

528 Linker_annotate_error_log(self, e) 

529 raise 

530 self._culink_handle = create_culink_handle(c_raw_culink) 

531  

532 for code in object_codes: 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

533 assert_type(code, ObjectCode) 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

534 Linker_add_code_object(self, code) 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

535 return 0 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

536  

537  

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

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

540 data = object_code.code 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

541 cdef cynvjitlink.nvJitLinkHandle c_nvjitlink_h 

542 cdef cydriver.CUlinkState c_culink_state 

543 cdef cynvjitlink.nvJitLinkInputType c_nv_input_type 

544 cdef cydriver.CUjitInputType c_drv_input_type 

545 cdef const char* c_data_ptr 

546 cdef size_t c_data_size 

547 cdef const char* c_file_ptr 

548  

549 name_bytes = f"{object_code.name}".encode() 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

550 cdef const char* c_name_ptr = <const char*>name_bytes 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

551  

552 input_types = _nvjitlink_input_types if self._use_nvjitlink else _driver_input_types 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

553 py_input_type = input_types.get(object_code.code_type) 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

554 if py_input_type is None: 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

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

556  

557 if self._use_nvjitlink: 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

558 c_nvjitlink_h = as_cu(self._nvjitlink_handle) 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

559 c_nv_input_type = <cynvjitlink.nvJitLinkInputType><int>py_input_type 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

560 if isinstance(data, bytes): 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

561 c_data_ptr = <const char*>(<bytes>data) 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

562 c_data_size = len(data) 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

563 with nogil: 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

564 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, cynvjitlink.nvJitLinkAddData( 1QuTOqzArBsCmknDEFGHIvowxpyJKSLltMNRcdefghiabj

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

566 elif isinstance(data, str): 

567 file_bytes = data.encode() 

568 c_file_ptr = <const char*>file_bytes 

569 with nogil: 

570 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, cynvjitlink.nvJitLinkAddFile( 

571 c_nvjitlink_h, c_nv_input_type, c_file_ptr)) 

572 else: 

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

574 else: 

575 c_culink_state = as_cu(self._culink_handle) 

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

577 try: 

578 if isinstance(data, bytes): 

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

580 c_data_size = len(data) 

581 with nogil: 

582 HANDLE_RETURN(cydriver.cuLinkAddData( 

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

584 0, NULL, NULL)) 

585 elif isinstance(data, str): 

586 file_bytes = data.encode() 

587 c_file_ptr = <const char*>file_bytes 

588 with nogil: 

589 HANDLE_RETURN(cydriver.cuLinkAddFile( 

590 c_culink_state, c_drv_input_type, c_file_ptr, 0, NULL, NULL)) 

591 else: 

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

593 except CUDAError as e: 

594 Linker_annotate_error_log(self, e) 

595 raise 

596  

597  

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

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

600 if target_type not in ("cubin", "ptx"): 1QuOqzArBsCmknDEFGHIvowxpyJKSLltMNcdefghiabj

601 raise ValueError(f"Unsupported target type: {target_type}") 1S

602  

603 cdef cynvjitlink.nvJitLinkHandle c_nvjitlink_h 

604 cdef cydriver.CUlinkState c_culink_state 

605 cdef size_t c_output_size = 0 1QuOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

606 cdef char* c_code_ptr 

607 cdef void* c_cubin_out = NULL 1QuOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

608  

609 if self._use_nvjitlink: 1QuOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

610 c_nvjitlink_h = as_cu(self._nvjitlink_handle) 1QuOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

611 with nogil: 1QuOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

612 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, cynvjitlink.nvJitLinkComplete(c_nvjitlink_h)) 1QuOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

613 if target_type == "cubin": 1uOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

614 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, 1uOqzArBsCmknDEFGHIvowxpyJKLtMNcdefghiabj

615 cynvjitlink.nvJitLinkGetLinkedCubinSize(c_nvjitlink_h, &c_output_size)) 1uOqzArBsCmknDEFGHIvowxpyJKLtMNcdefghiabj

616 code = bytearray(c_output_size) 1uOqzArBsCmknDEFGHIvowxpyJKLtMNcdefghiabj

617 c_code_ptr = <char*>(<bytearray>code) 1uOqzArBsCmknDEFGHIvowxpyJKLtMNcdefghiabj

618 with nogil: 1uOqzArBsCmknDEFGHIvowxpyJKLtMNcdefghiabj

619 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, 1uOqzArBsCmknDEFGHIvowxpyJKLtMNcdefghiabj

620 cynvjitlink.nvJitLinkGetLinkedCubin(c_nvjitlink_h, c_code_ptr)) 1uOqzArBsCmknDEFGHIvowxpyJKLtMNcdefghiabj

621 else: 

622 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, 1l

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

624 code = bytearray(c_output_size) 1l

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

626 with nogil: 1l

627 HANDLE_RETURN_NVJITLINK(c_nvjitlink_h, 1l

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

629 else: 

630 c_culink_state = as_cu(self._culink_handle) 

631 try: 

632 with nogil: 

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

634 except CUDAError as e: 

635 Linker_annotate_error_log(self, e) 

636 raise 

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

638  

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

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

641 self._info_log = self.get_info_log() 1uOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

642 self._error_log = self.get_error_log() 1uOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

643  

644 return ObjectCode._init(bytes(code), target_type, name=self._options.name) 1uOqzArBsCmknDEFGHIvowxpyJKLltMNcdefghiabj

645  

646  

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

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

649 error_log = self.get_error_log() 

650 if error_log: 

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

652  

653  

654# ============================================================================= 

655# Private implementation: module-level state and initialization 

656# ============================================================================= 

657  

658# TODO: revisit this treatment for py313t builds 

659_driver = None # populated if nvJitLink cannot be used 

660_inited = False 

661_use_nvjitlink_backend = None # set by _decide_nvjitlink_or_driver() 

662  

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

664_nvjitlink_input_types = None 

665_driver_input_types = None 

666  

667  

668def _nvjitlink_has_version_symbol(nvjitlink) -> bool: 

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

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

671  

672  

673# Note: this function is reused in the tests 

674def _decide_nvjitlink_or_driver() -> bool: 

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

676 global _driver, _use_nvjitlink_backend 

677 if _use_nvjitlink_backend is not None: 2P ) * O q z A r B s C m k n D E F G H I v o w x p y J ! ' R c d e f g h i a b j + , - . / : ; = ? @ [ ] ^ _ ` { | } ~ abbbcbdbebfb

678 return not _use_nvjitlink_backend 2P ) * O q z A r B s C m k n D E F G H I v o w x p y J R c d e f g h i a b j + , - . / : ; = ? @ [ ] ^ _ ` { | } ~ abbbcbdbebfb

679  

680 warn_txt_common = ( 

681 "the driver APIs will be used instead, which do not support" 1P!'

682 " minor version compatibility or linking LTO IRs." 

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

684 ) 

685  

686 nvjitlink_module = _optional_cuda_import( 1P!'

687 "cuda.bindings.nvjitlink", 

688 probe_function=lambda module: module.version(), # probe triggers nvJitLink runtime load 1P!'

689 ) 

690 if nvjitlink_module is None: 1P!

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

692 else: 

693 from cuda.bindings._internal import nvjitlink 

694  

695 if _nvjitlink_has_version_symbol(nvjitlink): 

696 _use_nvjitlink_backend = True 

697 return False # Use nvjitlink 

698 warn_txt = ( 

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

700 f" Therefore cuda.bindings.nvjitlink is not usable and {warn_txt_common} nvJitLink." 

701 ) 

702  

703 warn(warn_txt, stacklevel=2, category=RuntimeWarning) 1!

704 _use_nvjitlink_backend = False 1!

705 _driver = driver 1!

706 return True 1!

707  

708  

709def _lazy_init() -> None: 

710 global _inited, _nvjitlink_input_types, _driver_input_types 

711 if _inited: 1P#QuTVKSLlt$%W7UXYZ012398654MNRcdefghiabj

712 return 1P#QuTVKSLlt$%W7UXYZ012398654MNRcdefghiabj

713  

714 _decide_nvjitlink_or_driver() 

715 if _use_nvjitlink_backend: 

716 _nvjitlink_input_types = { 

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

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

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

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

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

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

723 } 

724 else: 

725 _driver_input_types = { 

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

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

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

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

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

731 } 

732 _inited = True