From 45c61ccd0cf9e16fd02c5bdd083b609e31738c17 Mon Sep 17 00:00:00 2001 From: Leo Fang Date: Fri, 6 Jun 2025 14:19:36 +0000 Subject: [PATCH 1/4] add PTX helpers --- cuda_bindings/cuda/bindings/__init__.py | 1 + cuda_bindings/cuda/bindings/utils/__init__.py | 131 ++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 cuda_bindings/cuda/bindings/utils/__init__.py diff --git a/cuda_bindings/cuda/bindings/__init__.py b/cuda_bindings/cuda/bindings/__init__.py index 8086b4f6f1f..4f4f4032dbe 100644 --- a/cuda_bindings/cuda/bindings/__init__.py +++ b/cuda_bindings/cuda/bindings/__init__.py @@ -2,3 +2,4 @@ # SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE from cuda.bindings._version import __version__ +from cuda.bindings import utils diff --git a/cuda_bindings/cuda/bindings/utils/__init__.py b/cuda_bindings/cuda/bindings/utils/__init__.py new file mode 100644 index 00000000000..adf643b7715 --- /dev/null +++ b/cuda_bindings/cuda/bindings/utils/__init__.py @@ -0,0 +1,131 @@ +# Copyright 2025 NVIDIA Corporation. All rights reserved. +# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + +import re + + +# Mapping based on the official PTX ISA <-> CUDA Release table +# https://docs.nvidia.com/cuda/parallel-thread-execution/#release-notes-ptx-release-history +_ptx_to_cuda = { + "1.0": (1, 0), + "1.1": (1, 1), + "1.2": (2, 0), + "1.3": (2, 1), + "1.4": (2, 2), + "2.0": (3, 0), + "2.1": (3, 1), + "2.2": (3, 2), + "2.3": (4, 0), + "3.0": (4, 1), + "3.1": (5, 0), + "3.2": (5, 5), + "4.0": (6, 0), + "4.1": (6, 5), + "4.2": (7, 0), + "4.3": (7, 5), + "5.0": (8, 0), + "6.0": (9, 0), + "6.1": (9, 1), + "6.2": (9, 2), + "6.3": (10, 0), + "6.4": (10, 1), + "6.5": (10, 2), + "7.0": (11, 0), + "7.1": (11, 1), + "7.2": (11, 2), + "7.3": (11, 3), + "7.4": (11, 4), + "7.5": (11, 5), + "7.6": (11, 6), + "7.7": (11, 7), + "7.8": (11, 8), + "8.0": (12, 0), + "8.1": (12, 1), + "8.2": (12, 2), + "8.3": (12, 3), + "8.4": (12, 4), + "8.5": (12, 5), + "8.6": (12, 7), + "8.7": (12, 8), + "8.8": (12, 9), +} + + +def get_minimal_required_driver_ver_from_ptx_ver(ptx_version: str) -> int: + """ + Maps PTX ISA version to the minimal CUDA driver version. + + Parameters + ---------- + ptx_version : str + PTX ISA version as a string, e.g. "8.8" for PTX ISA 8.8. This is the ``.version`` + directive in the PTX header. + + Returns + ------- + int + Minimal CUDA driver version as 1000 * major + 10 * minor, e.g. 12090 for CUDA 12.9. + + Raises + ------ + ValueError + If the PTX version is unknown. + + Examples + -------- + >>> get_minimal_required_driver_ver_from_ptx_ver("8.8") + 12090 + >>> get_minimal_required_driver_ver_from_ptx_ver("7.0") + 11000 + """ + try: + major, minor = _ptx_to_cuda[ptx_version] + return 1000 * major + 10 * minor + except KeyError: + raise ValueError(f"Unknown or unsupported PTX ISA version: {ptx_version}") + + + +# Regex pattern to match .version directive and capture the version number +_ptx_ver_pattern = re.compile(r'\.version\s+([0-9]+\.[0-9]+)') + + +def get_ptx_ver(ptx: str) -> str: + """ + Extract the PTX ISA version string from PTX source code. + + Parameters + ---------- + ptx : str + The PTX assembly source code as a string. + + Returns + ------- + str + The PTX ISA version string, e.g., "8.8". + + Raises + ------ + ValueError + If the .version directive is not found in the PTX source. + + Examples + -------- + >>> ptx = r''' + ... .version 8.8 + ... .target sm_86 + ... .address_size 64 + ... + ... .visible .entry test_kernel() + ... { + ... ret; + ... } + ... ''' + >>> get_ptx_ver(ptx) + '8.8' + """ + m = _ptx_ver_pattern.search(ptx) + if m: + return m.group(1) + else: + raise ValueError("No .version directive found in PTX source. Is it a valid PTX?") From b732b9d7a3a503d20405aa391270d7f7bdc38378 Mon Sep 17 00:00:00 2001 From: Leo Fang Date: Mon, 21 Jul 2025 13:00:00 +0000 Subject: [PATCH 2/4] avoid clobbering __init__.py + address review comments --- cuda_bindings/cuda/bindings/utils/__init__.py | 131 +---------------- .../cuda/bindings/utils/_ptx_utils.py | 132 ++++++++++++++++++ 2 files changed, 134 insertions(+), 129 deletions(-) create mode 100644 cuda_bindings/cuda/bindings/utils/_ptx_utils.py diff --git a/cuda_bindings/cuda/bindings/utils/__init__.py b/cuda_bindings/cuda/bindings/utils/__init__.py index adf643b7715..4cb1284150b 100644 --- a/cuda_bindings/cuda/bindings/utils/__init__.py +++ b/cuda_bindings/cuda/bindings/utils/__init__.py @@ -1,131 +1,4 @@ -# Copyright 2025 NVIDIA Corporation. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE -import re - - -# Mapping based on the official PTX ISA <-> CUDA Release table -# https://docs.nvidia.com/cuda/parallel-thread-execution/#release-notes-ptx-release-history -_ptx_to_cuda = { - "1.0": (1, 0), - "1.1": (1, 1), - "1.2": (2, 0), - "1.3": (2, 1), - "1.4": (2, 2), - "2.0": (3, 0), - "2.1": (3, 1), - "2.2": (3, 2), - "2.3": (4, 0), - "3.0": (4, 1), - "3.1": (5, 0), - "3.2": (5, 5), - "4.0": (6, 0), - "4.1": (6, 5), - "4.2": (7, 0), - "4.3": (7, 5), - "5.0": (8, 0), - "6.0": (9, 0), - "6.1": (9, 1), - "6.2": (9, 2), - "6.3": (10, 0), - "6.4": (10, 1), - "6.5": (10, 2), - "7.0": (11, 0), - "7.1": (11, 1), - "7.2": (11, 2), - "7.3": (11, 3), - "7.4": (11, 4), - "7.5": (11, 5), - "7.6": (11, 6), - "7.7": (11, 7), - "7.8": (11, 8), - "8.0": (12, 0), - "8.1": (12, 1), - "8.2": (12, 2), - "8.3": (12, 3), - "8.4": (12, 4), - "8.5": (12, 5), - "8.6": (12, 7), - "8.7": (12, 8), - "8.8": (12, 9), -} - - -def get_minimal_required_driver_ver_from_ptx_ver(ptx_version: str) -> int: - """ - Maps PTX ISA version to the minimal CUDA driver version. - - Parameters - ---------- - ptx_version : str - PTX ISA version as a string, e.g. "8.8" for PTX ISA 8.8. This is the ``.version`` - directive in the PTX header. - - Returns - ------- - int - Minimal CUDA driver version as 1000 * major + 10 * minor, e.g. 12090 for CUDA 12.9. - - Raises - ------ - ValueError - If the PTX version is unknown. - - Examples - -------- - >>> get_minimal_required_driver_ver_from_ptx_ver("8.8") - 12090 - >>> get_minimal_required_driver_ver_from_ptx_ver("7.0") - 11000 - """ - try: - major, minor = _ptx_to_cuda[ptx_version] - return 1000 * major + 10 * minor - except KeyError: - raise ValueError(f"Unknown or unsupported PTX ISA version: {ptx_version}") - - - -# Regex pattern to match .version directive and capture the version number -_ptx_ver_pattern = re.compile(r'\.version\s+([0-9]+\.[0-9]+)') - - -def get_ptx_ver(ptx: str) -> str: - """ - Extract the PTX ISA version string from PTX source code. - - Parameters - ---------- - ptx : str - The PTX assembly source code as a string. - - Returns - ------- - str - The PTX ISA version string, e.g., "8.8". - - Raises - ------ - ValueError - If the .version directive is not found in the PTX source. - - Examples - -------- - >>> ptx = r''' - ... .version 8.8 - ... .target sm_86 - ... .address_size 64 - ... - ... .visible .entry test_kernel() - ... { - ... ret; - ... } - ... ''' - >>> get_ptx_ver(ptx) - '8.8' - """ - m = _ptx_ver_pattern.search(ptx) - if m: - return m.group(1) - else: - raise ValueError("No .version directive found in PTX source. Is it a valid PTX?") +from ._ptx_utils import get_minimal_required_cuda_ver_from_ptx_ver, get_ptx_ver diff --git a/cuda_bindings/cuda/bindings/utils/_ptx_utils.py b/cuda_bindings/cuda/bindings/utils/_ptx_utils.py new file mode 100644 index 00000000000..702604c4cc5 --- /dev/null +++ b/cuda_bindings/cuda/bindings/utils/_ptx_utils.py @@ -0,0 +1,132 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + +import re + + +# Mapping based on the official PTX ISA <-> CUDA Release table +# https://docs.nvidia.com/cuda/parallel-thread-execution/#release-notes-ptx-release-history +_ptx_to_cuda = { + "1.0": (1, 0), + "1.1": (1, 1), + "1.2": (2, 0), + "1.3": (2, 1), + "1.4": (2, 2), + "2.0": (3, 0), + "2.1": (3, 1), + "2.2": (3, 2), + "2.3": (4, 0), + "3.0": (4, 1), + "3.1": (5, 0), + "3.2": (5, 5), + "4.0": (6, 0), + "4.1": (6, 5), + "4.2": (7, 0), + "4.3": (7, 5), + "5.0": (8, 0), + "6.0": (9, 0), + "6.1": (9, 1), + "6.2": (9, 2), + "6.3": (10, 0), + "6.4": (10, 1), + "6.5": (10, 2), + "7.0": (11, 0), + "7.1": (11, 1), + "7.2": (11, 2), + "7.3": (11, 3), + "7.4": (11, 4), + "7.5": (11, 5), + "7.6": (11, 6), + "7.7": (11, 7), + "7.8": (11, 8), + "8.0": (12, 0), + "8.1": (12, 1), + "8.2": (12, 2), + "8.3": (12, 3), + "8.4": (12, 4), + "8.5": (12, 5), + "8.6": (12, 7), + "8.7": (12, 8), + "8.8": (12, 9), +} + + +def get_minimal_required_cuda_ver_from_ptx_ver(ptx_version: str) -> int: + """ + Maps the PTX ISA version to the minimal CUDA driver, nvPTXCompiler, or nvJitLink version + that is needed to load a PTX of the given ISA version. + + Parameters + ---------- + ptx_version : str + PTX ISA version as a string, e.g. "8.8" for PTX ISA 8.8. This is the ``.version`` + directive in the PTX header. + + Returns + ------- + int + Minimal CUDA version as 1000 * major + 10 * minor, e.g. 12090 for CUDA 12.9. + + Raises + ------ + ValueError + If the PTX version is unknown. + + Examples + -------- + >>> get_minimal_required_driver_ver_from_ptx_ver("8.8") + 12090 + >>> get_minimal_required_driver_ver_from_ptx_ver("7.0") + 11000 + """ + try: + major, minor = _ptx_to_cuda[ptx_version] + return 1000 * major + 10 * minor + except KeyError: + raise ValueError(f"Unknown or unsupported PTX ISA version: {ptx_version}") + + +# Regex pattern to match .version directive and capture the version number +# TODO: if import speed is a concern, consider lazy-initializing it. +_ptx_ver_pattern = re.compile(r'\.version\s+([0-9]+\.[0-9]+)') + + +def get_ptx_ver(ptx: str) -> str: + """ + Extract the PTX ISA version string from PTX source code. + + Parameters + ---------- + ptx : str + The PTX assembly source code as a string. + + Returns + ------- + str + The PTX ISA version string, e.g., "8.8". + + Raises + ------ + ValueError + If the .version directive is not found in the PTX source. + + Examples + -------- + >>> ptx = r''' + ... .version 8.8 + ... .target sm_86 + ... .address_size 64 + ... + ... .visible .entry test_kernel() + ... { + ... ret; + ... } + ... ''' + >>> get_ptx_ver(ptx) + '8.8' + """ + m = _ptx_ver_pattern.search(ptx) + if m: + return m.group(1) + else: + raise ValueError("No .version directive found in PTX source. Is it a valid PTX?") From 94fb13a04dc5a81ba386d9cf6e821d7da9403163 Mon Sep 17 00:00:00 2001 From: Leo Fang Date: Mon, 21 Jul 2025 13:02:28 +0000 Subject: [PATCH 3/4] add simple test + fix linter errors --- cuda_bindings/cuda/bindings/__init__.py | 2 +- .../cuda/bindings/utils/_ptx_utils.py | 5 +-- cuda_bindings/tests/test_utils.py | 43 +++++++++++++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 cuda_bindings/tests/test_utils.py diff --git a/cuda_bindings/cuda/bindings/__init__.py b/cuda_bindings/cuda/bindings/__init__.py index 59ad104633e..e8dc91a822d 100644 --- a/cuda_bindings/cuda/bindings/__init__.py +++ b/cuda_bindings/cuda/bindings/__init__.py @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE -from cuda.bindings._version import __version__ from cuda.bindings import utils +from cuda.bindings._version import __version__ diff --git a/cuda_bindings/cuda/bindings/utils/_ptx_utils.py b/cuda_bindings/cuda/bindings/utils/_ptx_utils.py index 702604c4cc5..d303d5980bc 100644 --- a/cuda_bindings/cuda/bindings/utils/_ptx_utils.py +++ b/cuda_bindings/cuda/bindings/utils/_ptx_utils.py @@ -3,7 +3,6 @@ import re - # Mapping based on the official PTX ISA <-> CUDA Release table # https://docs.nvidia.com/cuda/parallel-thread-execution/#release-notes-ptx-release-history _ptx_to_cuda = { @@ -83,12 +82,12 @@ def get_minimal_required_cuda_ver_from_ptx_ver(ptx_version: str) -> int: major, minor = _ptx_to_cuda[ptx_version] return 1000 * major + 10 * minor except KeyError: - raise ValueError(f"Unknown or unsupported PTX ISA version: {ptx_version}") + raise ValueError(f"Unknown or unsupported PTX ISA version: {ptx_version}") from None # Regex pattern to match .version directive and capture the version number # TODO: if import speed is a concern, consider lazy-initializing it. -_ptx_ver_pattern = re.compile(r'\.version\s+([0-9]+\.[0-9]+)') +_ptx_ver_pattern = re.compile(r"\.version\s+([0-9]+\.[0-9]+)") def get_ptx_ver(ptx: str) -> str: diff --git a/cuda_bindings/tests/test_utils.py b/cuda_bindings/tests/test_utils.py new file mode 100644 index 00000000000..b0c228f441e --- /dev/null +++ b/cuda_bindings/tests/test_utils.py @@ -0,0 +1,43 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + +import pytest + +from cuda.bindings.utils import get_minimal_required_cuda_ver_from_ptx_ver, get_ptx_ver + +ptx_88_kernel = r""" +.version 8.8 +.target sm_75 +.address_size 64 + + // .globl empty_kernel + +.visible .entry empty_kernel() +{ + ret; +} +""" + + +ptx_72_kernel = r""" +.version 7.2 +.target sm_75 +.address_size 64 + + // .globl empty_kernel + +.visible .entry empty_kernel() +{ + ret; +} +""" + + +@pytest.mark.parametrize( + "kernel,actual_ptx_ver,min_cuda_ver", ((ptx_88_kernel, "8.8", 12090), (ptx_72_kernel, "7.2", 11020)) +) +def test_ptx_utils(kernel, actual_ptx_ver, min_cuda_ver): + ptx_ver = get_ptx_ver(kernel) + assert ptx_ver == actual_ptx_ver + cuda_ver = get_minimal_required_cuda_ver_from_ptx_ver(ptx_ver) + assert cuda_ver == min_cuda_ver From 3b60964c1fe5ec06790f21fde15056a16335abbf Mon Sep 17 00:00:00 2001 From: Leo Fang Date: Mon, 21 Jul 2025 13:12:38 +0000 Subject: [PATCH 4/4] add docs --- cuda_bindings/cuda/bindings/__init__.py | 2 +- cuda_bindings/docs/source/api.rst | 1 + cuda_bindings/docs/source/module/utils.rst | 16 ++++++++++++++++ .../docs/source/release/12.X.Y-notes.rst | 4 ++++ 4 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 cuda_bindings/docs/source/module/utils.rst diff --git a/cuda_bindings/cuda/bindings/__init__.py b/cuda_bindings/cuda/bindings/__init__.py index e8dc91a822d..38d71fcfde4 100644 --- a/cuda_bindings/cuda/bindings/__init__.py +++ b/cuda_bindings/cuda/bindings/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE from cuda.bindings import utils diff --git a/cuda_bindings/docs/source/api.rst b/cuda_bindings/docs/source/api.rst index 28a2b8d240f..4277bc745b4 100644 --- a/cuda_bindings/docs/source/api.rst +++ b/cuda_bindings/docs/source/api.rst @@ -15,3 +15,4 @@ CUDA Python API Reference module/nvjitlink module/nvvm module/cufile + module/utils diff --git a/cuda_bindings/docs/source/module/utils.rst b/cuda_bindings/docs/source/module/utils.rst new file mode 100644 index 00000000000..534437dfed0 --- /dev/null +++ b/cuda_bindings/docs/source/module/utils.rst @@ -0,0 +1,16 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + +.. module:: cuda.bindings.utils + +Utils module +============ + +Functions +--------- + +.. autosummary:: + :toctree: generated/ + + get_minimal_required_cuda_ver_from_ptx_ver + get_ptx_ver diff --git a/cuda_bindings/docs/source/release/12.X.Y-notes.rst b/cuda_bindings/docs/source/release/12.X.Y-notes.rst index 4ac1f4da692..b187b4c8dfb 100644 --- a/cuda_bindings/docs/source/release/12.X.Y-notes.rst +++ b/cuda_bindings/docs/source/release/12.X.Y-notes.rst @@ -1,6 +1,8 @@ .. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. .. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE +.. module:: cuda.bindings + ``cuda-bindings`` 12.X.Y Release notes ====================================== @@ -24,6 +26,8 @@ Bug fixes Miscellaneous ------------- +* Added PTX utilities including :func:`~utils.get_minimal_required_cuda_ver_from_ptx_ver` and :func:`~utils.get_ptx_ver`. + Known issues ------------