diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e7520a8c116bc17839194160c97b4bafd44126a4..914c5f7d050e66f16cc5c114f0fbea680e99c36c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -66,7 +66,8 @@ jobs:
         -   name: "Main Script"
             run: |
                 export CC=gcc
-                CONDA_ENVIRONMENT=.test-conda-env-py3.yml
+                CONDA_ENVIRONMENT=.test-conda-env.yml
+                grep -v ocl-icd .test-conda-env-py3.yml > $CONDA_ENVIRONMENT
                 curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh
                 ./configure.py --cxxflags= --ldflags= --cl-libname=OpenCL
                 . ./build-and-test-py-project-within-miniconda.sh
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 29c364fcabe3eeb19bc04f0261eb7d7432b1b851..76b8a07d403e7cb38322b6f9700303b99ef2f73d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -154,12 +154,13 @@ Python 3 POCL (+GL and special functions):
       junit: test/pytest.xml
 
 Python 3 Conda Apple:
-  script:
-  - CONDA_ENVIRONMENT=.test-conda-env-py3.yml
-  - export CC=gcc
-  - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh
-  - "./configure.py --cxxflags= --ldflags= --cl-libname=OpenCL"
-  - ". ./build-and-test-py-project-within-miniconda.sh"
+  script: |
+    CONDA_ENVIRONMENT=.test-conda-env.yml
+    grep -v ocl-icd .test-conda-env-py3.yml > $CONDA_ENVIRONMENT
+    export CC=gcc
+    curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh
+    ./configure.py --cxxflags= --ldflags= --cl-libname=OpenCL
+    . ./build-and-test-py-project-within-miniconda.sh
   tags:
   - apple
   except:
diff --git a/.test-conda-env-py3.yml b/.test-conda-env-py3.yml
index 498f1a6b333eafd594b2c7982ee3b2f93913b742..f57daef50f0123216d0aff556237f7f5813d00a1 100644
--- a/.test-conda-env-py3.yml
+++ b/.test-conda-env-py3.yml
@@ -7,6 +7,7 @@ dependencies:
 - python=3
 - git
 - numpy
+- conda-forge/label/ocl-icd-dev::ocl-icd=3.0.0.dev0
 - pocl
 - mako
 - pybind11
diff --git a/doc/make_constants.py b/doc/make_constants.py
index 5056335390e31697c0188ead269781194e7b60d8..d2c7a42bcef4119388d0feb8a84c98ca8f823141 100644
--- a/doc/make_constants.py
+++ b/doc/make_constants.py
@@ -30,7 +30,10 @@ cl_11 = ("CL_1.1", "0.92")
 cl_12 = ("CL_1.2", "2011.2")
 cl_12_2015 = ("CL_1.2", "2015.2")
 cl_20 = ("CL_2.0", "2015.2")
+cl_21_late = ("CL_2.1", "2020.3")
 cl_21 = ("CL_2.1", "2016.2")
+cl_22 = ("CL_2.1", "2020.3")
+cl_30 = ("CL_3.0", "2020.3")
 amd_devattr = ("cl_amd_device_attribute_query", "2013.2")
 qcom_hp_devattr = ("cl_qcom_ext_host_ptr", "2016.2")
 intel_me_devattr = ("cl_intel_advanced_motion_estimation", "2016.2")
@@ -82,6 +85,9 @@ const_ext_lookup = {
             "INVALID_PIPE_SIZE": cl_20,
             "INVALID_DEVICE_QUEUE": cl_20,
 
+            "INVALID_SPEC_ID": cl_22,
+            "MAX_SIZE_RESTRICTION_EXCEEDED": cl_22,
+
             },
 
         cl.device_info: {
@@ -185,6 +191,21 @@ const_ext_lookup = {
             "IL_VERSION": cl_21,
             "MAX_NUM_SUB_GROUPS": cl_21,
             "SUB_GROUP_INDEPENDENT_FORWARD_PROGRESS": cl_21,
+
+            "NUMERIC_VERSION": cl_30,
+            "EXTENSIONS_WITH_VERSION": cl_30,
+            "ILS_WITH_VERSION": cl_30,
+            "BUILT_IN_KERNELS_WITH_VERSION": cl_30,
+            "ATOMIC_MEMORY_CAPABILITIES": cl_30,
+            "ATOMIC_FENCE_CAPABILITIES": cl_30,
+            "NON_UNIFORM_WORK_GROUP_SUPPORT": cl_30,
+            "OPENCL_C_ALL_VERSIONS": cl_30,
+            "PREFERRED_WORK_GROUP_SIZE_MULTIPLE": cl_30,
+            "WORK_GROUP_COLLECTIVE_FUNCTIONS_SUPPORT": cl_30,
+            "GENERIC_ADDRESS_SPACE_SUPPORT": cl_30,
+            "OPENCL_C_FEATURES": cl_30,
+            "DEVICE_ENQUEUE_CAPABILITIES": cl_30,
+            "PIPE_SUPPORT": cl_30,
             },
 
         cl.device_topology_type_amd: {
@@ -230,6 +251,11 @@ const_ext_lookup = {
             "INTEROP_USER_SYNC": cl_12,
             },
 
+        cl.channel_type: {
+            "UNORM_INT24": ("CL_1.2", "2020.3"),
+            "UNORM_INT_101010_2": ("CL_2.1", "2020.3"),
+            },
+
         cl.channel_order: {
             "Rx": cl_11,
             "RGx": cl_11,
@@ -248,14 +274,26 @@ const_ext_lookup = {
             "GLOBAL_WORK_SIZE": cl_12,
             },
 
+        cl.kernel_sub_group_info: {
+            "MAX_SUB_GROUP_SIZE_FOR_NDRANGE": cl_21_late,
+            "SUB_GROUP_COUNT_FOR_NDRANGE": cl_21_late,
+            "LOCAL_SIZE_FOR_SUB_GROUP_COUNT": cl_21_late,
+            "MAX_NUM_SUB_GROUPS": cl_21_late,
+            "COMPILE_NUM_SUB_GROUPS": cl_21_late,
+            },
+
         cl.addressing_mode: {
             "MIRRORED_REPEAT": cl_11,
             },
 
         cl.sampler_info: {
-            "MIP_FILTER_MODE": cl_20,
-            "LOD_MIN": cl_20,
-            "LOD_MAX": cl_20,
+            "MIP_FILTER_MODE": ("(deprecated)", "2015.2"),
+            "LOD_MIN": ("(deprecated)", "2015.2"),
+            "LOD_MAX": ("(deprecated)", "2015.2"),
+            "MIP_FILTER_MODE_KHR": ("cl_khr_mipmap_image", "2020.3"),
+            "LOD_MIN_KHR": ("cl_khr_mipmap_image", "2020.3"),
+            "LOD_MAX_KHR": ("cl_khr_mipmap_image", "2020.3"),
+            "PROPERTIES": cl_30,
             },
 
         cl.event_info: {
@@ -276,6 +314,17 @@ const_ext_lookup = {
             "NUM_SAMPLES": cl_12,
             },
 
+        cl.pipe_info: {
+            "PACKET_SIZE": ("CL_2.0", "2020.3"),
+            "MAX_PACKETS": ("CL_2.0", "2020.3"),
+            "PROPERTIES": cl_30,
+            },
+
+        cl.pipe_properties: {
+            "PACKET_SIZE": ("CL_2.0", "2020.3"),
+            "MAX_PACKETS": ("CL_2.0", "2020.3"),
+            },
+
         cl.map_flags: {
             "WRITE_INVALIDATE_REGION": cl_12,
             },
@@ -283,6 +332,9 @@ const_ext_lookup = {
         cl.program_info: {
             "NUM_KERNELS": cl_12,
             "KERNEL_NAMES": cl_12,
+            "PROGRAM_IL": cl_21_late,
+            "SCOPE_GLOBAL_CTORS_PRESENT": cl_22,
+            "SCOPE_GLOBAL_DTORS_PRESENT": cl_22,
             },
 
         cl.program_build_info: {
@@ -348,6 +400,7 @@ const_ext_lookup = {
             "SVM_MEMFILL": cl_20,
             "SVM_MAP": cl_20,
             "SVM_UNMAP": cl_20,
+            "SVM_MIGRATE_MEM": cl_30,
             },
 
         cl.command_queue_info: {
@@ -401,6 +454,21 @@ const_ext_lookup = {
             "NEXT_PARITIONNABLE": cl_12,
             },
 
+        cl.device_atomic_capabilities: {
+            "ORDER_RELAXED": cl_30,
+            "ORDER_ACQ_REL": cl_30,
+            "ORDER_SEQ_CST": cl_30,
+            "SCOPE_WORK_ITEM": cl_30,
+            "SCOPE_WORK_GROUP": cl_30,
+            "SCOPE_DEVICE": cl_30,
+            "SCOPE_ALL_DEVICES": cl_30,
+            },
+
+        cl.device_device_enqueue_capabilities: {
+            "SUPPORTED": cl_30,
+            "REPLACEABLE_DEFAULT": cl_30,
+            },
+
         cl.profiling_info: {
             "COMPLETE": cl_20,
             },
@@ -410,6 +478,18 @@ const_ext_lookup = {
             "CONTENT_UNDEFINED": cl_12,
             },
 
+        cl.version_bits: {
+            "MAJOR_BITS": cl_30,
+            "MINOR_BITS": cl_30,
+            "PATCH_BITS": cl_30,
+            "MAJOR_MASK": cl_30,
+            "MINOR_MASK": cl_30,
+            "PATCH_MASK": cl_30,
+            },
+
+        cl.khronos_vendor_id: {
+            "CODEPLAY": cl_30,
+            },
         }
 try:
     gl_ci = cl.gl_context_info
@@ -438,8 +518,8 @@ def doc_class(cls):
         print()
 
     if cls in cls_ext_lookup:
-        for l in get_extra_lines(cls_ext_lookup[cls]):
-            print(l)
+        for ln in get_extra_lines(cls_ext_lookup[cls]):
+            print(ln)
 
     cls_const_ext = const_ext_lookup.get(cls, {})
     for name in sorted(dir(cls)):
@@ -447,8 +527,8 @@ def doc_class(cls):
             print("    .. attribute :: %s" % name)
 
             if name in cls_const_ext:
-                for l in get_extra_lines(cls_const_ext[name]):
-                    print("    "+l)
+                for ln in get_extra_lines(cls_const_ext[name]):
+                    print("    "+ln)
 
     print("    .. method :: to_string(value)")
     print()
diff --git a/doc/misc.rst b/doc/misc.rst
index b8d5c22e2477b1e9d9f2e2ca53a992702592dbb2..aaad2ad72aaac06abef5d6cf7d30ceddd90ad53e 100644
--- a/doc/misc.rst
+++ b/doc/misc.rst
@@ -764,6 +764,10 @@ OpenCL Specification
 
    See the  `CL specification <https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_API.html#clCreateSamplerWithProperties>`__.
 
+.. c:function:: void clCreatePipe()
+
+   See the  `CL specification <https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_API.html#clCreatePipe>`__.
+
 Internal Types
 --------------
 
diff --git a/doc/runtime_const.rst b/doc/runtime_const.rst
index 53ebf1fe96fa60a12598a3f776095f94244a7dcf..864a641c7ce303e3d46ecfecbf8c6d443113e364 100644
--- a/doc/runtime_const.rst
+++ b/doc/runtime_const.rst
@@ -4,3 +4,25 @@ OpenCL Runtime: Constants
 .. currentmodule:: pyopencl
 
 .. include:: constants.inc
+
+.. class:: NameVersion
+    Describes the version of a specific feature.
+
+    .. note::
+
+        Only available with OpenCL 3.0 or newer.
+
+    .. versionadded:: 2020.3
+
+    .. method:: __init__(version, name)
+    .. attribute:: version
+    .. attribute:: name
+
+.. class:: DeviceTopologyAmd
+    .. method:: __init__(bus, device, function)
+    .. attribute:: type
+    .. attribute:: bus
+    .. attribute:: device
+    .. attribute:: function
+
+.. vim: shiftwidth=4
diff --git a/doc/runtime_memory.rst b/doc/runtime_memory.rst
index ce2ee2227a1311ada8b3074c4bf3ea1d095d9151..fc121554842af4c6565d88fe1305082fd76cf7f5 100644
--- a/doc/runtime_memory.rst
+++ b/doc/runtime_memory.rst
@@ -381,3 +381,23 @@ Samplers
 
     |comparable|
 
+Pipes
+-----
+
+.. class:: Pipe(context, flags, packet_size, max_packets, properties)
+
+    See :class:`mem_flags` for values of *flags*.
+
+    :arg properties: a sequence
+        of keys and values from :class:`pipe_properties` as accepted
+        by :c:func:`clCreatePipe`. The trailing *0* is added automatically
+        and does not need to be included.
+
+    This function Requires OpenCL 2 or newer.
+
+    .. versionadded:: 2020.3
+
+    .. method:: get_pipe_info(param)
+
+        See :class:`pipe_info` for values of *param*.
+
diff --git a/doc/runtime_platform.rst b/doc/runtime_platform.rst
index 51eecdba8710dc9688dd40ec3fc6e04286768b11..d6e2ecb859edf03f4f1a769b117e2644df1d04f0 100644
--- a/doc/runtime_platform.rst
+++ b/doc/runtime_platform.rst
@@ -45,6 +45,8 @@ Device
 
 .. class:: Device
 
+    Two instances of this class may be compared using *=="* and *"!="*.
+
     .. attribute:: info
 
         Lower case versions of the :class:`device_info` constants
@@ -88,7 +90,19 @@ Device
 
         .. versionadded:: 2011.2
 
-    Two instances of this class may be compared using *=="* and *"!="*.
+    .. method:: device_and_host_timer
+
+        :returns: a tuple ``(device_timestamp, host_timestamp)``.
+
+        Only available with CL 2.0.
+
+        .. versionadded:: 2020.3
+
+    .. method:: host_timer
+
+        Only available with CL 2.0.
+
+        .. versionadded:: 2020.3
 
 Context
 -------
@@ -160,6 +174,8 @@ Context
     .. automethod:: from_int_ptr
     .. autoattribute:: int_ptr
 
+    .. method:: set_default_device_command_queue(dev, queue)
+
     |comparable|
 
 .. function:: create_some_context(interactive=True, answers=None, cache_dir=None)
diff --git a/doc/runtime_program.rst b/doc/runtime_program.rst
index 18d831eb88ac6d3971651242aeb6367220dc3d9a..8cb2077ccddb785595490dd3ff9244abdfd431d9 100644
--- a/doc/runtime_program.rst
+++ b/doc/runtime_program.rst
@@ -110,6 +110,12 @@ Program
 
         Returns a list of all :class:`Kernel` objects in the :class:`Program`.
 
+    .. method:: set_specialization_constant(spec_id, buffer)
+
+        Only available with CL 2.2 and newer.
+
+        .. versionadded:: 2020.3
+
     .. automethod:: from_int_ptr
     .. autoattribute:: int_ptr
 
@@ -144,6 +150,12 @@ Kernel
         may be used as attributes on instances of this class
         to directly query info attributes.
 
+    .. method:: clone()
+
+        Only available with CL 2.1.
+
+        .. versionadded:: 2020.3
+
     .. method:: get_info(param)
 
         See :class:`kernel_info` for values of *param*.
@@ -158,6 +170,17 @@ Kernel
 
         Only available in OpenCL 1.2 and newer.
 
+    .. method:: get_sub_group_info(self, device, param, input_value=None)
+
+        When the OpenCL spec requests *input_value* to be of type ``size_t``,
+        these may be passed directly as a number. When it requests
+        *input_value* to be of type ``size_t *``, a tuple of integers
+        may be passed.
+
+        Only available in OpenCL 2.1 and newer.
+
+        .. versionadded:: 2020.3
+
     .. method:: set_arg(self, index, arg)
 
         *arg* may be
diff --git a/pyopencl/__init__.py b/pyopencl/__init__.py
index 4a5ee953f5dd170dca1e64eec0e68eb29eb3a787..0f4709d12b8d4d6c4086a0aa3e0d94fdd4d49e64 100644
--- a/pyopencl/__init__.py
+++ b/pyopencl/__init__.py
@@ -80,6 +80,8 @@ from pyopencl._cl import (  # noqa: F401
         mem_object_type,
         mem_info,
         image_info,
+        pipe_info,
+        pipe_properties,
         addressing_mode,
         filter_mode,
         sampler_info,
@@ -95,6 +97,7 @@ from pyopencl._cl import (  # noqa: F401
         kernel_arg_access_qualifier,
         kernel_arg_type_qualifier,
         kernel_work_group_info,
+        kernel_sub_group_info,
 
         event_info,
         command_type,
@@ -103,6 +106,11 @@ from pyopencl._cl import (  # noqa: F401
         mem_migration_flags,
         device_partition_property,
         device_affinity_domain,
+        device_atomic_capabilities,
+        device_device_enqueue_capabilities,
+
+        version_bits,
+        khronos_vendor_id,
 
         Error, MemoryError, LogicError, RuntimeError,
 
@@ -149,6 +157,10 @@ from pyopencl._cl import (  # noqa: F401
 
         Image,
         Sampler,
+
+        # This class is available unconditionally, even though CL only
+        # has it on CL2.0 and newer.
+        Pipe,
         )
 
 try:
@@ -234,6 +246,9 @@ BITFIELD_CONSTANT_CLASSES = (
         _cl.device_svm_capabilities,
         _cl.queue_properties,
         _cl.svm_mem_flags,
+        _cl.device_atomic_capabilities,
+        _cl.device_device_enqueue_capabilities,
+        _cl.version_bits,
         )
 
 
@@ -1004,7 +1019,7 @@ def _add_functionality():
     class _ImageInfoGetter:
         def __init__(self, event):
             from warnings import warn
-            warn("Image.image.attr is deprecated. "
+            warn("Image.image.attr is deprecated and will go away in 2021. "
                     "Use Image.attr directly, instead.")
 
             self.event = event
@@ -1287,6 +1302,7 @@ def _add_functionality():
             _cl.MemoryObjectHolder:
             (MemoryObjectHolder.get_info, _cl.mem_info, []),
             Image: (_cl.Image.get_image_info, _cl.image_info, []),
+            Pipe: (_cl.Pipe.get_pipe_info, _cl.pipe_info, []),
             Program: (Program.get_info, _cl.program_info, []),
             Kernel: (Kernel.get_info, _cl.kernel_info, []),
             _cl.Sampler: (Sampler.get_info, _cl.sampler_info, []),
diff --git a/src/wrap_cl.hpp b/src/wrap_cl.hpp
index 42ee4c11cdea48dfc11043eeb09a68f631d20ada..1ef9969417e49f518f0dd09489dbddfdd5fd61ea 100644
--- a/src/wrap_cl.hpp
+++ b/src/wrap_cl.hpp
@@ -30,6 +30,19 @@
 // CL 1.2 undecided:
 // clSetPrintfCallback
 
+// CL 2.0 complete
+
+// CL 2.1 complete
+
+// CL 2.2 complete
+
+// CL 3.0 missing:
+// clCreateBufferWithProperties
+// clCreateImageWithProperties
+// (no wrappers for now: OpenCL 3.0 does not define any optional properties for
+// buffers or images, no implementations to test with.)
+
+
 // {{{ includes
 
 #define CL_USE_DEPRECATED_OPENCL_1_1_APIS
@@ -52,7 +65,7 @@
 #else
 
 // elsewhere ------------------------------------------------------------------
-#define CL_TARGET_OPENCL_VERSION 220
+#define CL_TARGET_OPENCL_VERSION 300
 
 #include <CL/cl.h>
 #include "pyopencl_ext.h"
@@ -91,7 +104,9 @@
 #define PYOPENCL_CL_VERSION PYOPENCL_PRETEND_CL_VERSION
 #else
 
-#if defined(CL_VERSION_2_2)
+#if defined(CL_VERSION_3_0)
+#define PYOPENCL_CL_VERSION 0x3000
+#elif defined(CL_VERSION_2_2)
 #define PYOPENCL_CL_VERSION 0x2020
 #elif defined(CL_VERSION_2_1)
 #define PYOPENCL_CL_VERSION 0x2010
@@ -108,13 +123,6 @@
 #endif
 
 
-#if (PY_VERSION_HEX >= 0x03000000) or defined(PYPY_VERSION)
-#define PYOPENCL_STD_MOVE_IF_NEW_BUF_INTF(s) std::move(s)
-#else
-#define PYOPENCL_STD_MOVE_IF_NEW_BUF_INTF(s) (s)
-#endif
-
-
 #if defined(_WIN32)
 // MSVC does not understand variable-length arrays
 #define PYOPENCL_STACK_CONTAINER(TYPE, NAME, COUNT) std::vector<TYPE> NAME(COUNT)
@@ -429,6 +437,7 @@
 namespace pyopencl
 {
   class program;
+  class command_queue;
 
   // {{{ error
   class error : public std::runtime_error
@@ -578,6 +587,20 @@ namespace pyopencl
 #endif
             PYOPENCL_GET_STR_INFO(Platform, m_platform, param_name);
 
+#if PYOPENCL_CL_VERSION >= 0x2010
+          case CL_PLATFORM_HOST_TIMER_RESOLUTION:
+            PYOPENCL_GET_TYPED_INFO(Platform, m_platform, param_name, cl_ulong);
+#endif
+#if PYOPENCL_CL_VERSION >= 0x3000
+          case CL_PLATFORM_NUMERIC_VERSION:
+            PYOPENCL_GET_TYPED_INFO(Platform, m_platform, param_name, cl_version);
+          case CL_PLATFORM_EXTENSIONS_WITH_VERSION:
+            {
+              std::vector<cl_name_version> result;
+              PYOPENCL_GET_VEC_INFO(Platform, m_platform, param_name, result);
+              PYOPENCL_RETURN_VECTOR(cl_name_version, result);
+            }
+#endif
           default:
             throw error("Platform.get_info", CL_INVALID_VALUE);
         }
@@ -914,6 +937,35 @@ namespace pyopencl
           case CL_DEVICE_MAX_NUM_SUB_GROUPS: DEV_GET_INT_INF(cl_uint);
           case CL_DEVICE_SUB_GROUP_INDEPENDENT_FORWARD_PROGRESS: DEV_GET_INT_INF(cl_bool);
 #endif
+#if PYOPENCL_CL_VERSION >= 0x3000
+          case CL_DEVICE_NUMERIC_VERSION: DEV_GET_INT_INF(cl_version);
+          case CL_DEVICE_EXTENSIONS_WITH_VERSION:
+          case CL_DEVICE_ILS_WITH_VERSION:
+          case CL_DEVICE_BUILT_IN_KERNELS_WITH_VERSION:
+          case CL_DEVICE_OPENCL_C_ALL_VERSIONS:
+          case CL_DEVICE_OPENCL_C_FEATURES:
+            {
+              std::vector<cl_name_version> result;
+              PYOPENCL_GET_VEC_INFO(Device, m_device, param_name, result);
+              PYOPENCL_RETURN_VECTOR(cl_name_version, result);
+            }
+          case CL_DEVICE_ATOMIC_MEMORY_CAPABILITIES: DEV_GET_INT_INF(cl_device_atomic_capabilities);
+          case CL_DEVICE_ATOMIC_FENCE_CAPABILITIES: DEV_GET_INT_INF(cl_device_atomic_capabilities);
+          case CL_DEVICE_NON_UNIFORM_WORK_GROUP_SUPPORT: DEV_GET_INT_INF(cl_bool);
+          case CL_DEVICE_PREFERRED_WORK_GROUP_SIZE_MULTIPLE: DEV_GET_INT_INF(size_t);
+          case CL_DEVICE_WORK_GROUP_COLLECTIVE_FUNCTIONS_SUPPORT: DEV_GET_INT_INF(cl_bool);
+          case CL_DEVICE_GENERIC_ADDRESS_SPACE_SUPPORT: DEV_GET_INT_INF(cl_bool);
+
+#ifdef CL_DEVICE_DEVICE_ENQUEUE_SUPPORT
+          case CL_DEVICE_DEVICE_ENQUEUE_SUPPORT: DEV_GET_INT_INF(cl_bool);
+#endif
+#ifdef CL_DEVICE_DEVICE_ENQUEUE_CAPABILITIES
+          case CL_DEVICE_DEVICE_ENQUEUE_CAPABILITIES: DEV_GET_INT_INF(cl_device_device_enqueue_capabilities);
+#endif
+
+          case CL_DEVICE_PIPE_SUPPORT: DEV_GET_INT_INF(cl_bool);
+#endif
+
 #ifdef CL_DEVICE_ME_VERSION_INTEL
           case CL_DEVICE_ME_VERSION_INTEL: DEV_GET_INT_INF(cl_uint);
 #endif
@@ -978,6 +1030,23 @@ namespace pyopencl
       }
 #endif
 
+#if PYOPENCL_CL_VERSION >= 0x2010
+      py::tuple device_and_host_timer() const
+      {
+        cl_ulong device_timestamp, host_timestamp;
+        PYOPENCL_CALL_GUARDED(clGetDeviceAndHostTimer,
+            (m_device, &device_timestamp, &host_timestamp));
+        return py::make_tuple(device_timestamp, host_timestamp);
+      }
+
+      cl_ulong host_timer() const
+      {
+        cl_ulong host_timestamp;
+        PYOPENCL_CALL_GUARDED(clGetHostTimer,
+            (m_device, &host_timestamp));
+        return host_timestamp;
+      }
+#endif
   };
 
 
@@ -1161,6 +1230,10 @@ namespace pyopencl
 
         return major_ver << 12 | minor_ver << 4;
       }
+
+#if PYOPENCL_CL_VERSION >= 0x2010
+      void set_default_device_command_queue(device const &dev, command_queue const &queue);
+#endif
   };
 
 
@@ -1454,6 +1527,24 @@ namespace pyopencl
           case CL_QUEUE_PROPERTIES:
             PYOPENCL_GET_TYPED_INFO(CommandQueue, m_queue, param_name,
                 cl_command_queue_properties);
+#if PYOPENCL_CL_VERSION >= 0x2000
+          case CL_QUEUE_SIZE:
+            PYOPENCL_GET_TYPED_INFO(CommandQueue, m_queue, param_name,
+                cl_uint);
+#endif
+#if PYOPENCL_CL_VERSION >= 0x2010
+          case CL_QUEUE_DEVICE_DEFAULT:
+            PYOPENCL_GET_OPAQUE_INFO(
+                CommandQueue, m_queue, param_name, cl_command_queue, command_queue);
+#endif
+#if PYOPENCL_CL_VERSION >= 0x3000
+          case CL_QUEUE_PROPERTIES_ARRAY:
+            {
+              std::vector<cl_queue_properties> result;
+              PYOPENCL_GET_VEC_INFO(CommandQueue, m_queue, param_name, result);
+              PYOPENCL_RETURN_VECTOR(cl_queue_properties, result);
+            }
+#endif
 
           default:
             throw error("CommandQueue.get_info", CL_INVALID_VALUE);
@@ -1903,12 +1994,12 @@ namespace pyopencl
         if (retain)
           PYOPENCL_CALL_GUARDED(clRetainMemObject, (mem));
 
-        m_hostbuf = PYOPENCL_STD_MOVE_IF_NEW_BUF_INTF(hostbuf);
+        m_hostbuf = std::move(hostbuf);
       }
 
       memory_object(memory_object &src)
         : m_valid(true), m_mem(src.m_mem),
-        m_hostbuf(PYOPENCL_STD_MOVE_IF_NEW_BUF_INTF(src.m_hostbuf))
+        m_hostbuf(std::move(src.m_hostbuf))
       {
         PYOPENCL_CALL_GUARDED(clRetainMemObject, (m_mem));
       }
@@ -2046,7 +2137,7 @@ namespace pyopencl
   {
     public:
       buffer(cl_mem mem, bool retain, hostbuf_t hostbuf=hostbuf_t())
-        : memory_object(mem, retain, PYOPENCL_STD_MOVE_IF_NEW_BUF_INTF(hostbuf))
+        : memory_object(mem, retain, std::move(hostbuf))
       { }
 
 #if PYOPENCL_CL_VERSION >= 0x1010
@@ -2152,7 +2243,7 @@ namespace pyopencl
 
     try
     {
-      return new buffer(mem, false, PYOPENCL_STD_MOVE_IF_NEW_BUF_INTF(retained_buf_obj));
+      return new buffer(mem, false, std::move(retained_buf_obj));
     }
     catch (...)
     {
@@ -2465,7 +2556,7 @@ namespace pyopencl
   {
     public:
       image(cl_mem mem, bool retain, hostbuf_t hostbuf=hostbuf_t())
-        : memory_object(mem, retain, PYOPENCL_STD_MOVE_IF_NEW_BUF_INTF(hostbuf))
+        : memory_object(mem, retain, std::move(hostbuf))
       { }
 
       py::object get_image_info(cl_image_info param_name) const
@@ -2507,7 +2598,7 @@ namespace pyopencl
 #endif
 
           default:
-            throw error("MemoryObject.get_image_info", CL_INVALID_VALUE);
+            throw error("Image.get_image_info", CL_INVALID_VALUE);
         }
       }
   };
@@ -2715,7 +2806,7 @@ namespace pyopencl
 
     try
     {
-      return new image(mem, false, PYOPENCL_STD_MOVE_IF_NEW_BUF_INTF(retained_buf_obj));
+      return new image(mem, false, std::move(retained_buf_obj));
     }
     catch (...)
     {
@@ -2768,7 +2859,7 @@ namespace pyopencl
 
     try
     {
-      return new image(mem, false, PYOPENCL_STD_MOVE_IF_NEW_BUF_INTF(retained_buf_obj));
+      return new image(mem, false, std::move(retained_buf_obj));
     }
     catch (...)
     {
@@ -2986,6 +3077,80 @@ namespace pyopencl
   // }}}
 
 
+  // {{{ pipe
+
+  class pipe : public memory_object
+  {
+    public:
+      pipe(cl_mem mem, bool retain)
+        : memory_object(mem, retain)
+      { }
+
+      py::object get_pipe_info(cl_pipe_info param_name) const
+      {
+#if PYOPENCL_CL_VERSION >= 0x2000
+        switch (param_name)
+        {
+          case CL_PIPE_PACKET_SIZE:
+          case CL_PIPE_MAX_PACKETS:
+            PYOPENCL_GET_TYPED_INFO(Pipe, data(), param_name, cl_uint);
+
+          default:
+            throw error("Pipe.get_pipe_info", CL_INVALID_VALUE);
+        }
+#else
+        throw error("Pipes not available. PyOpenCL was not compiled against a CL2+ header.",
+            CL_INVALID_VALUE);
+#endif
+      }
+  };
+
+#if PYOPENCL_CL_VERSION >= 0x2000
+  inline
+  pipe *create_pipe(
+      context const &ctx,
+      cl_mem_flags flags,
+      cl_uint pipe_packet_size,
+      cl_uint pipe_max_packets,
+      py::sequence py_props)
+  {
+    PYOPENCL_STACK_CONTAINER(cl_pipe_properties, props, py::len(py_props) + 1);
+    {
+      size_t i = 0;
+      for (auto prop: py_props)
+        props[i++] = py::cast<cl_pipe_properties>(prop);
+      props[i++] = 0;
+    }
+
+    cl_int status_code;
+    PYOPENCL_PRINT_CALL_TRACE("clCreatePipe");
+
+    cl_mem mem = clCreatePipe(
+        ctx.data(),
+        flags,
+        pipe_packet_size,
+        pipe_max_packets,
+        PYOPENCL_STACK_CONTAINER_GET_PTR(props),
+        &status_code);
+
+    if (status_code != CL_SUCCESS)
+      throw pyopencl::error("Pipe", status_code);
+
+    try
+    {
+      return new pipe(mem, false);
+    }
+    catch (...)
+    {
+      PYOPENCL_CALL_GUARDED(clReleaseMemObject, (mem));
+      throw;
+    }
+}
+#endif
+
+  // }}}
+
+
   // {{{ maps
   class memory_map
   {
@@ -3591,6 +3756,23 @@ namespace pyopencl
           case CL_SAMPLER_NORMALIZED_COORDS:
             PYOPENCL_GET_TYPED_INFO(Sampler, m_sampler, param_name,
                 cl_bool);
+#if PYOPENCL_CL_VERSION >= 0x3000
+          case CL_SAMPLER_PROPERTIES:
+            {
+              std::vector<cl_sampler_properties> result;
+              PYOPENCL_GET_VEC_INFO(Sampler, m_sampler, param_name, result);
+              PYOPENCL_RETURN_VECTOR(cl_sampler_properties, result);
+            }
+#endif
+
+#ifdef CL_SAMPLER_MIP_FILTER_MODE_KHR
+          case CL_SAMPLER_MIP_FILTER_MODE_KHR:
+            PYOPENCL_GET_TYPED_INFO(Sampler, m_sampler, param_name,
+                cl_filter_mode);
+          case CL_SAMPLER_LOD_MIN_KHR:
+          case CL_SAMPLER_LOD_MAX_KHR:
+            PYOPENCL_GET_TYPED_INFO(Sampler, m_sampler, param_name, float);
+#endif
 
           default:
             throw error("Sampler.get_info", CL_INVALID_VALUE);
@@ -3648,8 +3830,7 @@ namespace pyopencl
             PYOPENCL_GET_OPAQUE_INFO(Program, m_program, param_name,
                 cl_context, context);
           case CL_PROGRAM_NUM_DEVICES:
-            PYOPENCL_GET_TYPED_INFO(Program, m_program, param_name,
-                cl_uint);
+            PYOPENCL_GET_TYPED_INFO(Program, m_program, param_name, cl_uint);
           case CL_PROGRAM_DEVICES:
             {
               std::vector<cl_device_id> result;
@@ -3719,6 +3900,15 @@ namespace pyopencl
           case CL_PROGRAM_KERNEL_NAMES:
             PYOPENCL_GET_STR_INFO(Program, m_program, param_name);
 #endif
+#if PYOPENCL_CL_VERSION >= 0x2010
+          case CL_PROGRAM_IL:
+            PYOPENCL_GET_STR_INFO(Program, m_program, param_name);
+#endif
+#if PYOPENCL_CL_VERSION >= 0x2020
+          case CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT:
+          case CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT:
+            PYOPENCL_GET_TYPED_INFO(Program, m_program, param_name, cl_bool);
+#endif
 
           default:
             throw error("Program.get_info", CL_INVALID_VALUE);
@@ -3806,6 +3996,16 @@ namespace pyopencl
              0, 0));
       }
 #endif
+
+#if PYOPENCL_CL_VERSION >= 0x2020
+      void set_specialization_constant(cl_uint spec_id, py::object py_buffer)
+      {
+        py_buffer_wrapper bufwrap;
+        bufwrap.get(py_buffer.ptr(), PyBUF_ANY_CONTIGUOUS);
+        PYOPENCL_CALL_GUARDED(clSetProgramSpecializationConstant,
+            (m_program, spec_id, bufwrap.m_buf.len, bufwrap.m_buf.buf));
+      }
+#endif
   };
 
 
@@ -4077,6 +4277,28 @@ namespace pyopencl
 
       PYOPENCL_EQUALITY_TESTS(kernel);
 
+#if PYOPENCL_CL_VERSION >= 0x2010
+      kernel *clone()
+      {
+        cl_int status_code;
+
+        PYOPENCL_PRINT_CALL_TRACE("clCloneKernel");
+        cl_kernel result = clCloneKernel(m_kernel, &status_code);
+        if (status_code != CL_SUCCESS)
+          throw pyopencl::error("clCloneKernel", status_code);
+
+        try
+        {
+          return new kernel(result, /* retain */ false);
+        }
+        catch (...)
+        {
+          PYOPENCL_CALL_GUARDED_CLEANUP(clReleaseKernel, (result));
+          throw;
+        }
+      }
+#endif
+
       void set_arg_null(cl_uint arg_index)
       {
         cl_mem m = 0;
@@ -4290,6 +4512,70 @@ namespace pyopencl
         }
       }
 #endif
+
+#if PYOPENCL_CL_VERSION >= 0x2010
+    py::object get_sub_group_info(
+        device const &dev,
+        cl_kernel_sub_group_info param_name,
+        py::object py_input_value)
+    {
+      switch (param_name)
+      {
+        // size_t * -> size_t
+        case CL_KERNEL_MAX_SUB_GROUP_SIZE_FOR_NDRANGE:
+        case CL_KERNEL_SUB_GROUP_COUNT_FOR_NDRANGE:
+          {
+            std::vector<size_t> input_value;
+            COPY_PY_LIST(size_t, input_value);
+
+            size_t param_value;
+            PYOPENCL_CALL_GUARDED(clGetKernelSubGroupInfo,
+                (m_kernel, dev.data(), param_name,
+                 input_value.size()*sizeof(input_value.front()),
+                 input_value.empty() ? nullptr : &input_value.front(),
+                 sizeof(param_value), &param_value, 0));
+
+            return py::cast(param_value);
+          }
+
+        // size_t -> size_t[]
+        case CL_KERNEL_LOCAL_SIZE_FOR_SUB_GROUP_COUNT:
+          {
+            size_t input_value = py::cast<size_t>(py_input_value);
+
+            std::vector<size_t> result;
+            size_t size;
+            PYOPENCL_CALL_GUARDED(clGetKernelSubGroupInfo,
+                (m_kernel, dev.data(), param_name,
+                 sizeof(input_value), &input_value,
+                 0, nullptr, &size));
+            result.resize(size / sizeof(result.front()));
+            PYOPENCL_CALL_GUARDED(clGetKernelSubGroupInfo,
+                (m_kernel, dev.data(), param_name,
+                 sizeof(input_value), &input_value,
+                 size, result.empty() ? nullptr : &result.front(), 0));
+
+            PYOPENCL_RETURN_VECTOR(size_t, result);
+          }
+
+        // () -> size_t
+        case CL_KERNEL_MAX_NUM_SUB_GROUPS:
+        case CL_KERNEL_COMPILE_NUM_SUB_GROUPS:
+          {
+            size_t param_value;
+            PYOPENCL_CALL_GUARDED(clGetKernelSubGroupInfo,
+                (m_kernel, dev.data(), param_name,
+                 0, nullptr,
+                 sizeof(param_value), &param_value, 0));
+
+            return py::cast(param_value);
+          }
+
+        default:
+          throw error("Kernel.get_sub_group_info", CL_INVALID_VALUE);
+      }
+  }
+#endif
   };
 
 
@@ -4457,7 +4743,7 @@ namespace pyopencl
   {
     public:
       gl_buffer(cl_mem mem, bool retain, hostbuf_t hostbuf=hostbuf_t())
-        : memory_object(mem, retain, PYOPENCL_STD_MOVE_IF_NEW_BUF_INTF(hostbuf))
+        : memory_object(mem, retain, std::move(hostbuf))
       { }
   };
 
@@ -4468,7 +4754,7 @@ namespace pyopencl
   {
     public:
       gl_renderbuffer(cl_mem mem, bool retain, hostbuf_t hostbuf=hostbuf_t())
-        : memory_object(mem, retain, PYOPENCL_STD_MOVE_IF_NEW_BUF_INTF(hostbuf))
+        : memory_object(mem, retain, std::move(hostbuf))
       { }
   };
 
@@ -4479,7 +4765,7 @@ namespace pyopencl
   {
     public:
       gl_texture(cl_mem mem, bool retain, hostbuf_t hostbuf=hostbuf_t())
-        : image(mem, retain, PYOPENCL_STD_MOVE_IF_NEW_BUF_INTF(hostbuf))
+        : image(mem, retain, std::move(hostbuf))
       { }
 
       py::object get_gl_texture_info(cl_gl_texture_info param_name)
@@ -4695,6 +4981,15 @@ namespace pyopencl
 
   // {{{ deferred implementation bits
 
+#if PYOPENCL_CL_VERSION >= 0x2010
+  inline void context::set_default_device_command_queue(device const &dev, command_queue const &queue)
+  {
+    PYOPENCL_CALL_GUARDED(clSetDefaultDeviceCommandQueue,
+        (m_context, dev.data(), queue.data()));
+  }
+#endif
+
+
   inline program *error::get_program() const
   {
     return new program(m_program, /* retain */ true);
@@ -4779,6 +5074,19 @@ namespace pyopencl
         PYOPENCL_GET_TYPED_INFO(MemObject, data(), param_name,
             size_t);
 #endif
+#if PYOPENCL_CL_VERSION >= 0x2000
+      case CL_MEM_USES_SVM_POINTER:
+        PYOPENCL_GET_TYPED_INFO(MemObject, data(), param_name,
+            cl_bool);
+#endif
+#if PYOPENCL_CL_VERSION >= 0x3000
+      case CL_MEM_PROPERTIES:
+            {
+              std::vector<cl_mem_properties> result;
+              PYOPENCL_GET_VEC_INFO(MemObject, data(), param_name, result);
+              PYOPENCL_RETURN_VECTOR(cl_mem_properties, result);
+            }
+#endif
 
       default:
         throw error("MemoryObjectHolder.get_info", CL_INVALID_VALUE);
diff --git a/src/wrap_cl_part_1.cpp b/src/wrap_cl_part_1.cpp
index 9079d058b8b6cf2f835613584b991ede77c66798..4b0ec771ef4f20dd3edfbc7065152d6492effc39 100644
--- a/src/wrap_cl_part_1.cpp
+++ b/src/wrap_cl_part_1.cpp
@@ -68,6 +68,10 @@ void pyopencl_expose_part_1(py::module &m)
       .DEF_SIMPLE_METHOD(create_sub_devices)
 #endif
       PYOPENCL_EXPOSE_TO_FROM_INT_PTR(cl_device_id)
+#if PYOPENCL_CL_VERSION >= 0x2010
+      .DEF_SIMPLE_METHOD(device_and_host_timer)
+      .DEF_SIMPLE_METHOD(host_timer)
+#endif
       ;
   }
 
@@ -99,6 +103,9 @@ void pyopencl_expose_part_1(py::module &m)
       .def(py::self != py::self)
       .def("__hash__", &cls::hash)
       PYOPENCL_EXPOSE_TO_FROM_INT_PTR(cl_context)
+#if PYOPENCL_CL_VERSION >= 0x2010
+      .DEF_SIMPLE_METHOD(set_default_device_command_queue)
+#endif
       ;
   }
 
diff --git a/src/wrap_cl_part_2.cpp b/src/wrap_cl_part_2.cpp
index 175b5aa5932a16ac0528bb7656778f179b490f6f..9644eea6785b2f668b480fd5404d977481c1e193 100644
--- a/src/wrap_cl_part_2.cpp
+++ b/src/wrap_cl_part_2.cpp
@@ -222,6 +222,36 @@ void pyopencl_expose_part_2(py::module &m)
 
   // }}}
 
+  // {{{ pipe
+
+  {
+    typedef pyopencl::pipe cls;
+    py::class_<cls, memory_object>(m, "Pipe", py::dynamic_attr())
+#if PYOPENCL_CL_VERSION >= 0x2000
+      .def(
+          py::init(
+            [](
+              context const &ctx,
+              cl_mem_flags flags,
+              cl_uint pipe_packet_size,
+              cl_uint pipe_max_packets,
+              py::sequence py_props)
+            {
+              return create_pipe(ctx, flags, pipe_packet_size, pipe_max_packets, py_props);
+            }),
+          py::arg("context"),
+          py::arg("flags"),
+          py::arg("packet_size"),
+          py::arg("max_packets"),
+          py::arg("properties")
+          )
+#endif
+      .DEF_SIMPLE_METHOD(get_pipe_info)
+      ;
+  }
+
+  // }}}
+
   // {{{ memory_map
   {
     typedef memory_map cls;
@@ -400,6 +430,11 @@ void pyopencl_expose_part_2(py::module &m)
           py::arg("options")="",
           py::arg("devices")=py::none()
           )
+#endif
+#if PYOPENCL_CL_VERSION >= 0x2020
+      .def("set_specialization_constant", &cls::set_specialization_constant,
+          py::arg("spec_id"),
+          py::arg("buffer"))
 #endif
       .def(py::self == py::self)
       .def(py::self != py::self)
@@ -427,6 +462,9 @@ void pyopencl_expose_part_2(py::module &m)
       .def(py::init<const program &, std::string const &>())
       .DEF_SIMPLE_METHOD(get_info)
       .DEF_SIMPLE_METHOD(get_work_group_info)
+#if PYOPENCL_CL_VERSION >= 0x2000
+      .DEF_SIMPLE_METHOD(clone)
+#endif
       .def("_set_arg_null", &cls::set_arg_null)
       .def("_set_arg_buf", &cls::set_arg_buf)
 #if PYOPENCL_CL_VERSION >= 0x2000
@@ -440,6 +478,13 @@ void pyopencl_expose_part_2(py::module &m)
       .def(py::self != py::self)
       .def("__hash__", &cls::hash)
       PYOPENCL_EXPOSE_TO_FROM_INT_PTR(cl_kernel)
+#if PYOPENCL_CL_VERSION >= 0x1020
+      .def("get_sub_group_info", &cls::get_sub_group_info,
+          py::arg("device"),
+          py::arg("param"),
+          py::arg("input_value")=py::none()
+          )
+#endif
       ;
   }
 
diff --git a/src/wrap_constants.cpp b/src/wrap_constants.cpp
index 4b40e85b922883a0699e4d0ca4ff57baf3784596..0cc20d8fc8e1a4bb1e25ec637ae31ccc147a8eb2 100644
--- a/src/wrap_constants.cpp
+++ b/src/wrap_constants.cpp
@@ -59,6 +59,8 @@ namespace
   class mem_object_type { };
   class mem_info { };
   class image_info { };
+  class pipe_info { };
+  class pipe_properties { };
   class addressing_mode { };
   class filter_mode { };
   class sampler_info { };
@@ -74,6 +76,7 @@ namespace
   class kernel_arg_access_qualifier { };
   class kernel_arg_type_qualifier { };
   class kernel_work_group_info { };
+  class kernel_sub_group_info { };
   class event_info { };
   class command_type { };
   class command_execution_status { };
@@ -83,6 +86,11 @@ namespace
 
   class device_partition_property { };
   class device_affinity_domain { };
+  class device_atomic_capabilities { };
+  class device_device_enqueue_capabilities { };
+
+  class version_bits { };
+  class khronos_vendor_id { };
 
   class gl_object_type { };
   class gl_texture_info { };
@@ -235,6 +243,11 @@ void pyopencl_expose_constants(py::module &m)
     ADD_ATTR(, INVALID_DEVICE_QUEUE);
 #endif
 
+#if PYOPENCL_CL_VERSION >= 0x2020
+    ADD_ATTR(, INVALID_SPEC_ID);
+    ADD_ATTR(, MAX_SIZE_RESTRICTION_EXCEEDED);
+#endif
+
 #if defined(cl_ext_device_fission) && defined(PYOPENCL_USE_DEVICE_FISSION)
     ADD_ATTR(, DEVICE_PARTITION_FAILED_EXT);
     ADD_ATTR(, INVALID_PARTITION_COUNT_EXT);
@@ -250,6 +263,13 @@ void pyopencl_expose_constants(py::module &m)
     ADD_ATTR(PLATFORM_, VENDOR);
 #if !(defined(CL_PLATFORM_NVIDIA) && CL_PLATFORM_NVIDIA == 0x3001)
     ADD_ATTR(PLATFORM_, EXTENSIONS);
+#endif
+#if PYOPENCL_CL_VERSION >= 0x2010
+    ADD_ATTR(PLATFORM_, HOST_TIMER_RESOLUTION);
+#endif
+#if PYOPENCL_CL_VERSION >= 0x3000
+    ADD_ATTR(PLATFORM_, NUMERIC_VERSION);
+    ADD_ATTR(PLATFORM_, EXTENSIONS_WITH_VERSION);
 #endif
   }
 
@@ -468,6 +488,27 @@ void pyopencl_expose_constants(py::module &m)
     ADD_ATTR(DEVICE_, IL_VERSION);
     ADD_ATTR(DEVICE_, MAX_NUM_SUB_GROUPS);
     ADD_ATTR(DEVICE_, SUB_GROUP_INDEPENDENT_FORWARD_PROGRESS);
+#endif
+#if PYOPENCL_CL_VERSION >= 0x3000
+    ADD_ATTR(DEVICE_, NUMERIC_VERSION);
+    ADD_ATTR(DEVICE_, EXTENSIONS_WITH_VERSION);
+    ADD_ATTR(DEVICE_, ILS_WITH_VERSION);
+    ADD_ATTR(DEVICE_, BUILT_IN_KERNELS_WITH_VERSION);
+    ADD_ATTR(DEVICE_, ATOMIC_MEMORY_CAPABILITIES);
+    ADD_ATTR(DEVICE_, ATOMIC_FENCE_CAPABILITIES);
+    ADD_ATTR(DEVICE_, NON_UNIFORM_WORK_GROUP_SUPPORT);
+    ADD_ATTR(DEVICE_, OPENCL_C_ALL_VERSIONS);
+    ADD_ATTR(DEVICE_, PREFERRED_WORK_GROUP_SIZE_MULTIPLE);
+    ADD_ATTR(DEVICE_, WORK_GROUP_COLLECTIVE_FUNCTIONS_SUPPORT);
+    ADD_ATTR(DEVICE_, GENERIC_ADDRESS_SPACE_SUPPORT);
+    ADD_ATTR(DEVICE_, OPENCL_C_FEATURES);
+#ifdef CL_DEVICE_DEVICE_ENQUEUE_SUPPORT
+    // some busted headers shipped by Debian have this
+    cls.attr("DEVICE_ENQUEUE_CAPABILITIES") = CL_DEVICE_DEVICE_ENQUEUE_SUPPORT;
+#else
+    ADD_ATTR(DEVICE_, DEVICE_ENQUEUE_CAPABILITIES);
+#endif
+    ADD_ATTR(DEVICE_, PIPE_SUPPORT);
 #endif
     /* cl_intel_advanced_motion_estimation */
 #ifdef CL_DEVICE_ME_VERSION_INTEL
@@ -616,6 +657,9 @@ void pyopencl_expose_constants(py::module &m)
     ADD_ATTR(QUEUE_, DEVICE);
     ADD_ATTR(QUEUE_, REFERENCE_COUNT);
     ADD_ATTR(QUEUE_, PROPERTIES);
+#if PYOPENCL_CL_VERSION >= 0x3000
+    ADD_ATTR(QUEUE_, PROPERTIES_ARRAY);
+#endif
   }
 
   {
@@ -623,6 +667,9 @@ void pyopencl_expose_constants(py::module &m)
 #if PYOPENCL_CL_VERSION >= 0x2000
     ADD_ATTR(QUEUE_, PROPERTIES);
     ADD_ATTR(QUEUE_, SIZE);
+#endif
+#if PYOPENCL_CL_VERSION >= 0x2010
+    ADD_ATTR(QUEUE_, DEVICE_DEFAULT);
 #endif
   }
 
@@ -700,6 +747,12 @@ void pyopencl_expose_constants(py::module &m)
     ADD_ATTR( , UNSIGNED_INT32);
     ADD_ATTR( , HALF_FLOAT);
     ADD_ATTR( , FLOAT);
+#if PYOPENCL_CL_VERSION >= 0x1020
+    ADD_ATTR( , UNORM_INT24);
+#endif
+#if PYOPENCL_CL_VERSION >= 0x2010
+    ADD_ATTR( , UNORM_INT_101010_2);
+#endif
   }
 
   {
@@ -733,6 +786,9 @@ void pyopencl_expose_constants(py::module &m)
 #endif
 #if PYOPENCL_CL_VERSION >= 0x2000
     ADD_ATTR(MEM_, USES_SVM_POINTER);
+#endif
+#if PYOPENCL_CL_VERSION >= 0x3000
+    ADD_ATTR(MEM_, PROPERTIES);
 #endif
   }
 
@@ -753,6 +809,24 @@ void pyopencl_expose_constants(py::module &m)
 #endif
   }
 
+  {
+    py::class_<pipe_info> cls(m, "pipe_info");
+#if PYOPENCL_CL_VERSION >= 0x2000
+    ADD_ATTR(PIPE_, PACKET_SIZE);
+    ADD_ATTR(PIPE_, MAX_PACKETS);
+#endif
+#if PYOPENCL_CL_VERSION >= 0x3000
+    ADD_ATTR(PIPE_, PROPERTIES);
+#endif
+  }
+
+  {
+    py::class_<pipe_properties> cls(m, "pipe_properties");
+#if PYOPENCL_CL_VERSION >= 0x2000
+    ADD_ATTR(PIPE_, PACKET_SIZE);
+    ADD_ATTR(PIPE_, MAX_PACKETS);
+#endif
+  }
   {
     py::class_<addressing_mode> cls(m, "addressing_mode");
     ADD_ATTR(ADDRESS_, NONE);
@@ -782,6 +856,16 @@ void pyopencl_expose_constants(py::module &m)
     ADD_ATTR(SAMPLER_, LOD_MIN);
     ADD_ATTR(SAMPLER_, LOD_MAX);
 #endif
+#if PYOPENCL_CL_VERSION >= 0x3000
+    ADD_ATTR(SAMPLER_, PROPERTIES);
+#endif
+// {{{ cl_khr_mipmap_image
+#ifdef CL_SAMPLER_MIP_FILTER_MODE_KHR
+    ADD_ATTR(SAMPLER_, MIP_FILTER_MODE_KHR);
+    ADD_ATTR(SAMPLER_, LOD_MIN_KHR);
+    ADD_ATTR(SAMPLER_, LOD_MAX_KHR);
+#endif
+// }}}
   }
 
   {
@@ -812,6 +896,13 @@ void pyopencl_expose_constants(py::module &m)
 #if PYOPENCL_CL_VERSION >= 0x1020
     ADD_ATTR(PROGRAM_, NUM_KERNELS);
     ADD_ATTR(PROGRAM_, KERNEL_NAMES);
+#endif
+#if PYOPENCL_CL_VERSION >= 0x2010
+    ADD_ATTR(PROGRAM_, IL);
+#endif
+#if PYOPENCL_CL_VERSION >= 0x2020
+    ADD_ATTR(PROGRAM_, SCOPE_GLOBAL_CTORS_PRESENT);
+    ADD_ATTR(PROGRAM_, SCOPE_GLOBAL_DTORS_PRESENT);
 #endif
   }
 
@@ -911,6 +1002,17 @@ void pyopencl_expose_constants(py::module &m)
 #endif
   }
 
+  {
+    py::class_<kernel_sub_group_info> cls(m, "kernel_sub_group_info");
+#if PYOPENCL_CL_VERSION >= 0x2010
+    ADD_ATTR(KERNEL_, MAX_SUB_GROUP_SIZE_FOR_NDRANGE);
+    ADD_ATTR(KERNEL_, SUB_GROUP_COUNT_FOR_NDRANGE);
+    ADD_ATTR(KERNEL_, LOCAL_SIZE_FOR_SUB_GROUP_COUNT);
+    ADD_ATTR(KERNEL_, MAX_NUM_SUB_GROUPS);
+    ADD_ATTR(KERNEL_, COMPILE_NUM_SUB_GROUPS);
+#endif
+  }
+
   {
     py::class_<event_info> cls(m, "event_info");
     ADD_ATTR(EVENT_, COMMAND_QUEUE);
@@ -959,6 +1061,9 @@ void pyopencl_expose_constants(py::module &m)
     ADD_ATTR(COMMAND_, SVM_MEMFILL);
     ADD_ATTR(COMMAND_, SVM_MAP);
     ADD_ATTR(COMMAND_, SVM_UNMAP);
+#endif
+#if PYOPENCL_CL_VERSION >= 0x3000
+    ADD_ATTR(COMMAND_, SVM_MIGRATE_MEM);
 #endif
   }
 
@@ -1022,6 +1127,47 @@ void pyopencl_expose_constants(py::module &m)
 #endif
   }
 
+  {
+    py::class_<device_atomic_capabilities> cls(m, "device_atomic_capabilities");
+#if PYOPENCL_CL_VERSION >= 0x3000
+    ADD_ATTR(DEVICE_ATOMIC_, ORDER_RELAXED);
+    ADD_ATTR(DEVICE_ATOMIC_, ORDER_ACQ_REL);
+    ADD_ATTR(DEVICE_ATOMIC_, ORDER_SEQ_CST);
+    ADD_ATTR(DEVICE_ATOMIC_, SCOPE_WORK_ITEM);
+    ADD_ATTR(DEVICE_ATOMIC_, SCOPE_WORK_GROUP);
+    ADD_ATTR(DEVICE_ATOMIC_, SCOPE_DEVICE);
+    ADD_ATTR(DEVICE_ATOMIC_, SCOPE_ALL_DEVICES);
+#endif
+  }
+  {
+    py::class_<device_device_enqueue_capabilities> cls(m, "device_device_enqueue_capabilities");
+#if (PYOPENCL_CL_VERSION >= 0x3000) && defined(CL_DEVICE_DEVICE_ENQUEUE_CAPABILITIES)
+    ADD_ATTR(DEVICE_QUEUE_, SUPPORTED);
+    ADD_ATTR(DEVICE_QUEUE_, REPLACEABLE_DEFAULT);
+#endif
+  }
+
+  {
+    py::class_<version_bits> cls(m, "version_bits");
+#if PYOPENCL_CL_VERSION >= 0x3000
+    ADD_ATTR(VERSION_, MAJOR_BITS);
+    ADD_ATTR(VERSION_, MINOR_BITS);
+    ADD_ATTR(VERSION_, PATCH_BITS);
+
+    ADD_ATTR(VERSION_, MAJOR_MASK);
+    ADD_ATTR(VERSION_, MINOR_MASK);
+    ADD_ATTR(VERSION_, PATCH_MASK);
+#endif
+  }
+
+  {
+    py::class_<khronos_vendor_id> cls(m, "khronos_vendor_id");
+#if PYOPENCL_CL_VERSION >= 0x3000
+    ADD_ATTR(KHRONOS_VENDOR_ID_, CODEPLAY);
+#endif
+  }
+
+
 #ifdef HAVE_GL
   {
     py::class_<gl_object_type> cls(m, "gl_object_type");
@@ -1040,6 +1186,40 @@ void pyopencl_expose_constants(py::module &m)
 
   // }}}
 
+  // {{{ cl_name_version
+#if PYOPENCL_CL_VERSION >= 0x3000
+  {
+    typedef cl_name_version cls;
+    py::class_<cls>(m, "NameVersion")
+      .def(py::init(
+            [](cl_version version, const char* name)
+            {
+              cl_name_version result;
+              result.version = version;
+              result.name[0] = '\0';
+              // https://stackoverflow.com/a/1258577
+              strncat(result.name, name, CL_NAME_VERSION_MAX_NAME_SIZE-1);
+              return result;
+            }),
+          py::arg("version")=0,
+          py::arg("name")=0)
+
+      .def_property("version",
+          [](cls &t) { return t.version; },
+          [](cls &t, cl_version val) { t.version = val; })
+      .def_property("name",
+          [](cls &t) { return t.name; },
+          [](cls &t, const char *name)
+          {
+              t.name[0] = '\0';
+              // https://stackoverflow.com/a/1258577
+              strncat(t.name, name, CL_NAME_VERSION_MAX_NAME_SIZE-1);
+          })
+      ;
+  }
+#endif
+  // }}}
+
   // {{{ CL_DEVICE_TOPOLOGY_AMD
 
 #ifdef CL_DEVICE_TOPOLOGY_AMD
@@ -1050,6 +1230,7 @@ void pyopencl_expose_constants(py::module &m)
             [](cl_char bus, cl_char device, cl_char function)
             {
               cl_device_topology_amd result;
+              result.pcie.type = CL_DEVICE_TOPOLOGY_TYPE_PCIE_AMD;
               result.pcie.bus = bus;
               result.pcie.device = device;
               result.pcie.function = function;