diff --git a/doc/misc.rst b/doc/misc.rst
index 610152c2f15b9654cf2b15d9104b1518d4144f6e..3dde753638d31ac1d39faf6168900ceb68d221eb 100644
--- a/doc/misc.rst
+++ b/doc/misc.rst
@@ -123,6 +123,7 @@ Version 2016.2
   for more information.
 * Add support for **range** and **slice** kwargs and data-less reductions
   to :class:`pyopencl.reduction.ReductionKernel`.
+* Add support for SPIR-V. (See :class:`pyopencl.Program`.)
 
 Version 2016.1
 --------------
diff --git a/doc/runtime.rst b/doc/runtime.rst
index fd17da8bc5371f4cbbfdf8f5993a118216007fd5..dfe64c541ac2d2389708b3c534d5d097caa7094d 100644
--- a/doc/runtime.rst
+++ b/doc/runtime.rst
@@ -660,6 +660,14 @@ Programs and Kernels
            Program(context, devices, binaries)
 
     *binaries* must contain one binary for each entry in *devices*.
+    If *src* is a :class:`bytes` object starting with a valid `SPIR-V
+    <https://www.khronos.org/spir>`_ magic number, it will be handed
+    off to the OpenCL implementation as such, rather than as OpenCL C source
+    code. (SPIR-V support requires OpenCL 2.1.)
+
+    .. versionchanged:: 2016.2
+
+        Add support for SPIR-V.
 
     .. attribute:: info
 
diff --git a/pyopencl/__init__.py b/pyopencl/__init__.py
index 00cdaa2a23333f534731868ba34107a423a19e6e..49ec01ef77945c6dd4bb3ae817527ec241498f7f 100644
--- a/pyopencl/__init__.py
+++ b/pyopencl/__init__.py
@@ -297,6 +297,13 @@ class Program(object):
             # 2-argument form: context, source
             context, source = arg1, arg2
 
+            from pyopencl.tools import is_spirv
+            if is_spirv(source):
+                # no caching in SPIR-V case
+                self._context = context
+                self._prg = _cl._Program(context, source)
+                return
+
             import sys
             if isinstance(source, six.text_type) and sys.version_info < (3,):
                 from warnings import warn
diff --git a/pyopencl/cffi_cl.py b/pyopencl/cffi_cl.py
index 52645cdecb360de4f8a98a07662da01c44e9c415..20e68401dff1ceadd3d07f4e5f05449f2c03726a 100644
--- a/pyopencl/cffi_cl.py
+++ b/pyopencl/cffi_cl.py
@@ -993,7 +993,12 @@ class _Program(_Common):
 
     def __init__(self, *args):
         if len(args) == 2:
-            self._init_source(*args)
+            ctx, source = args
+            from pyopencl.tools import is_spirv
+            if is_spirv(source):
+                self._init_il(ctx, source)
+            else:
+                self._init_source(ctx, source)
         else:
             self._init_binary(*args)
 
@@ -1003,6 +1008,12 @@ class _Program(_Common):
             ptr_program, context.ptr, _to_cstring(src)))
         self.ptr = ptr_program[0]
 
+    def _init_il(self, context, il):
+        ptr_program = _ffi.new('clobj_t*')
+        _handle_error(_lib.create_program_with_il(
+            ptr_program, context.ptr, il, len(il)))
+        self.ptr = ptr_program[0]
+
     def _init_binary(self, context, devices, binaries):
         if len(devices) != len(binaries):
             raise RuntimeError("device and binary counts don't match",
diff --git a/pyopencl/tools.py b/pyopencl/tools.py
index b1383cb7b95abe46e22661ba6572e40457b8d6da..2e24890bc476c25ad580e966ab17b88687addd5c 100644
--- a/pyopencl/tools.py
+++ b/pyopencl/tools.py
@@ -931,4 +931,13 @@ def array_module(a):
 
 # }}}
 
+
+def is_spirv(s):
+    spirv_magic = b"\x07\x23\x02\x03"
+    return (
+            isinstance(s, six.binary_type)
+            and (
+                s[:4] == spirv_magic
+                or s[:4] == spirv_magic[::-1]))
+
 # vim: foldmethod=marker
diff --git a/src/c_wrapper/program.cpp b/src/c_wrapper/program.cpp
index a80686aec50269a11933ae0325ccf705c236ad72..d50d96802830269c0b8e35bbda4b85fe97e644c7 100644
--- a/src/c_wrapper/program.cpp
+++ b/src/c_wrapper/program.cpp
@@ -140,6 +140,21 @@ create_program_with_source(clobj_t *prog, clobj_t _ctx, const char *_src)
         });
 }
 
+error*
+create_program_with_il(clobj_t *prog, clobj_t _ctx, void *il, size_t length)
+{
+#if PYOPENCL_CL_VERSION >= 0x2010
+    auto ctx = static_cast<context*>(_ctx);
+    return c_handle_error([&] {
+            cl_program result = pyopencl_call_guarded(
+                clCreateProgramWithIL, ctx, il, length);
+            *prog = new_program(result, KND_SOURCE);
+        });
+#else
+    PYOPENCL_UNSUPPORTED_BEFORE(clCreateProgramWithIL, "CL 2.1")
+#endif
+}
+
 error*
 create_program_with_binary(clobj_t *prog, clobj_t _ctx,
                            cl_uint num_devices, const clobj_t *devices,
diff --git a/src/c_wrapper/wrap_cl_core.h b/src/c_wrapper/wrap_cl_core.h
index c96b53f189aa526e09893da68800769b8effead2..8ef6f2e8066b4b50e938ee67dc0df1191dc9e4e6 100644
--- a/src/c_wrapper/wrap_cl_core.h
+++ b/src/c_wrapper/wrap_cl_core.h
@@ -171,6 +171,7 @@ error* enqueue_svm_migrate_mem(
 
 error *create_program_with_source(clobj_t *program, clobj_t context,
                                   const char *src);
+error* create_program_with_il(clobj_t *prog, clobj_t _ctx, void *il, size_t length);
 error *create_program_with_binary(clobj_t *program, clobj_t context,
                                   cl_uint num_devices, const clobj_t *devices,
                                   const unsigned char **binaries,
diff --git a/test/test_wrapper.py b/test/test_wrapper.py
index 07f32522dc6af789e44812521ec6501803b1ad7c..29020c8e0c79cee8af92d805af970bd7e1549c54 100644
--- a/test/test_wrapper.py
+++ b/test/test_wrapper.py
@@ -30,6 +30,7 @@ import pytest
 
 import pyopencl as cl
 import pyopencl.array as cl_array
+import pyopencl.clrandom
 from pyopencl.tools import (  # noqa
         pytest_generate_tests_for_pyopencl as pytest_generate_tests)
 
@@ -906,6 +907,31 @@ def test_sub_buffers(ctx_factory):
     assert np.array_equal(a_sub, a_sub_ref)
 
 
+def test_spirv(ctx_factory):
+    ctx = ctx_factory()
+    queue = cl.CommandQueue(ctx)
+
+    # if (ctx._get_cl_version() < (2, 1) and
+    #         cl.get_cl_header_version() < (2, 1)):
+    #     from pytest import skip
+    #     skip("SPIR-V program creation only available in OpenCL 2.1")
+
+    n = 50000
+
+    a_dev = cl.clrandom.rand(queue, n, np.float32)
+    b_dev = cl.clrandom.rand(queue, n, np.float32)
+    dest_dev = cl_array.empty_like(a_dev)
+
+    with open("add-vectors.spv", "rb") as spv_file:
+        spv = spv_file.read()
+
+    prg = cl.Program(ctx, spv)
+
+    prg.sum(queue, a_dev.shape, None, a_dev.data, b_dev.data, dest_dev.data)
+
+    assert la.norm((dest_dev - (a_dev+b_dev)).get()) < 1e-7
+
+
 if __name__ == "__main__":
     # make sure that import failures get reported, instead of skipping the tests.
     import pyopencl  # noqa