Repository navigation
CUDA: Add CUDA 11.8 / Hopper support and required fixes #8579
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
4a9672f
f7adcec
e3e7d82
c4e4081
3e7ebb4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -343,51 +343,69 @@ def get_log(self): | |
| return '' | ||
|
|
||
|
|
||
| COMPUTE_CAPABILITIES = ( | ||
| (3, 5), (3, 7), | ||
| (5, 0), (5, 2), (5, 3), | ||
| (6, 0), (6, 1), (6, 2), | ||
| (7, 0), (7, 2), (7, 5), | ||
| (8, 0), (8, 6), (8, 7), (8, 9), | ||
| (9, 0) | ||
| ) | ||
|
|
||
| # Maps CTK version -> (min supported cc, max supported cc) | ||
| CTK_SUPPORTED = { | ||
| (11, 0): ((3, 5), (8, 0)), | ||
| (11, 1): ((3, 5), (8, 6)), | ||
| (11, 2): ((3, 5), (8, 6)), | ||
| (11, 3): ((3, 5), (8, 6)), | ||
| (11, 4): ((3, 5), (8, 7)), | ||
| (11, 5): ((3, 5), (8, 7)), | ||
| (11, 6): ((3, 5), (8, 7)), | ||
| (11, 7): ((3, 5), (8, 7)), | ||
| (11, 8): ((3, 5), (9, 0)), | ||
| } | ||
|
|
||
|
|
||
| def ccs_supported_by_ctk(ctk_version): | ||
| try: | ||
| # For supported versions, we look up the range of supported CCs | ||
| min_cc, max_cc = CTK_SUPPORTED[ctk_version] | ||
| return tuple([cc for cc in COMPUTE_CAPABILITIES | ||
| if min_cc <= cc <= max_cc]) | ||
| except KeyError: | ||
| # For unsupported CUDA toolkit versions, all we can do is assume all | ||
| # non-deprecated versions we are aware of are supported. | ||
| return tuple([cc for cc in COMPUTE_CAPABILITIES if cc >= (5, 3)]) | ||
|
|
||
|
|
||
| _supported_cc = None | ||
|
|
||
|
|
||
| def get_supported_ccs(): | ||
| # Attempt to return cached list | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there's an in-process cache need, should something like
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would be way better than the current bodge!
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually it doesn't even need to be cached, this just makes no sense. The supported CCs is a property of the NVVM version, so I'm going to just make I think there could be an argument for the logic in |
||
| global _supported_cc | ||
|
|
||
| if _supported_cc: | ||
| if _supported_cc is not None: | ||
| return _supported_cc | ||
|
|
||
| try: | ||
| from numba.cuda.cudadrv.runtime import runtime | ||
| cudart_version = runtime.get_version() | ||
| except: # noqa: E722 | ||
| # The CUDA Runtime may not be present | ||
| cudart_version = (0, 0) | ||
|
|
||
| ctk_ver = f"{cudart_version[0]}.{cudart_version[1]}" | ||
| unsupported_ver = f"CUDA Toolkit {ctk_ver} is unsupported by Numba - " \ | ||
| + "11.0 is the minimum required version." | ||
|
|
||
| # List of supported compute capability in sorted order | ||
| if cudart_version == (0, 0): | ||
| # We can't support anything if there's an error getting the runtime | ||
| # version (e.g. if it's not present or there's another issue) | ||
| _supported_cc = () | ||
| elif cudart_version == (11, 0): | ||
| _supported_cc = ((3, 5), (3, 7), | ||
| (5, 0), (5, 2), (5, 3), | ||
| (6, 0), (6, 1), (6, 2), | ||
| (7, 0), (7, 2), (7, 5), | ||
| (8, 0)) | ||
| elif cudart_version > (11, 0): | ||
| _supported_cc = ((3, 5), (3, 7), | ||
| (5, 0), (5, 2), (5, 3), | ||
| (6, 0), (6, 1), (6, 2), | ||
| (7, 0), (7, 2), (7, 5), | ||
| (8, 0), (8, 6)) | ||
| elif cudart_version > (11, 4): | ||
| _supported_cc = ((3, 5), (3, 7), | ||
| (5, 0), (5, 2), (5, 3), | ||
| (6, 0), (6, 1), (6, 2), | ||
| (7, 0), (7, 2), (7, 5), | ||
| (8, 0), (8, 6), (8, 7)) | ||
| else: | ||
| return _supported_cc | ||
|
|
||
| # Ensure the minimum CTK version requirement is met | ||
| if cudart_version < (11, 0): | ||
| _supported_cc = () | ||
| ctk_ver = f"{cudart_version[0]}.{cudart_version[1]}" | ||
| unsupported_ver = (f"CUDA Toolkit {ctk_ver} is unsupported by Numba - " | ||
| "11.0 is the minimum required version.") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should the minimum version be specified as a constant somewhere (maybe a module global, or perhaps even all this encoding of supported CCs etc ought to go into it's own module or YAML file?) and then code be updated in terms of that constant?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think instead of hardcoding the minimum CTK required, it can be derived from the dict of supported CCs, then it doesn't need to be stored separately anywhere. Additionally, I'm going to use The CC support matrix for NVPTX will differ, so I think keeping this encoding of supported CCs in |
||
| warnings.warn(unsupported_ver) | ||
| return _supported_cc | ||
|
|
||
| _supported_cc = ccs_supported_by_ctk(cudart_version) | ||
| return _supported_cc | ||
|
|
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,7 @@ | ||
| import warnings | ||
|
|
||
| from llvmlite import ir | ||
| from numba.cuda.cudadrv.nvvm import (llvm_to_ptx, set_cuda_kernel, | ||
| get_arch_option, get_supported_ccs) | ||
| from numba.cuda.cudadrv import nvvm | ||
| from ctypes import c_size_t, c_uint64, sizeof | ||
| from numba.cuda.testing import unittest | ||
| from numba.cuda.cudadrv.nvvm import LibDevice, NvvmError, NVVM | ||
|
|
@@ -15,7 +14,8 @@ | |
| class TestNvvmDriver(unittest.TestCase): | ||
| def get_nvvmir(self): | ||
| if NVVM().is_nvvm70: | ||
| metadata = metadata_nvvm70 | ||
| versions = NVVM().get_ir_version() | ||
| metadata = metadata_nvvm70 % versions | ||
|
Comment on lines
+17
to
+18
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps update to use
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I decided against that because it makes the IR less legible due to the presence of |
||
| else: | ||
| metadata = metadata_nvvm34 | ||
|
|
||
|
|
@@ -25,21 +25,22 @@ def get_nvvmir(self): | |
|
|
||
| def test_nvvm_compile_simple(self): | ||
| nvvmir = self.get_nvvmir() | ||
| ptx = llvm_to_ptx(nvvmir).decode('utf8') | ||
| ptx = nvvm.llvm_to_ptx(nvvmir).decode('utf8') | ||
| self.assertTrue('simple' in ptx) | ||
| self.assertTrue('ave' in ptx) | ||
|
|
||
| def test_nvvm_from_llvm(self): | ||
| m = ir.Module("test_nvvm_from_llvm") | ||
| m.triple = 'nvptx64-nvidia-cuda' | ||
| nvvm.add_ir_version(m) | ||
|
Comment on lines
33
to
+35
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this 3 line "setup" for a CUDA-target-LLVM-module sufficiently common to make it worth creating a utility function? The main concern here is that the
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does happen for new modules created by the |
||
| fty = ir.FunctionType(ir.VoidType(), [ir.IntType(32)]) | ||
| kernel = ir.Function(m, fty, name='mycudakernel') | ||
| bldr = ir.IRBuilder(kernel.append_basic_block('entry')) | ||
| bldr.ret_void() | ||
| set_cuda_kernel(kernel) | ||
| nvvm.set_cuda_kernel(kernel) | ||
|
|
||
| m.data_layout = NVVM().data_layout | ||
| ptx = llvm_to_ptx(str(m)).decode('utf8') | ||
| ptx = nvvm.llvm_to_ptx(str(m)).decode('utf8') | ||
| self.assertTrue('mycudakernel' in ptx) | ||
| if is64bit: | ||
| self.assertTrue('.address_size 64' in ptx) | ||
|
|
@@ -50,40 +51,42 @@ def test_nvvm_ir_verify_fail(self): | |
| m = ir.Module("test_bad_ir") | ||
| m.triple = "unknown-unknown-unknown" | ||
| m.data_layout = NVVM().data_layout | ||
| nvvm.add_ir_version(m) | ||
| with self.assertRaisesRegex(NvvmError, 'Invalid target triple'): | ||
| llvm_to_ptx(str(m)) | ||
| nvvm.llvm_to_ptx(str(m)) | ||
|
|
||
| def _test_nvvm_support(self, arch): | ||
| compute_xx = 'compute_{0}{1}'.format(*arch) | ||
| nvvmir = self.get_nvvmir() | ||
| ptx = llvm_to_ptx(nvvmir, arch=compute_xx, ftz=1, prec_sqrt=0, | ||
| prec_div=0).decode('utf8') | ||
| ptx = nvvm.llvm_to_ptx(nvvmir, arch=compute_xx, ftz=1, prec_sqrt=0, | ||
| prec_div=0).decode('utf8') | ||
| self.assertIn(".target sm_{0}{1}".format(*arch), ptx) | ||
| self.assertIn('simple', ptx) | ||
| self.assertIn('ave', ptx) | ||
|
|
||
| def test_nvvm_support(self): | ||
| """Test supported CC by NVVM | ||
| """ | ||
| for arch in get_supported_ccs(): | ||
| for arch in nvvm.get_supported_ccs(): | ||
| self._test_nvvm_support(arch=arch) | ||
|
|
||
| def test_nvvm_warning(self): | ||
| m = ir.Module("test_nvvm_warning") | ||
| m.triple = 'nvptx64-nvidia-cuda' | ||
| m.data_layout = NVVM().data_layout | ||
| nvvm.add_ir_version(m) | ||
|
|
||
| fty = ir.FunctionType(ir.VoidType(), []) | ||
| kernel = ir.Function(m, fty, name='inlinekernel') | ||
| builder = ir.IRBuilder(kernel.append_basic_block('entry')) | ||
| builder.ret_void() | ||
| set_cuda_kernel(kernel) | ||
| nvvm.set_cuda_kernel(kernel) | ||
|
|
||
| # Add the noinline attribute to trigger NVVM to generate a warning | ||
| kernel.attributes.add('noinline') | ||
|
|
||
| with warnings.catch_warnings(record=True) as w: | ||
| llvm_to_ptx(str(m)) | ||
| nvvm.llvm_to_ptx(str(m)) | ||
|
|
||
| self.assertEqual(len(w), 1) | ||
| self.assertIn('overriding noinline attribute', str(w[0])) | ||
|
|
@@ -107,14 +110,14 @@ def test_nvvm_future_support(self): | |
| class TestArchOption(unittest.TestCase): | ||
| def test_get_arch_option(self): | ||
| # Test returning the nearest lowest arch. | ||
| self.assertEqual(get_arch_option(5, 3), 'compute_53') | ||
| self.assertEqual(get_arch_option(7, 5), 'compute_75') | ||
| self.assertEqual(get_arch_option(7, 7), 'compute_75') | ||
| self.assertEqual(nvvm.get_arch_option(5, 3), 'compute_53') | ||
| self.assertEqual(nvvm.get_arch_option(7, 5), 'compute_75') | ||
| self.assertEqual(nvvm.get_arch_option(7, 7), 'compute_75') | ||
| # Test known arch. | ||
| supported_cc = get_supported_ccs() | ||
| supported_cc = nvvm.get_supported_ccs() | ||
| for arch in supported_cc: | ||
| self.assertEqual(get_arch_option(*arch), 'compute_%d%d' % arch) | ||
| self.assertEqual(get_arch_option(1000, 0), | ||
| self.assertEqual(nvvm.get_arch_option(*arch), 'compute_%d%d' % arch) | ||
| self.assertEqual(nvvm.get_arch_option(1000, 0), | ||
| 'compute_%d%d' % supported_cc[-1]) | ||
|
|
||
|
|
||
|
|
@@ -163,7 +166,7 @@ def test_libdevice_load(self): | |
|
|
||
| metadata_nvvm70 = ''' | ||
| !nvvmir.version = !{!1} | ||
| !1 = !{i32 1, i32 6, i32 3, i32 0} | ||
| !1 = !{i32 %s, i32 %s, i32 %s, i32 %s} | ||
|
|
||
| !nvvm.annotations = !{!2} | ||
| !2 = !{void (i32*)* @simple, !"kernel", i32 1} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are the bounds inclusive/exclusive, should this be clarified?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now clarified.