1#!/usr/bin/env python3
2
3# OpenCL built-in library: type conversion functions
4#
5# Copyright (c) 2013 Victor Oliveira <[email protected]>
6# Copyright (c) 2013 Jesse Towner <[email protected]>
7#
8# Permission is hereby granted, free of charge, to any person obtaining a copy
9# of this software and associated documentation files (the "Software"), to deal
10# in the Software without restriction, including without limitation the rights
11# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12# copies of the Software, and to permit persons to whom the Software is
13# furnished to do so, subject to the following conditions:
14#
15# The above copyright notice and this permission notice shall be included in
16# all copies or substantial portions of the Software.
17#
18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24# THE SOFTWARE.
25
26# This script generates the file convert_type.cl, which contains all of the
27# OpenCL functions in the form:
28#
29# convert_<destTypen><_sat><_roundingMode>(<sourceTypen>)
30
31types = ['char', 'uchar', 'short', 'ushort', 'int', 'uint', 'long', 'ulong', 'float', 'double']
32int_types = ['char', 'uchar', 'short', 'ushort', 'int', 'uint', 'long', 'ulong']
33unsigned_types = ['uchar', 'ushort', 'uint', 'ulong']
34float_types = ['float', 'double']
35int64_types = ['long', 'ulong']
36float64_types = ['double']
37vector_sizes = ['', '2', '3', '4', '8', '16']
38half_sizes = [('2',''), ('4','2'), ('8','4'), ('16','8')]
39
40saturation = ['','_sat']
41rounding_modes = ['_rtz','_rte','_rtp','_rtn']
42float_prefix = {'float':'FLT_', 'double':'DBL_'}
43float_suffix = {'float':'f', 'double':''}
44
45bool_type = {'char'  : 'char',
46             'uchar' : 'char',
47             'short' : 'short',
48             'ushort': 'short',
49             'int'   : 'int',
50             'uint'  : 'int',
51             'long'  : 'long',
52             'ulong' : 'long',
53             'float'  : 'int',
54             'double' : 'long'}
55
56unsigned_type = {'char'  : 'uchar',
57                 'uchar' : 'uchar',
58                 'short' : 'ushort',
59                 'ushort': 'ushort',
60                 'int'   : 'uint',
61                 'uint'  : 'uint',
62                 'long'  : 'ulong',
63                 'ulong' : 'ulong'}
64
65sizeof_type = {'char'  : 1, 'uchar'  : 1,
66               'short' : 2, 'ushort' : 2,
67               'int'   : 4, 'uint'   : 4,
68               'long'  : 8, 'ulong'  : 8,
69               'float' : 4, 'double' : 8}
70
71limit_max = {'char'  : 'CHAR_MAX',
72             'uchar' : 'UCHAR_MAX',
73             'short' : 'SHRT_MAX',
74             'ushort': 'USHRT_MAX',
75             'int'   : 'INT_MAX',
76             'uint'  : 'UINT_MAX',
77             'long'  : 'LONG_MAX',
78             'ulong' : 'ULONG_MAX'}
79
80limit_min = {'char'  : 'CHAR_MIN',
81             'uchar' : '0',
82             'short' : 'SHRT_MIN',
83             'ushort': '0',
84             'int'   : 'INT_MIN',
85             'uint'  : '0',
86             'long'  : 'LONG_MIN',
87             'ulong' : '0'}
88
89def conditional_guard(src, dst):
90  int64_count = 0
91  float64_count = 0
92  if src in int64_types:
93    int64_count = int64_count +1
94  elif src in float64_types:
95    float64_count = float64_count + 1
96  if dst in int64_types:
97    int64_count = int64_count +1
98  elif dst in float64_types:
99    float64_count = float64_count + 1
100  if float64_count > 0 and int64_count > 0:
101    print("#if defined(cl_khr_fp64) && defined(cles_khr_int64)")
102    return True
103  elif float64_count > 0:
104    print("#ifdef cl_khr_fp64")
105    return True
106  elif int64_count > 0:
107    print("#ifdef cles_khr_int64")
108    return True
109  return False
110
111
112print("""/* !!!! AUTOGENERATED FILE generated by convert_type.py !!!!!
113
114   DON'T CHANGE THIS FILE. MAKE YOUR CHANGES TO convert_type.py AND RUN:
115   $ ./generate-conversion-type-cl.sh
116
117   OpenCL type conversion functions
118
119   Copyright (c) 2013 Victor Oliveira <[email protected]>
120   Copyright (c) 2013 Jesse Towner <[email protected]>
121
122   Permission is hereby granted, free of charge, to any person obtaining a copy
123   of this software and associated documentation files (the "Software"), to deal
124   in the Software without restriction, including without limitation the rights
125   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
126   copies of the Software, and to permit persons to whom the Software is
127   furnished to do so, subject to the following conditions:
128
129   The above copyright notice and this permission notice shall be included in
130   all copies or substantial portions of the Software.
131
132   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
133   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
134   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
135   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
136   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
137   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
138   THE SOFTWARE.
139*/
140
141#include <clc/clc.h>
142
143#ifdef cl_khr_fp64
144#pragma OPENCL EXTENSION cl_khr_fp64 : enable
145#endif
146
147#define INFINITY 1.0f / 0.0f
148
149""")
150
151#
152# Default Conversions
153#
154# All conversions are in accordance with the OpenCL specification,
155# which cites the C99 conversion rules.
156#
157# Casting from floating point to integer results in conversions
158# with truncation, so it should be suitable for the default convert
159# functions.
160#
161# Conversions from integer to floating-point, and floating-point to
162# floating-point through casting is done with the default rounding
163# mode. While C99 allows dynamically changing the rounding mode
164# during runtime, it is not a supported feature in OpenCL according
165# to Section 7.1 - Rounding Modes in the OpenCL 1.2 specification.
166#
167# Therefore, we can assume for optimization purposes that the
168# rounding mode is fixed to round-to-nearest-even. Platform target
169# authors should ensure that the rounding-control registers remain
170# in this state, and that this invariant holds.
171#
172# Also note, even though the OpenCL specification isn't entirely
173# clear on this matter, we implement all rounding mode combinations
174# even for integer-to-integer conversions. When such a conversion
175# is used, the rounding mode is ignored.
176#
177
178def generate_default_conversion(src, dst, mode):
179  close_conditional = conditional_guard(src, dst)
180
181  # scalar conversions
182  print("""_CLC_DEF _CLC_OVERLOAD
183{DST} convert_{DST}{M}({SRC} x)
184{{
185  return ({DST})x;
186}}
187""".format(SRC=src, DST=dst, M=mode))
188
189  # vector conversions, done through decomposition to components
190  for size, half_size in half_sizes:
191    print("""_CLC_DEF _CLC_OVERLOAD
192{DST}{N} convert_{DST}{N}{M}({SRC}{N} x)
193{{
194  return ({DST}{N})(convert_{DST}{H}(x.lo), convert_{DST}{H}(x.hi));
195}}
196""".format(SRC=src, DST=dst, N=size, H=half_size, M=mode))
197
198  # 3-component vector conversions
199  print("""_CLC_DEF _CLC_OVERLOAD
200{DST}3 convert_{DST}3{M}({SRC}3 x)
201{{
202  return ({DST}3)(convert_{DST}2(x.s01), convert_{DST}(x.s2));
203}}""".format(SRC=src, DST=dst, M=mode))
204
205  if close_conditional:
206    print("#endif")
207
208
209for src in types:
210  for dst in types:
211    generate_default_conversion(src, dst, '')
212
213for src in int_types:
214  for dst in int_types:
215    for mode in rounding_modes:
216      generate_default_conversion(src, dst, mode)
217
218#
219# Saturated Conversions To Integers
220#
221# These functions are dependent on the unsaturated conversion functions
222# generated above, and use clamp, max, min, and select to eliminate
223# branching and vectorize the conversions.
224#
225# Again, as above, we allow all rounding modes for integer-to-integer
226# conversions with saturation.
227#
228
229def generate_saturated_conversion(src, dst, size):
230  # Header
231  close_conditional = conditional_guard(src, dst)
232  print("""_CLC_DEF _CLC_OVERLOAD
233{DST}{N} convert_{DST}{N}_sat({SRC}{N} x)
234{{""".format(DST=dst, SRC=src, N=size))
235
236  # FIXME: This is a work around for lack of select function with
237  # signed third argument when the first two arguments are unsigned types.
238  # We cast to the signed type for sign-extension, then do a bitcast to
239  # the unsigned type.
240  if dst in unsigned_types:
241    bool_prefix = "as_{DST}{N}(convert_{BOOL}{N}".format(DST=dst, BOOL=bool_type[dst], N=size);
242    bool_suffix = ")"
243  else:
244    bool_prefix = "convert_{BOOL}{N}".format(BOOL=bool_type[dst], N=size);
245    bool_suffix = ""
246
247  # Body
248  if src == dst:
249
250    # Conversion between same types
251    print("  return x;")
252
253  elif src in float_types:
254
255    # Conversion from float to int
256    print("""  {DST}{N} y = convert_{DST}{N}(x);
257  y = select(y, ({DST}{N}){DST_MIN}, {BP}(x < ({SRC}{N}){DST_MIN}){BS});
258  y = select(y, ({DST}{N}){DST_MAX}, {BP}(x > ({SRC}{N}){DST_MAX}){BS});
259  return y;""".format(SRC=src, DST=dst, N=size,
260      DST_MIN=limit_min[dst], DST_MAX=limit_max[dst],
261      BP=bool_prefix, BS=bool_suffix))
262
263  else:
264
265    # Integer to integer convesion with sizeof(src) == sizeof(dst)
266    if sizeof_type[src] == sizeof_type[dst]:
267      if src in unsigned_types:
268        print("  x = min(x, ({SRC}){DST_MAX});".format(SRC=src, DST_MAX=limit_max[dst]))
269      else:
270        print("  x = max(x, ({SRC})0);".format(SRC=src))
271
272    # Integer to integer conversion where sizeof(src) > sizeof(dst)
273    elif sizeof_type[src] > sizeof_type[dst]:
274      if src in unsigned_types:
275        print("  x = min(x, ({SRC}){DST_MAX});".format(SRC=src, DST_MAX=limit_max[dst]))
276      else:
277        print("  x = clamp(x, ({SRC}){DST_MIN}, ({SRC}){DST_MAX});"
278          .format(SRC=src, DST_MIN=limit_min[dst], DST_MAX=limit_max[dst]))
279
280    # Integer to integer conversion where sizeof(src) < sizeof(dst)
281    elif src not in unsigned_types and dst in unsigned_types:
282        print("  x = max(x, ({SRC})0);".format(SRC=src))
283
284    print("  return convert_{DST}{N}(x);".format(DST=dst, N=size))
285
286  # Footer
287  print("}")
288  if close_conditional:
289    print("#endif")
290
291
292for src in types:
293  for dst in int_types:
294    for size in vector_sizes:
295      generate_saturated_conversion(src, dst, size)
296
297
298def generate_saturated_conversion_with_rounding(src, dst, size, mode):
299  # Header
300  close_conditional = conditional_guard(src, dst)
301
302  # Body
303  print("""_CLC_DEF _CLC_OVERLOAD
304{DST}{N} convert_{DST}{N}_sat{M}({SRC}{N} x)
305{{
306  return convert_{DST}{N}_sat(x);
307}}
308""".format(DST=dst, SRC=src, N=size, M=mode))
309
310  # Footer
311  if close_conditional:
312    print("#endif")
313
314
315for src in int_types:
316  for dst in int_types:
317    for size in vector_sizes:
318      for mode in rounding_modes:
319        generate_saturated_conversion_with_rounding(src, dst, size, mode)
320
321#
322# Conversions To/From Floating-Point With Rounding
323#
324# Note that we assume as above that casts from floating-point to
325# integer are done with truncation, and that the default rounding
326# mode is fixed to round-to-nearest-even, as per C99 and OpenCL
327# rounding rules.
328#
329# These functions rely on the use of abs, ceil, fabs, floor,
330# nextafter, sign, rint and the above generated conversion functions.
331#
332# Only conversions to integers can have saturation.
333#
334
335def generate_float_conversion(src, dst, size, mode, sat):
336  # Header
337  close_conditional = conditional_guard(src, dst)
338  print("""_CLC_DEF _CLC_OVERLOAD
339{DST}{N} convert_{DST}{N}{S}{M}({SRC}{N} x)
340{{""".format(SRC=src, DST=dst, N=size, M=mode, S=sat))
341
342  # Perform conversion
343  if dst in int_types:
344    if mode == '_rte':
345      print("  x = rint(x);");
346    elif mode == '_rtp':
347      print("  x = ceil(x);");
348    elif mode == '_rtn':
349      print("  x = floor(x);");
350    print("  return convert_{DST}{N}{S}(x);".format(DST=dst, N=size, S=sat))
351  elif mode == '_rte':
352    print("  return convert_{DST}{N}(x);".format(DST=dst, N=size))
353  else:
354    print("  {DST}{N} r = convert_{DST}{N}(x);".format(DST=dst, N=size))
355    print("  {SRC}{N} y = convert_{SRC}{N}(y);".format(SRC=src, N=size))
356    if mode == '_rtz':
357      if src in int_types:
358        print("  {USRC}{N} abs_x = abs(x);".format(USRC=unsigned_type[src], N=size))
359        print("  {USRC}{N} abs_y = abs(y);".format(USRC=unsigned_type[src], N=size))
360      else:
361        print("  {SRC}{N} abs_x = fabs(x);".format(SRC=src, N=size))
362        print("  {SRC}{N} abs_y = fabs(y);".format(SRC=src, N=size))
363      print("  return select(r, nextafter(r, sign(r) * ({DST}{N})-INFINITY), convert_{BOOL}{N}(abs_y > abs_x));"
364        .format(DST=dst, N=size, BOOL=bool_type[dst]))
365    if mode == '_rtp':
366      print("  return select(r, nextafter(r, ({DST}{N})INFINITY), convert_{BOOL}{N}(y < x));"
367        .format(DST=dst, N=size, BOOL=bool_type[dst]))
368    if mode == '_rtn':
369      print("  return select(r, nextafter(r, ({DST}{N})-INFINITY), convert_{BOOL}{N}(y > x));"
370        .format(DST=dst, N=size, BOOL=bool_type[dst]))
371
372  # Footer
373  print("}")
374  if close_conditional:
375    print("#endif")
376
377
378for src in float_types:
379  for dst in int_types:
380    for size in vector_sizes:
381      for mode in rounding_modes:
382        for sat in saturation:
383          generate_float_conversion(src, dst, size, mode, sat)
384
385
386for src in types:
387  for dst in float_types:
388    for size in vector_sizes:
389      for mode in rounding_modes:
390        generate_float_conversion(src, dst, size, mode, '')
391