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