From 3ad1a3ae5be1588fd05aeb2022ea2f33c0d39eaf Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Keck <Jean-Baptiste.Keck@imag.fr>
Date: Thu, 18 May 2017 17:59:40 +0200
Subject: [PATCH] custom atomics

---
 hysop/__init__.py                             |  12 +-
 .../device/codegen/base/cl_extensions.py      |  11 +-
 .../device/codegen/base/struct_codegen.py     |   4 +-
 .../device/codegen/base/union_codegen.py      |   7 +-
 .../backend/device/codegen/base/variables.py  | 103 +++++++++++------
 .../codegen/functions/custom_atomics.py       | 109 ++++++++++++++++++
 .../codegen/functions/stretching_rhs.py       |  10 +-
 .../backend/device/codegen/unions/__init__.py |   0
 .../device/codegen/unions/float_int.py        |  71 ++++++++++++
 hysop/backend/device/codegen/unions/xaa       |  73 ++++++++++++
 hysop/backend/device/opencl/opencl_types.py   |  25 +++-
 11 files changed, 370 insertions(+), 55 deletions(-)
 create mode 100644 hysop/backend/device/codegen/functions/custom_atomics.py
 create mode 100644 hysop/backend/device/codegen/unions/__init__.py
 create mode 100644 hysop/backend/device/codegen/unions/float_int.py
 create mode 100644 hysop/backend/device/codegen/unions/xaa

diff --git a/hysop/__init__.py b/hysop/__init__.py
index 19f86c98e..b5afb0684 100644
--- a/hysop/__init__.py
+++ b/hysop/__init__.py
@@ -17,16 +17,16 @@ __SCALES_ENABLED__ = "ON" is "ON"
 __OPTIMIZE__       = "OFF"       is "ON"
 
 __VERBOSE__        = True
-__DEBUG__          = False
-__TRACE__          = False
-__KERNEL_DEBUG__   = True
-__PROFILE__        = False
+__DEBUG__          = "ON"   in ["2", "3"]
+__TRACE__          = "ON"   in ["5"]
+__KERNEL_DEBUG__   = "ON"   in ["4", "3"]
+__PROFILE__        = "OFF" in ["0", "1"]
 
 __ENABLE_LONG_TESTS__ = "OFF" is "ON"
 
 # OpenCL
-__DEFAULT_PLATFORM_ID__ = 1
-__DEFAULT_DEVICE_ID__   = 1
+__DEFAULT_PLATFORM_ID__ = 0
+__DEFAULT_DEVICE_ID__   = 0
 
 
 
diff --git a/hysop/backend/device/codegen/base/cl_extensions.py b/hysop/backend/device/codegen/base/cl_extensions.py
index 3dc403c7c..26120e309 100644
--- a/hysop/backend/device/codegen/base/cl_extensions.py
+++ b/hysop/backend/device/codegen/base/cl_extensions.py
@@ -30,8 +30,15 @@ class ClExtCodeGen(OpenClCodeGenerator):
         with self._codeblock_('pragma_extensions'):
             if ext_name not in _cl_extension_custom_declarations.keys():
                 with self._align_() as al:
-                    base = '#pragma OPENCL EXTENSION {}$ : enable'
-                    al.append(base.format(ext_name))
+                    base = \
+'''
+#if {ext}
+    #pragma OPENCL EXTENSION {ext}$ : enable
+#else
+    #error Your OpenCL device is missing the {ext} extension!
+#endif
+'''
+                    al.append(base.format(ext=ext_name))
             else:
                 self.append(_cl_extension_custom_declarations[ext_name]); 
 
diff --git a/hysop/backend/device/codegen/base/struct_codegen.py b/hysop/backend/device/codegen/base/struct_codegen.py
index 0b63f281a..12f6754de 100644
--- a/hysop/backend/device/codegen/base/struct_codegen.py
+++ b/hysop/backend/device/codegen/base/struct_codegen.py
@@ -20,7 +20,7 @@ class StructCodeGenerator(OpenClCodeGenerator):
         
         self.typedef = typedef
         self.dtype = np.dtype(dtype)
-        self.ctype = self.typedef if self.typedef else self.name
+        self.ctype = self.typedef if self.typedef else 'struct {}'.format(self.name)
         
         cl.tools.get_or_register_dtype(self.ctype,self.dtype)
         register_ctype_dtype(self.ctype,self.dtype)
@@ -35,7 +35,7 @@ class StructCodeGenerator(OpenClCodeGenerator):
 
     def c_decl(self):
         dtype,cdecl = cl.tools.match_dtype_to_c_struct( \
-                self.device,self.ctype,self.dtype,self.context)
+                self.device,self.ctype.replace('struct',''),self.dtype,self.context)
         return cdecl
 
     def gencode(self, comments, ctype_overrides):
diff --git a/hysop/backend/device/codegen/base/union_codegen.py b/hysop/backend/device/codegen/base/union_codegen.py
index e1eb66ca3..52cb7cfd0 100644
--- a/hysop/backend/device/codegen/base/union_codegen.py
+++ b/hysop/backend/device/codegen/base/union_codegen.py
@@ -20,7 +20,7 @@ class UnionCodeGenerator(OpenClCodeGenerator):
         
         self.typedef = typedef
         self.dtype = np.dtype(dtype)
-        self.ctype = self.typedef if (self.typedef is not None) else self.name
+        self.ctype = self.typedef if (self.typedef is not None) else 'union {}'.format(self.name)
 
         cl.tools.get_or_register_dtype(self.ctype,self.dtype)
         register_ctype_dtype(self.ctype,self.dtype)
@@ -29,8 +29,11 @@ class UnionCodeGenerator(OpenClCodeGenerator):
     
     def c_decl(self):
         dtype,cdecl = cl.tools.match_dtype_to_c_struct( \
-                self.device,self.ctype,self.dtype,self.context)
+                self.device,self.ctype.replace('union',''),self.dtype,self.context)
         return cdecl
+    
+    def fields(self):
+        return self.dtype.fields
 
     def gencode(self, comments, ctype_overrides):
         union_vars = re.compile('\s+((?:struct\s+)?\w+)\s+((?:\s*\**(?:\w+)(?:\[\d+\])*[,;])+)')
diff --git a/hysop/backend/device/codegen/base/variables.py b/hysop/backend/device/codegen/base/variables.py
index 7dcb0322a..e6a600693 100644
--- a/hysop/backend/device/codegen/base/variables.py
+++ b/hysop/backend/device/codegen/base/variables.py
@@ -1,8 +1,7 @@
     
-import re
+from hysop.deps import np, re, copy
 import hysop.backend.device.opencl.opencl_types
 
-from hysop.constants import np
 from hysop.backend.device.codegen.base.utils import VarDict
 from hysop.backend.device.opencl.opencl_types import OpenClTypeGen
 
@@ -65,7 +64,7 @@ class CodegenVariable(object):
             value=None, svalue=None,
             const=False, add_impl_const=False,
             storage=None, nl=None, 
-            ptr=False, restrict=False,
+            ptr=False, restrict=False, volatile=False,
             init=None,
             symbolic_mode=False,struct_var=None):
 
@@ -100,19 +99,22 @@ class CodegenVariable(object):
         self.svalue  = svalue
 
         self.storage = storage
+        self.volatile = volatile
         self.nl = nl if (nl is not None) else (storage is not None)
         self.ptr = ptr
         self.struct_var = struct_var
         self.symbolic_mode = symbolic_mode
         self.init=init
         
-        const          = [const]          if isinstance(const,bool)          else list(const)
-        add_impl_const = [add_impl_const] if isinstance(add_impl_const,bool) else list(add_impl_const)
-        restrict       = [restrict]       if isinstance(restrict,bool)       else list(restrict)
+        const = [const] if isinstance(const,bool) else list(const)
+        add_impl_const = [add_impl_const] if isinstance(add_impl_const,bool) \
+                else list(add_impl_const)
+        restrict = [restrict] if isinstance(restrict,bool) else list(restrict)
         _len = max(max(len(const),len(add_impl_const)), len(restrict))
-        const          = np.asarray(const          + (_len-len(const))*[False],          dtype=bool)
-        add_impl_const = np.asarray(add_impl_const + (_len-len(add_impl_const))*[False], dtype=bool)
-        restrict       = np.asarray(restrict       + (_len-len(restrict))*[False],       dtype=bool)
+        const = np.asarray(const + (_len-len(const))*[False], dtype=bool)
+        add_impl_const = np.asarray(add_impl_const + (_len-len(add_impl_const))*[False], 
+                dtype=bool)
+        restrict = np.asarray(restrict + (_len-len(restrict))*[False], dtype=bool)
         if (const.size != add_impl_const.size) or (const.size != restrict.size):
             raise ValueError('const, add_impl_const, restrict size mismatch!')
         if not ptr and restrict.any():
@@ -120,7 +122,8 @@ class CodegenVariable(object):
         if not ptr and (const.size>1 or add_impl_const.size>1):
             raise ValueError('Non pointer types cannot have multiple const qualifiers!')
         if not ptr and np.logical_and(const, add_impl_const).any():
-            raise ValueError('Variable {} is const and add_impl_const has been specified!'.format(name))
+            raise ValueError('Variable {} is const and add_impl_const has been specified!'\
+                    .format(name))
         #if (struct_var is not None) and (struct_var.const) and (not const[0]):
             #raise ValueError('Variable {} declared in const struct {} is not const!'.format(name,struct_var.name))
         self.const          = const
@@ -128,7 +131,45 @@ class CodegenVariable(object):
         self.restrict       = restrict
 
 
+    def full_ctype(self, const=None, impl=False):
+        const = self.const if (const is None) else const
+        ctype = self.ctype
+
+        if not self.ptr:
+            if const or (impl and self.add_impl_const):
+                const = 'const '
+            else:
+                const = ''
+        else:
+            const = ' const' if const else ''#' '*6
+            ctype += '{} *'.format(const)
+            if impl and self.add_impl_const:
+                ctype += 'const '
+            const=''
+        
+        restrict = 'restrict' if self.restrict else ''
+        storage  = ('volatile ' if self.volatile else '')
+        storage  += (self.storage + ' ') if self.storage else ''
+        return '{storage}{const}{ctype}{restrict}'.format(storage=storage,
+                restrict=restrict,const=const,ctype=ctype)
     
+    def argument(self,impl,const=None):
+        name    = self.name
+        nl      = '\n' if self.nl else ''
+        return '{} {name}{nl}'.format(self.full_ctype(impl=impl,const=const),name=name,nl=nl)
+
+
+    def alias(self, name, ctype, 
+            const=None, volatile=None):
+        handle = copy.copy(self)
+        handle.ctype = ctype
+        handle.name=name
+        handle.const = const if (const is not None) else handle.const
+        handle.volatile = volatile if (volatile is not None) else handle.volatile
+        handle.init='({})({})'.format(handle.full_ctype(), self)
+        return handle
+
+
     def is_symbolic(self):
         return (self.value is None) or self.symbolic_mode
     def in_struct(self):
@@ -172,6 +213,7 @@ class CodegenVariable(object):
         
         storage = storage if (storage is not None) else self.storage
         storage = '{} '.format(storage) if (storage is not None) else ''
+        storage = '{}{}'.format('volatile ' if self.volatile else '', storage)
 
         if const is not None:
             self.const = np.asarray(self.const,bool)
@@ -182,7 +224,8 @@ class CodegenVariable(object):
             const = self.const
         
         if restrict is not None:
-            restrict = (restrict,)+(False,)*(self.restrict.size-1) if isinstance(restrict,bool) else restrict
+            restrict = (restrict,)+(False,)*(self.restrict.size-1) \
+                    if isinstance(restrict,bool) else restrict
             restrict = np.asarray(restrict,dtype=bool)
             restrict = np.logical_or(restrict, self.restrict)
         else:     
@@ -198,7 +241,8 @@ class CodegenVariable(object):
                     for (s,c,r) in zip(sshape,const,restrict)]
                 array_qualifiers = ''.join(qualifiers)
             else:
-                qualifiers = [('const' if c else '') + (' restrict' if r else '' ) for (c,r) in zip(const,restrict)]
+                qualifiers = [('const' if c else '') + (' restrict' if r else '' ) \
+                        for (c,r) in zip(const,restrict)]
                 impl_const = ' const' if self.add_impl_const[0] else ''
                 array_qualifiers = '{} *'.format(impl_const)+' *'.join(qualifiers)
 
@@ -232,31 +276,6 @@ class CodegenVariable(object):
             codegen.append(code)
         return code
 
-    def argument(self,impl,const=None):
-        const = self.const if (const is None) else const
-        ctype = self.ctype
-
-        if not self.ptr:
-            if const or (impl and self.add_impl_const):
-                const = 'const '
-            else:
-                const = ''
-        else:
-            const = ' const' if const else ' '*6
-            ctype += '{} *'.format(const)
-            if impl and self.add_impl_const:
-                ctype += 'const '
-            const=''
-        
-        restrict = 'restrict' if self.restrict else ''
-        storage  = (self.storage + ' ') if self.storage else ''
-        if self.storage=='__local':
-            storage+=' '
-        name    = self.name
-        nl      = '\n' if self.nl else ''
-
-        return '{storage}{const}{ctype}{restrict} {name}{nl}'.format(storage=storage,restrict=restrict,const=const,ctype=ctype,name=name,nl=nl)
-
     def __call__(self):
         return self.sval()
     def __getitem__(self,ss):
@@ -642,7 +661,15 @@ class CodegenStruct(CodegenVariable):
         if key in self.vars:
             return self.vars[key]
         else:
-            raise KeyError('Unknown field {} in struct {}.\nAvailable fields are: {}'.format(key,self.name,self.vars.keys()))
+            msg='Unknown field {} in struct {}.\nAvailable fields are: {}'
+            msg=msg.format(key,self.name,self.vars.keys())
+            raise KeyError(msg)
+
+    def __getattr__(self, name):
+        if name in self.vars:
+            return self.__getitem__(name)
+        else:
+            raise AttributeError
 
     def genvars(self,struct,var_overrides):
         if var_overrides is None:
diff --git a/hysop/backend/device/codegen/functions/custom_atomics.py b/hysop/backend/device/codegen/functions/custom_atomics.py
new file mode 100644
index 000000000..b1283303e
--- /dev/null
+++ b/hysop/backend/device/codegen/functions/custom_atomics.py
@@ -0,0 +1,109 @@
+
+
+from hysop.backend.device.codegen.base.opencl_codegen   import OpenClCodeGenerator
+from hysop.backend.device.codegen.base.function_codegen import OpenClFunctionCodeGenerator
+from hysop.backend.device.codegen.base.variables        import CodegenVariable, CodegenVectorClBuiltin
+from hysop.backend.device.opencl.opencl_types            import OpenClTypeGen
+from hysop.backend.device.codegen.base.utils            import WriteOnceDict, ArgDict
+
+class CustomAtomicFunction(OpenClFunctionCodeGenerator):
+    
+    def __init__(self,typegen,ftype,op,
+            storage='__global'):
+
+        reqs,union = self.build_union(ftype,typegen)
+
+        args = ArgDict()
+        args['p'] = CodegenVariable('p',ftype,add_impl_const=False,typegen=typegen,
+                ptr=True, volatile=True, storage=storage)
+        args['val'] = CodegenVariable('val',ftype,const=True,typegen=typegen)
+
+        fname = 'custom_atomic_{}_op{}'.format(union.name,hex(hash(op))[2:6])
+        super(CustomAtomicFunction,self).__init__(basename=fname, 
+                output=ftype,args=args,typegen=typegen)
+
+        self.op = op
+        self.ftype=ftype
+        self.storage=storage
+
+        if tg.opencl_version_greater(1,0):
+            if ftype=='float':
+                atomic_cmpxchg='atomic_cmpxchg({ptr},{intcmp},{val});'
+            elif ftype=='double':
+                self.declare_cl_extension('cl_khr_int64_base_atomics')
+                atomic_cmpxchg='atom_cmpxchg({ptr},{intcmp},{val});'
+            else:
+                raise NotImplementedError('Unknown ftype {}.'.format(ftype))
+        else:
+            if ftype=='float':
+                if storage=='__global':
+                    self.declare_cl_extension('cl_khr_global_int32_base_atomics')
+                elif storage=='__local':
+                    self.declare_cl_extension('cl_khr_local_int32_base_atomics')
+                else:
+                    raise NotImplementedError('Unknown storage {}.'.format(storage))
+            elif ftype=='double':
+                self.declare_cl_extension('cl_khr_int64_base_atomics')
+            else:
+                raise NotImplementedError('Unknown ftype {}.'.format(ftype))
+            atomic_cmpxchg='atom_cmpxchg({ptr},{intcmp},{val});'
+
+        self.update_requirements(reqs)
+        self.gencode(atomic_cmpxchg)
+
+    def build_union(self, ftype, tg):
+        from hysop.backend.device.codegen.unions.float_int import FloatIntegerUnion
+        reqs = WriteOnceDict()
+        union = FloatIntegerUnion(ftype, tg)
+        reqs['union'] = union
+        return reqs, union
+
+    def gencode(self, atomic_cmpxchg):
+        s = self
+        tg = s.typegen
+        op = s.op
+
+        p   = s.args['p']
+        val = s.args['val']
+        
+        union = self.reqs['union']
+        old_val = union.build_codegen_variable('old_val')
+        new_val = union.build_codegen_variable('new_val')
+        ret_val = union.build_codegen_variable('ret_val')
+        alias = p.alias(name='ip', ctype=old_val.intval.ctype,
+                volatile=False)
+        with s._function_():
+            old_val.declare(s)
+            new_val.declare(s)
+            ret_val.declare(s)
+            s.jumpline()
+            alias.declare(s)
+            with s._do_while_('{} != {}'.format(ret_val.intval, old_val.intval)):
+                load='{} = *{};'.format(old_val.floatval, p)
+                op='{} = {};'.format(new_val.floatval, op.format(old_val.floatval, val))
+                cmpxchg = '{} = {}'.format(ret_val.intval,
+                        atomic_cmpxchg.format(ptr=alias, intcmp=old_val.intval,
+                            val=new_val.intval))
+                s.append(load)
+                s.append(op)
+                s.append(cmpxchg)
+            s.jumpline()
+            s.append('return {};'.format(ret_val.floatval))
+
+
+
+           
+if __name__ == '__main__':
+    from hysop.backend.device.codegen.base.test import _test_typegen
+    tg = _test_typegen('double')
+    cg = OpenClCodeGenerator('main',tg)
+
+    h = CustomAtomicFunction(tg, 'float',  '{} + {}', '__global' )
+    f = CustomAtomicFunction(tg, 'double', '{} + {}', '__local' )
+    cg.require(h.name,f)
+    cg.require(f.name,f)
+
+    cg.edit()
+
+    cg.test_compile()
+
diff --git a/hysop/backend/device/codegen/functions/stretching_rhs.py b/hysop/backend/device/codegen/functions/stretching_rhs.py
index 140bebb15..fcb9f2bcb 100644
--- a/hysop/backend/device/codegen/functions/stretching_rhs.py
+++ b/hysop/backend/device/codegen/functions/stretching_rhs.py
@@ -49,11 +49,13 @@ class DirectionalStretchingRhsFunction(OpenClFunctionCodeGenerator):
         
         vtype = typegen.vtype(ftype,dim)
 
-        (args,basename) = self.build_prototype(typegen,dim,itype,ftype,vtype,order,direction,cached,
-                restrict,storage,vectorize_u,used_variables,formulation,is_conservative,is_periodic)
+        (args,basename) = self.build_prototype(typegen,dim,itype,ftype,vtype,order,
+                direction,cached,restrict,storage,vectorize_u,used_variables,
+                formulation,is_conservative,is_periodic)
 
-        reqs = self.build_requirements(typegen,dim,itype,ftype,vtype,order,direction,boundary,cached,
-                restrict,storage,vectorize_u,used_variables,is_conservative,is_periodic,args)
+        reqs = self.build_requirements(typegen,dim,itype,ftype,vtype,order,direction,
+                boundary,cached,restrict,storage,vectorize_u,used_variables,
+                is_conservative,is_periodic,args)
         
         super(DirectionalStretchingRhsFunction,self).__init__(basename=basename,
                 output=vtype,typegen=typegen,inline=True,
diff --git a/hysop/backend/device/codegen/unions/__init__.py b/hysop/backend/device/codegen/unions/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/hysop/backend/device/codegen/unions/float_int.py b/hysop/backend/device/codegen/unions/float_int.py
new file mode 100644
index 000000000..643d85f6e
--- /dev/null
+++ b/hysop/backend/device/codegen/unions/float_int.py
@@ -0,0 +1,71 @@
+
+
+from hysop.deps import np
+from hysop.tools.types import check_instance
+from hysop.backend.device.codegen.base.union_codegen import UnionCodeGenerator
+from hysop.backend.device.opencl.opencl_types  import OpenClTypeGen
+
+class FloatIntegerUnion(UnionCodeGenerator):
+    def __init__(self, ftype, typegen, typedef=None):
+
+        name,dtype,comments = self.build_dtype(typegen, ftype)
+
+        super(FloatIntegerUnion,self).__init__(name=name, 
+                dtype=dtype, 
+                typegen=typegen,
+                typedef=typedef, 
+                comments=comments)
+    
+    @staticmethod
+    def build_dtype(typegen, ftype):
+        tg = typegen
+
+        name = 'float_int'
+        
+        dtype = []
+        if ftype == 'half':
+            name+='16'
+            dtype.append(('intval', np.int16))
+            dtype.append(('uintval', np.uint16))
+            dtype.append(('floatval', np.float16))
+        elif ftype == 'float':
+            name+='32'
+            dtype.append(('intval', np.int32))
+            dtype.append(('uintval', np.uint32))
+            dtype.append(('floatval', np.float32))
+        elif ftype == 'double':
+            name+='64'
+            dtype.append(('intval', np.int64))
+            dtype.append(('uintval', np.uint64))
+            dtype.append(('floatval', np.float64))
+        else:
+            msg='Unknown ftype \'{}\', only half, float and double are supported.'
+            msg=msg.format(ftype)
+            raise ValueError(msg)
+        
+        comments = [
+                'Access value as a signed integer',
+                'Access value as a unsigned integer',
+                'Access value as a floating point',
+        ]
+
+        return name, dtype, comments
+
+
+if __name__ == '__main__':
+    from hysop.backend.device.codegen.base.opencl_codegen import OpenClCodeGenerator
+    from hysop.backend.device.codegen.base.test import _test_typegen
+
+    tg = _test_typegen()
+    
+    u1 = FloatIntegerUnion('double', tg)
+    u2 = FloatIntegerUnion('float', tg, 'custom32')
+
+    cg = OpenClCodeGenerator('test_generator',tg)
+    cg.declare_cl_extension('cl_khr_fp64') 
+    cg.require('u1',u1)
+    cg.require('u2',u2)
+
+    cg.edit()
+
+    cg.test_compile()
diff --git a/hysop/backend/device/codegen/unions/xaa b/hysop/backend/device/codegen/unions/xaa
new file mode 100644
index 000000000..e1eb66ca3
--- /dev/null
+++ b/hysop/backend/device/codegen/unions/xaa
@@ -0,0 +1,73 @@
+
+import pyopencl as cl
+import pyopencl.tools
+
+import numpy as np
+import re
+
+from hysop.backend.device.opencl.opencl_types     import np_dtype
+from hysop.backend.device.codegen.base.opencl_codegen   import OpenClCodeGenerator
+from hysop.backend.device.codegen.base.variables import VarDict, CodegenVariable, CodegenVector, CodegenStruct, CodegenVectorClBuiltin
+from hysop.backend.device.codegen.base.variables import register_ctype_dtype
+
+class UnionCodeGenerator(OpenClCodeGenerator):
+    def __init__(self,name,dtype,typegen,
+            typedef=None,comments=None,
+            ctype_overrides=None,
+            custom_types={}):
+        
+        super(UnionCodeGenerator,self).__init__(name=name,typegen=typegen)
+        
+        self.typedef = typedef
+        self.dtype = np.dtype(dtype)
+        self.ctype = self.typedef if (self.typedef is not None) else self.name
+
+        cl.tools.get_or_register_dtype(self.ctype,self.dtype)
+        register_ctype_dtype(self.ctype,self.dtype)
+
+        self.gencode(comments, ctype_overrides)
+    
+    def c_decl(self):
+        dtype,cdecl = cl.tools.match_dtype_to_c_struct( \
+                self.device,self.ctype,self.dtype,self.context)
+        return cdecl
+
+    def gencode(self, comments, ctype_overrides):
+        union_vars = re.compile('\s+((?:struct\s+)?\w+)\s+((?:\s*\**(?:\w+)(?:\[\d+\])*[,;])+)')
+        lines = self.c_decl().split('\n')
+        
+        with self._union_(name=self.name,typedef=self.typedef):
+            with self._var_block_() as vb:
+                i=0
+                for l in lines:
+                    match = union_vars.match(l)
+                    if match:
+                        ctype     = match.group(1)
+                        variables = match.group(2).replace(';','').split(',')
+                        if (ctype_overrides is not None) and (i in ctype_overrides.keys()):
+                            ctype = ctype_overrides[i]
+                        if comments is not None:
+                            vb.decl_var(ctype,','.join(variables), comment=comments[i])
+                        else:
+                            vb.decl_var(ctype,','.join(variables))
+                        i+=1
+
+    def build_codegen_variable(self, name, **kargs):
+        return CodegenStruct(varname=name, struct=self, **kargs)
+
+if __name__ == '__main__':
+
+    from hysop.backend.device.codegen.base.test import _test_typegen
+
+    dtype = []
+    dtype.append( ('f0', np.float32 ) )
+    dtype.append( ('d0', np.int32) )
+    dtype.append( ('v0', np_dtype('float4')) )
+    dtype.append( ('v1', np_dtype('int16')) )
+
+    scg = UnionCodeGenerator('TestUnion',dtype,typedef='TestUnion_s',
+            typegen=_test_typegen())
+    scg.edit()
+    scg.test_compile()
+    
+
diff --git a/hysop/backend/device/opencl/opencl_types.py b/hysop/backend/device/opencl/opencl_types.py
index 68ea14681..3a40d957a 100644
--- a/hysop/backend/device/opencl/opencl_types.py
+++ b/hysop/backend/device/opencl/opencl_types.py
@@ -1,6 +1,6 @@
 
 from hysop import __KERNEL_DEBUG__
-from hysop.deps import sm, np, it, string
+from hysop.deps import sm, np, it, string, re
 from hysop.backend.device.opencl import cl, clArray
 from hysop.tools.numerics import MPZ, MPQ, MPFR, F2Q
 
@@ -329,6 +329,29 @@ class OpenClTypeGen(TypeGen):
     def cl_requirements(self):
         return [self.float_base_type_require[self.fbtype]];
 
+    def opencl_version_greater(self, major, minor):
+        (cl_major, cl_minor) = self.opencl_version()
+        if cl_major < major:
+            return False
+        if (cl_major == major) and (cl_minor <= minor):
+            return False
+        return True
+
+    def opencl_version(self):
+        assert (self.device is not None)
+        sversion = self.device.version.strip()
+        _regexp='OpenCL\s+(\d)\.(\d)'
+        regexp=re.compile(_regexp)
+        match=re.match(regexp,sversion)
+        if not match:
+            msg='Could not extract OpenCL version from device returned version \'{}\' '
+            msg += 'and regular expression \'{}\'.'
+            msg=msg.format(sversion,_regexp)
+            raise RuntimeError(msg)
+        major = match.group(1)
+        minor = match.group(2)
+        return (major,minor)
+
     def dtype_from_str(self,stype):
         stype = stype.replace('ftype', self.fbtype).replace('fbtype',self.fbtype)
         btype = basetype(stype) 
-- 
GitLab