diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ec1495d7891a953f7f37484403924cd37ee10d87..92a000fc64216edb07adbb565fa2a5322e85cb92 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -101,7 +101,7 @@ Python 3 K40: Python 3 AMD GPU: script: - export PY_EXE=python3 - - export PYOPENCL_TEST=amd:fiji + - export PYOPENCL_TEST=amd:gfx803 - export EXTRA_INSTALL="pybind11 numpy mako" # https://andreask.cs.illinois.edu/MachineShop/UserNotes @@ -109,7 +109,6 @@ Python 3 AMD GPU: - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh - ". ./build-and-test-py-project.sh" - allow_failure: true tags: - python3 - amd-fiji diff --git a/pyopencl/__init__.py b/pyopencl/__init__.py index 7f77154f66278a5fc56bf59ebecc104aa5551a6c..5f92e136a2f712b12e5a8fdd792db1e123a21ebc 100644 --- a/pyopencl/__init__.py +++ b/pyopencl/__init__.py @@ -265,6 +265,66 @@ def _find_pyopencl_include_path(): # }}} +# {{{ build option munging + +def _split_options_if_necessary(options): + if isinstance(options, six.string_types): + import shlex + if six.PY2: + # shlex.split takes bytes (py2 str) on py2 + if isinstance(options, six.text_type): + options = options.encode("utf-8") + else: + # shlex.split takes unicode (py3 str) on py3 + if isinstance(options, six.binary_type): + options = options.decode("utf-8") + + options = shlex.split(options) + + return options + + +def _find_include_path(options): + def unquote(path): + if path.startswith('"') and path.endswith('"'): + return path[1:-1] + else: + return path + + include_path = ["."] + + option_idx = 0 + while option_idx < len(options): + option = options[option_idx].strip() + if option.startswith("-I") or option.startswith("/I"): + if len(option) == 2: + if option_idx+1 < len(options): + include_path.append(unquote(options[option_idx+1])) + option_idx += 2 + else: + include_path.append(unquote(option[2:].lstrip())) + option_idx += 1 + else: + option_idx += 1 + + # }}} + + return include_path + + +def _options_to_bytestring(options): + def encode_if_necessary(s): + if isinstance(s, six.text_type): + return s.encode("utf-8") + else: + return s + + return b" ".join(encode_if_necessary(s) for s in options) + + +# }}} + + # {{{ Program (wrapper around _Program, adds caching support) _DEFAULT_BUILD_OPTIONS = [] @@ -310,9 +370,9 @@ class Program(object): from pyopencl.tools import is_spirv if is_spirv(source): - # no caching in SPIR-V case + # FIXME no caching in SPIR-V case self._context = context - self._prg = _cl._Program(context, source) + self._prg = _cl._create_program_with_il(context, source) return import sys @@ -390,25 +450,8 @@ class Program(object): # {{{ build @classmethod - def _process_build_options(cls, context, options): - if isinstance(options, six.string_types): - import shlex - if six.PY2: - # shlex.split takes bytes (py2 str) on py2 - if isinstance(options, six.text_type): - options = options.encode("utf-8") - else: - # shlex.split takes unicode (py3 str) on py3 - if isinstance(options, six.binary_type): - options = options.decode("utf-8") - - options = shlex.split(options) - - def encode_if_necessary(s): - if isinstance(s, six.text_type): - return s.encode("utf-8") - else: - return s + def _process_build_options(cls, context, options, _add_include_path=False): + options = _split_options_if_necessary(options) options = (options + _DEFAULT_BUILD_OPTIONS @@ -421,35 +464,9 @@ class Program(object): if forced_options: options = options + forced_options.split() - # {{{ find include path - - def unquote(path): - if path.startswith('"') and path.endswith('"'): - return path[1:-1] - else: - return path - - include_path = ["."] - - option_idx = 0 - while option_idx < len(options): - option = options[option_idx].strip() - if option.startswith("-I") or option.startswith("/I"): - if len(option) == 2: - if option_idx+1 < len(options): - include_path.append(unquote(options[option_idx+1])) - option_idx += 2 - else: - include_path.append(unquote(option[2:].lstrip())) - option_idx += 1 - else: - option_idx += 1 - - # }}} - - options = [encode_if_necessary(s) for s in options] - - return b" ".join(options), include_path + return ( + _options_to_bytestring(options), + _find_include_path(options)) def build(self, options=[], devices=None, cache_dir=None): options_bytes, include_path = self._process_build_options( @@ -559,8 +576,11 @@ def create_program_with_built_in_kernels(context, devices, kernel_names): context, devices, kernel_names)) -def link_program(context, programs, options=[], devices=None): - options_bytes, _ = Program._process_build_options(context, options) +def link_program(context, programs, options=None, devices=None): + if options is None: + options = [] + + options_bytes = _options_to_bytestring(_split_options_if_necessary(options)) programs = [prg._get_prg() for prg in programs] raw_prg = _Program.link(context, programs, options_bytes, devices) return Program(raw_prg) @@ -700,6 +720,8 @@ def _add_functionality(): build_type = "From-source build" elif self.kind() == program_kind.BINARY: build_type = "From-binary build" + elif self.kind() == program_kind.IL: + build_type = "From-IL build" else: build_type = "Build" @@ -1670,10 +1692,11 @@ def enqueue_copy(queue, dest, src, **kwargs): elif get_cl_header_version() >= (2, 0) and isinstance(dest, SVM): # to SVM - if isinstance(src, SVM): - src = src.mem + if not isinstance(src, SVM): + src = SVM(src) - return _cl._enqueue_svm_memcpy(queue, dest.mem, src, **kwargs) + is_blocking = kwargs.pop("is_blocking", True) + return _cl._enqueue_svm_memcpy(queue, is_blocking, dest, src, **kwargs) else: # assume to-host @@ -1701,7 +1724,9 @@ def enqueue_copy(queue, dest, src, **kwargs): elif isinstance(src, SVM): # from svm # dest is not a SVM instance, otherwise we'd be in the branch above - return _cl._enqueue_svm_memcpy(queue, dest, src.mem, **kwargs) + is_blocking = kwargs.pop("is_blocking", True) + return _cl._enqueue_svm_memcpy( + queue, is_blocking, SVM(dest), src, **kwargs) else: # assume from-host raise TypeError("enqueue_copy cannot perform host-to-host transfers") diff --git a/src/wrap_cl.hpp b/src/wrap_cl.hpp index 145c0b9c264e024c93298cccf34a39fa281f331d..4892e9155c0d61967482635cdeaafbe0a35a768a 100644 --- a/src/wrap_cl.hpp +++ b/src/wrap_cl.hpp @@ -3768,7 +3768,7 @@ namespace pyopencl class program : noncopyable { public: - enum program_kind_type { KND_UNKNOWN, KND_SOURCE, KND_BINARY }; + enum program_kind_type { KND_UNKNOWN, KND_SOURCE, KND_BINARY, KND_IL }; private: cl_program m_program; @@ -4106,6 +4106,35 @@ namespace pyopencl +#if (PYOPENCL_CL_VERSION >= 0x2010) + inline + program *create_program_with_il( + context &ctx, + std::string const &src) + { + cl_int status_code; + PYOPENCL_PRINT_CALL_TRACE("clCreateProgramWithIL"); + cl_program result = clCreateProgramWithIL( + ctx.data(), src.c_str(), src.size(), &status_code); + if (status_code != CL_SUCCESS) + throw pyopencl::error("clCreateProgramWithIL", status_code); + + try + { + return new program(result, false, program::KND_IL); + } + catch (...) + { + clReleaseProgram(result); + throw; + } + } +#endif + + + + + #if PYOPENCL_CL_VERSION >= 0x1020 inline program *link_program( diff --git a/src/wrap_cl_part_2.cpp b/src/wrap_cl_part_2.cpp index edef9ab7d8ce013a80be87647af6c9e3a6b20d06..4ef6afca9244a0f9e4bacca026e899d9519a1637 100644 --- a/src/wrap_cl_part_2.cpp +++ b/src/wrap_cl_part_2.cpp @@ -284,7 +284,7 @@ void pyopencl_expose_part_2(py::module &m) ; } - m.def("_enqueue_svm_memcpyw", enqueue_svm_memcpy, + m.def("_enqueue_svm_memcpy", enqueue_svm_memcpy, py::arg("queue"), py::arg("is_blocking"), py::arg("dst"), @@ -351,6 +351,7 @@ void pyopencl_expose_part_2(py::module &m) .value("UNKNOWN", cls::KND_UNKNOWN) .value("SOURCE", cls::KND_SOURCE) .value("BINARY", cls::KND_BINARY) + .value("IL", cls::KND_IL) ; py::class_(m, "_Program", py::dynamic_attr()) @@ -405,6 +406,10 @@ void pyopencl_expose_part_2(py::module &m) ; } +#if (PYOPENCL_CL_VERSION >= 0x2010) + m.def("_create_program_with_il", create_program_with_il); +#endif + #if PYOPENCL_CL_VERSION >= 0x1020 m.def("unload_platform_compiler", unload_platform_compiler); #endif diff --git a/test/test_wrapper.py b/test/test_wrapper.py index ee3219e971b931c50351332f140f4cfdf4d9591f..9776b03231583a021260ff65de72a24d978f6722 100644 --- a/test/test_wrapper.py +++ b/test/test_wrapper.py @@ -918,8 +918,8 @@ def test_spirv(ctx_factory): if (ctx._get_cl_version() < (2, 1) or cl.get_cl_header_version() < (2, 1)): - from pytest import skip - skip("SPIR-V program creation only available in OpenCL 2.1 and higher") + pytest.skip("SPIR-V program creation only available " + "in OpenCL 2.1 and higher") n = 50000 @@ -930,7 +930,10 @@ def test_spirv(ctx_factory): with open("add-vectors-%d.spv" % queue.device.address_bits, "rb") as spv_file: spv = spv_file.read() - prg = cl.Program(ctx, spv) + prg = cl.Program(ctx, spv).build() + if (not prg.all_kernels() + and queue.device.platform.name.startswith("AMD Accelerated")): + pytest.skip("SPIR-V program creation on AMD did not result in any kernels") prg.sum(queue, a_dev.shape, None, a_dev.data, b_dev.data, dest_dev.data) @@ -954,6 +957,9 @@ def test_coarse_grain_svm(ctx_factory): if ("AMD" in dev.platform.name and dev.type & cl.device_type.CPU): pytest.xfail("AMD CPU doesn't do coarse-grain SVM") + if ("AMD" in dev.platform.name + and dev.type & cl.device_type.GPU): + pytest.xfail("AMD GPU crashes on SVM unmap") n = 3000 svm_ary = cl.SVM(cl.csvm_empty(ctx, (n,), np.float32, alignment=64))