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_<cls>(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))