Skip to content

Commit db42da1

Browse files
committed
allow ObjectCode to have a name
1 parent a8285b0 commit db42da1

File tree

6 files changed

+78
-27
lines changed

6 files changed

+78
-27
lines changed

cuda_core/cuda/core/experimental/_linker.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ class LinkerOptions:
100100
101101
Attributes
102102
----------
103+
name : str, optional
104+
Name of the linker. If the linking succeeds, the name is passed down to the generated `ObjectCode`.
103105
arch : str, optional
104106
Pass the SM architecture value, such as ``sm_<CC>`` (for generating CUBIN) or
105107
``compute_<CC>`` (for generating PTX). If not provided, the current device's architecture
@@ -161,6 +163,7 @@ class LinkerOptions:
161163
Default: False.
162164
"""
163165

166+
name: Optional[str] = "<default linker>"
164167
arch: Optional[str] = None
165168
max_register_count: Optional[int] = None
166169
time: Optional[bool] = None
@@ -184,6 +187,7 @@ class LinkerOptions:
184187

185188
def __post_init__(self):
186189
_lazy_init()
190+
self._name = self.name.encode()
187191
self.formatted_options = []
188192
if _nvjitlink:
189193
self._init_nvjitlink()
@@ -393,7 +397,7 @@ def _add_code_object(self, object_code: ObjectCode):
393397
data = object_code._module
394398
assert_type(data, bytes)
395399
with _exception_manager(self):
396-
name_str = f"{object_code._handle}_{object_code._code_type}"
400+
name_str = f"{object_code.name}"
397401
if _nvjitlink:
398402
_nvjitlink.add_data(
399403
self._mnff.handle,
@@ -455,7 +459,7 @@ def link(self, target_type) -> ObjectCode:
455459
addr, size = handle_return(_driver.cuLinkComplete(self._mnff.handle))
456460
code = (ctypes.c_char * size).from_address(addr)
457461

458-
return ObjectCode._init(bytes(code), target_type)
462+
return ObjectCode._init(bytes(code), target_type, name=self._options.name)
459463

460464
def get_error_log(self) -> str:
461465
"""Get the error log generated by the linker.

cuda_core/cuda/core/experimental/_module.py

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ class ObjectCode:
449449
context.
450450
"""
451451

452-
__slots__ = ("_handle", "_backend_version", "_code_type", "_module", "_loader", "_sym_map")
452+
__slots__ = ("_handle", "_backend_version", "_code_type", "_module", "_loader", "_sym_map", "_name")
453453
_supported_code_type = ("cubin", "ptx", "ltoir", "fatbin", "object", "library")
454454

455455
def __new__(self, *args, **kwargs):
@@ -459,7 +459,9 @@ def __new__(self, *args, **kwargs):
459459
)
460460

461461
@classmethod
462-
def _init(cls, module, code_type, *, symbol_mapping: Optional[dict] = None):
462+
def _init(cls, module, code_type, *,
463+
name: str = "",
464+
symbol_mapping: Optional[dict] = None):
463465
self = super().__new__(cls)
464466
assert code_type in self._supported_code_type, f"{code_type=} is not supported"
465467
_lazy_init()
@@ -473,112 +475,137 @@ def _init(cls, module, code_type, *, symbol_mapping: Optional[dict] = None):
473475
self._code_type = code_type
474476
self._module = module
475477
self._sym_map = {} if symbol_mapping is None else symbol_mapping
478+
self._name = name
476479

477480
return self
478481

479482
@classmethod
480-
def _reduce_helper(self, module, code_type, symbol_mapping):
483+
def _reduce_helper(self, module, code_type, name, symbol_mapping):
481484
# just for forwarding kwargs
482-
return ObjectCode._init(module, code_type, symbol_mapping=symbol_mapping)
485+
return ObjectCode._init(module, code_type, name=name, symbol_mapping=symbol_mapping)
483486

484487
def __reduce__(self):
485-
return ObjectCode._reduce_helper, (self._module, self._code_type, self._sym_map)
488+
return ObjectCode._reduce_helper, (self._module, self._code_type, self._name, self._sym_map)
486489

487490
@staticmethod
488-
def from_cubin(module: Union[bytes, str], *, symbol_mapping: Optional[dict] = None) -> "ObjectCode":
491+
def from_cubin(module: Union[bytes, str], *,
492+
name: str = "",
493+
symbol_mapping: Optional[dict] = None) -> "ObjectCode":
489494
"""Create an :class:`ObjectCode` instance from an existing cubin.
490495
491496
Parameters
492497
----------
493498
module : Union[bytes, str]
494499
Either a bytes object containing the in-memory cubin to load, or
495500
a file path string pointing to the on-disk cubin to load.
501+
name : Optional[str]
502+
A human-readable identifier representing this code object.
496503
symbol_mapping : Optional[dict]
497504
A dictionary specifying how the unmangled symbol names (as keys)
498505
should be mapped to the mangled names before trying to retrieve
499506
them (default to no mappings).
500507
"""
501-
return ObjectCode._init(module, "cubin", symbol_mapping=symbol_mapping)
508+
return ObjectCode._init(module, "cubin", name=name, symbol_mapping=symbol_mapping)
502509

503510
@staticmethod
504-
def from_ptx(module: Union[bytes, str], *, symbol_mapping: Optional[dict] = None) -> "ObjectCode":
511+
def from_ptx(module: Union[bytes, str], *,
512+
name: str = "",
513+
symbol_mapping: Optional[dict] = None) -> "ObjectCode":
505514
"""Create an :class:`ObjectCode` instance from an existing PTX.
506515
507516
Parameters
508517
----------
509518
module : Union[bytes, str]
510519
Either a bytes object containing the in-memory ptx code to load, or
511520
a file path string pointing to the on-disk ptx file to load.
521+
name : Optional[str]
522+
A human-readable identifier representing this code object.
512523
symbol_mapping : Optional[dict]
513524
A dictionary specifying how the unmangled symbol names (as keys)
514525
should be mapped to the mangled names before trying to retrieve
515526
them (default to no mappings).
516527
"""
517-
return ObjectCode._init(module, "ptx", symbol_mapping=symbol_mapping)
528+
return ObjectCode._init(module, "ptx", name=name, symbol_mapping=symbol_mapping)
518529

519530
@staticmethod
520-
def from_ltoir(module: Union[bytes, str], *, symbol_mapping: Optional[dict] = None) -> "ObjectCode":
531+
def from_ltoir(module: Union[bytes, str], *,
532+
name: str = "",
533+
symbol_mapping: Optional[dict] = None) -> "ObjectCode":
521534
"""Create an :class:`ObjectCode` instance from an existing LTOIR.
522535
523536
Parameters
524537
----------
525538
module : Union[bytes, str]
526539
Either a bytes object containing the in-memory ltoir code to load, or
527540
a file path string pointing to the on-disk ltoir file to load.
541+
name : Optional[str]
542+
A human-readable identifier representing this code object.
528543
symbol_mapping : Optional[dict]
529544
A dictionary specifying how the unmangled symbol names (as keys)
530545
should be mapped to the mangled names before trying to retrieve
531546
them (default to no mappings).
532547
"""
533-
return ObjectCode._init(module, "ltoir", symbol_mapping=symbol_mapping)
548+
return ObjectCode._init(module, "ltoir", name=name, symbol_mapping=symbol_mapping)
534549

535550
@staticmethod
536-
def from_fatbin(module: Union[bytes, str], *, symbol_mapping: Optional[dict] = None) -> "ObjectCode":
551+
def from_fatbin(module: Union[bytes, str], *,
552+
name: str = "",
553+
symbol_mapping: Optional[dict] = None) -> "ObjectCode":
537554
"""Create an :class:`ObjectCode` instance from an existing fatbin.
538555
539556
Parameters
540557
----------
541558
module : Union[bytes, str]
542559
Either a bytes object containing the in-memory fatbin to load, or
543560
a file path string pointing to the on-disk fatbin to load.
561+
name : Optional[str]
562+
A human-readable identifier representing this code object.
544563
symbol_mapping : Optional[dict]
545564
A dictionary specifying how the unmangled symbol names (as keys)
546565
should be mapped to the mangled names before trying to retrieve
547566
them (default to no mappings).
548567
"""
549-
return ObjectCode._init(module, "fatbin", symbol_mapping=symbol_mapping)
568+
return ObjectCode._init(module, "fatbin", name=name, symbol_mapping=symbol_mapping)
550569

551570
@staticmethod
552-
def from_object(module: Union[bytes, str], *, symbol_mapping: Optional[dict] = None) -> "ObjectCode":
571+
def from_object(module: Union[bytes, str], *,
572+
name: str = "",
573+
symbol_mapping: Optional[dict] = None) -> "ObjectCode":
553574
"""Create an :class:`ObjectCode` instance from an existing object code.
554575
555576
Parameters
556577
----------
557578
module : Union[bytes, str]
558579
Either a bytes object containing the in-memory object code to load, or
559580
a file path string pointing to the on-disk object code to load.
581+
name : Optional[str]
582+
A human-readable identifier representing this code object.
560583
symbol_mapping : Optional[dict]
561584
A dictionary specifying how the unmangled symbol names (as keys)
562585
should be mapped to the mangled names before trying to retrieve
563586
them (default to no mappings).
564587
"""
565-
return ObjectCode._init(module, "object", symbol_mapping=symbol_mapping)
588+
return ObjectCode._init(module, "object", name=name, symbol_mapping=symbol_mapping)
566589

567590
@staticmethod
568-
def from_library(module: Union[bytes, str], *, symbol_mapping: Optional[dict] = None) -> "ObjectCode":
591+
def from_library(module: Union[bytes, str], *,
592+
name: str = "",
593+
symbol_mapping: Optional[dict] = None) -> "ObjectCode":
569594
"""Create an :class:`ObjectCode` instance from an existing library.
570595
571596
Parameters
572597
----------
573598
module : Union[bytes, str]
574599
Either a bytes object containing the in-memory library to load, or
575600
a file path string pointing to the on-disk library to load.
601+
name : Optional[str]
602+
A human-readable identifier representing this code object.
576603
symbol_mapping : Optional[dict]
577604
A dictionary specifying how the unmangled symbol names (as keys)
578605
should be mapped to the mangled names before trying to retrieve
579606
them (default to no mappings).
580607
"""
581-
return ObjectCode._init(module, "library", symbol_mapping=symbol_mapping)
608+
return ObjectCode._init(module, "library", name=name, symbol_mapping=symbol_mapping)
582609

583610
# TODO: do we want to unload in a finalizer? Probably not..
584611

@@ -632,6 +659,11 @@ def code(self) -> CodeTypeT:
632659
"""Return the underlying code object."""
633660
return self._module
634661

662+
@property
663+
def name(self) -> str:
664+
"""Return a human-readable name of this code object."""
665+
return self._name
666+
635667
@property
636668
@precondition(_lazy_load_module)
637669
def handle(self):

cuda_core/cuda/core/experimental/_program.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ class ProgramOptions:
5757
5858
Attributes
5959
----------
60+
name : str, optional
61+
Name of the program. If the compilation succeeds, the name is passed down to the generated `ObjectCode`.
6062
arch : str, optional
6163
Pass the SM architecture value, such as ``sm_<CC>`` (for generating CUBIN) or
6264
``compute_<CC>`` (for generating PTX). If not provided, the current device's architecture
@@ -180,6 +182,7 @@ class ProgramOptions:
180182
Default: False
181183
"""
182184

185+
name: Optional[str] = "<default program>"
183186
arch: Optional[str] = None
184187
relocatable_device_code: Optional[bool] = None
185188
extensible_whole_program: Optional[bool] = None
@@ -222,6 +225,8 @@ class ProgramOptions:
222225
minimal: Optional[bool] = None
223226

224227
def __post_init__(self):
228+
self._name = self.name.encode()
229+
225230
self._formatted_options = []
226231
if self.arch is not None:
227232
self._formatted_options.append(f"--gpu-architecture={self.arch}")
@@ -396,7 +401,7 @@ def __init__(self, code, code_type, options: ProgramOptions = None):
396401
# TODO: support pre-loaded headers & include names
397402
# TODO: allow tuples once NVIDIA/cuda-python#72 is resolved
398403

399-
self._mnff.handle = handle_return(nvrtc.nvrtcCreateProgram(code.encode(), b"", 0, [], []))
404+
self._mnff.handle = handle_return(nvrtc.nvrtcCreateProgram(code.encode(), options._name, 0, [], []))
400405
self._backend = "NVRTC"
401406
self._linker = None
402407

@@ -413,6 +418,7 @@ def __init__(self, code, code_type, options: ProgramOptions = None):
413418

414419
def _translate_program_options(self, options: ProgramOptions) -> LinkerOptions:
415420
return LinkerOptions(
421+
name=options.name,
416422
arch=options.arch,
417423
max_register_count=options.max_register_count,
418424
time=options.time,
@@ -505,7 +511,7 @@ def compile(self, target_type, name_expressions=(), logs=None):
505511
handle_return(nvrtc.nvrtcGetProgramLog(self._mnff.handle, log), handle=self._mnff.handle)
506512
logs.write(log.decode("utf-8", errors="backslashreplace"))
507513

508-
return ObjectCode._init(data, target_type, symbol_mapping=symbol_mapping)
514+
return ObjectCode._init(data, target_type, symbol_mapping=symbol_mapping, name=self._options.name)
509515

510516
supported_backends = ("nvJitLink", "driver")
511517
if self._backend not in supported_backends:

cuda_core/docs/source/release/0.3.0-notes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ New features
2222

2323
- :class:`Kernel` adds :property:`Kernel.num_arguments` and :property:`Kernel.arguments_info` for introspection of kernel arguments. (#612)
2424
- Add pythonic access to kernel occupancy calculation functions via :property:`Kernel.occupancy`. (#648)
25+
- A name can be assigned to :class:`ObjectCode` instances generated by both :class:`Program` and :class:`Linker` through their respective
26+
options.
2527

2628
New examples
2729
------------

cuda_core/tests/test_linker.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ def test_linker_link_ptx_nvjitlink(compile_ltoir_functions):
109109
linker = Linker(*compile_ltoir_functions, options=options)
110110
linked_code = linker.link("ptx")
111111
assert isinstance(linked_code, ObjectCode)
112+
assert linked_code.name == options.name
112113

113114

114115
@pytest.mark.skipif(not is_culink_backend, reason="nvjitlink requires lto for ptx linking")
@@ -117,13 +118,15 @@ def test_linker_link_ptx_culink(compile_ptx_functions):
117118
linker = Linker(*compile_ptx_functions, options=options)
118119
linked_code = linker.link("ptx")
119120
assert isinstance(linked_code, ObjectCode)
121+
assert linked_code.name == options.name
120122

121123

122124
def test_linker_link_cubin(compile_ptx_functions):
123125
options = LinkerOptions(arch=ARCH)
124126
linker = Linker(*compile_ptx_functions, options=options)
125127
linked_code = linker.link("cubin")
126128
assert isinstance(linked_code, ObjectCode)
129+
assert linked_code.name == options.name
127130

128131

129132
def test_linker_link_ptx_multiple(compile_ptx_functions):
@@ -132,6 +135,7 @@ def test_linker_link_ptx_multiple(compile_ptx_functions):
132135
linker = Linker(*ptxes, options=options)
133136
linked_code = linker.link("cubin")
134137
assert isinstance(linked_code, ObjectCode)
138+
assert linked_code.name == options.name
135139

136140

137141
def test_linker_link_invalid_target_type(compile_ptx_functions):

cuda_core/tests/test_program.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def ptx_code_object():
2424
@pytest.mark.parametrize(
2525
"options",
2626
[
27+
ProgramOptions(name="abc"),
2728
ProgramOptions(device_code_optimize=True, debug=True),
2829
ProgramOptions(relocatable_device_code=True, max_register_count=32),
2930
ProgramOptions(ftz=True, prec_sqrt=False, prec_div=False),
@@ -105,21 +106,23 @@ def test_program_init_invalid_code_format():
105106
# This is tested against the current device's arch
106107
def test_program_compile_valid_target_type(init_cuda):
107108
code = 'extern "C" __global__ void my_kernel() {}'
108-
program = Program(code, "c++")
109+
program = Program(code, "c++", options={"name": "42"})
109110

110111
with warnings.catch_warnings(record=True) as w:
111112
warnings.simplefilter("always")
112113
ptx_object_code = program.compile("ptx")
114+
assert isinstance(ptx_object_code, ObjectCode)
115+
assert ptx_object_code.name == "42"
113116
if any("The CUDA driver version is older than the backend version" in str(warning.message) for warning in w):
114117
pytest.skip("PTX version too new for current driver")
118+
ptx_kernel = ptx_object_code.get_kernel("my_kernel")
119+
assert isinstance(ptx_kernel, Kernel)
115120

116-
program = Program(ptx_object_code._module.decode(), "ptx")
121+
program = Program(ptx_object_code._module.decode(), "ptx", options={"name": "24"})
117122
cubin_object_code = program.compile("cubin")
118-
ptx_kernel = ptx_object_code.get_kernel("my_kernel")
119-
cubin_kernel = cubin_object_code.get_kernel("my_kernel")
120-
assert isinstance(ptx_object_code, ObjectCode)
121123
assert isinstance(cubin_object_code, ObjectCode)
122-
assert isinstance(ptx_kernel, Kernel)
124+
assert cubin_object_code.name == "24"
125+
cubin_kernel = cubin_object_code.get_kernel("my_kernel")
123126
assert isinstance(cubin_kernel, Kernel)
124127

125128

0 commit comments

Comments
 (0)