551 lines · python
1##===----------------------------------------------------------------------===##2#3# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4# See https://llvm.org/LICENSE.txt for license information.5# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6#7##===----------------------------------------------------------------------===##8#9# This script generates OpenCL type conversion builtins, which are all of the10# OpenCL functions in the form:11#12# <prefix>convert_<destTypen><_sat><_roundingMode>(<sourceTypen>)13#14# The internal "CLC" versions of these builtins, with the <prefix> '__clc_'15# contain the actual implementations. These are generated by passing the16# '--clc' flag to the script.17#18# The OpenCL builtins, without any prefix, forward on to the CLC versions.19#20##===----------------------------------------------------------------------===##21 22import argparse23from sys import stderr24from os import path25 26parser = argparse.ArgumentParser()27parser.add_argument(28 "--clc", action="store_true", help="Generate clc internal conversions"29)30parser.add_argument(31 "--clspv", action="store_true", help="Generate the clspv variant of the code"32)33args = parser.parse_args()34 35clc = args.clc36clspv = args.clspv37 38 39# We don't generate clspv-specific code for clc conversions - don't allow this40# accidentally (later checks rely on mutual exclusivity)41if clc and clspv:42 print("Error: clc and clspv conversions are mutually exclusive", file=stderr)43 exit(1)44 45 46types = [47 "char",48 "uchar",49 "short",50 "ushort",51 "int",52 "uint",53 "long",54 "ulong",55 "half",56 "float",57 "double",58]59int_types = ["char", "uchar", "short", "ushort", "int", "uint", "long", "ulong"]60unsigned_types = ["uchar", "ushort", "uint", "ulong"]61float_types = ["half", "float", "double"]62int64_types = ["long", "ulong"]63float64_types = ["double"]64float16_types = ["half"]65vector_sizes = ["", "2", "3", "4", "8", "16"]66half_sizes = [("2", ""), ("4", "2"), ("8", "4"), ("16", "8")]67 68saturation = ["", "_sat"]69rounding_modes = ["_rtz", "_rte", "_rtp", "_rtn"]70 71bool_type = {72 "char": "char",73 "uchar": "char",74 "short": "short",75 "ushort": "short",76 "int": "int",77 "uint": "int",78 "long": "long",79 "ulong": "long",80 "half": "short",81 "float": "int",82 "double": "long",83}84 85unsigned_type = {86 "char": "uchar",87 "uchar": "uchar",88 "short": "ushort",89 "ushort": "ushort",90 "int": "uint",91 "uint": "uint",92 "long": "ulong",93 "ulong": "ulong",94}95 96sizeof_type = {97 "char": 1,98 "uchar": 1,99 "short": 2,100 "ushort": 2,101 "int": 4,102 "uint": 4,103 "long": 8,104 "ulong": 8,105 "half": 2,106 "float": 4,107 "double": 8,108}109 110limit_max = {111 "char": "CHAR_MAX",112 "uchar": "UCHAR_MAX",113 "short": "SHRT_MAX",114 "ushort": "USHRT_MAX",115 "int": "INT_MAX",116 "uint": "UINT_MAX",117 "long": "LONG_MAX",118 "ulong": "ULONG_MAX",119 "half": "0x1.ffcp+15",120}121 122limit_min = {123 "char": "CHAR_MIN",124 "uchar": "0",125 "short": "SHRT_MIN",126 "ushort": "0",127 "int": "INT_MIN",128 "uint": "0",129 "long": "LONG_MIN",130 "ulong": "0",131 "half": "-0x1.ffcp+15",132}133 134 135def conditional_guard(src, dst):136 int64_count = 0137 float64_count = 0138 float16_count = 0139 if src in int64_types:140 int64_count = int64_count + 1141 elif src in float64_types:142 float64_count = float64_count + 1143 elif src in float16_types:144 float16_count = float16_count + 1145 if dst in int64_types:146 int64_count = int64_count + 1147 elif dst in float64_types:148 float64_count = float64_count + 1149 elif dst in float16_types:150 float16_count = float16_count + 1151 if float64_count > 0 and float16_count > 0:152 print("#if defined(cl_khr_fp16) && defined(cl_khr_fp64)")153 return True154 elif float64_count > 0:155 # In embedded profile, if cl_khr_fp64 is supported cles_khr_int64 has to be156 print("#ifdef cl_khr_fp64")157 return True158 elif float16_count > 0:159 print("#if defined cl_khr_fp16")160 return True161 elif int64_count > 0:162 print("#if defined cles_khr_int64 || !defined(__EMBEDDED_PROFILE__)")163 return True164 return False165 166 167nl = "\n"168includes = []169if not clc:170 includes = ["<clc/opencl/convert.h>"]171else:172 includes = sorted(173 [174 "<clc/internal/clc.h>",175 "<clc/integer/definitions.h>",176 "<clc/float/definitions.h>",177 "<clc/integer/clc_abs.h>",178 "<clc/common/clc_sign.h>",179 "<clc/shared/clc_clamp.h>",180 "<clc/shared/clc_min.h>",181 "<clc/shared/clc_max.h>",182 "<clc/math/clc_fabs.h>",183 "<clc/math/clc_rint.h>",184 "<clc/math/clc_ceil.h>",185 "<clc/math/clc_floor.h>",186 "<clc/math/clc_nextafter.h>",187 "<clc/relational/clc_select.h>",188 ]189 )190 191print(192 f"""//===----------------------------------------------------------------------===//193//194// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.195// See https://llvm.org/LICENSE.txt for license information.196// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception197//198//===----------------------------------------------------------------------===//199//200// Automatically generated from {path.basename(__file__)}, do not edit!201//202// OpenCL type conversion functions203//204//===----------------------------------------------------------------------===//205 206{nl.join(['#include ' + f for f in includes])}207#include <clc/clc_convert.h>208 209#ifdef cl_khr_fp16210#pragma OPENCL EXTENSION cl_khr_fp16 : enable211#endif212 213#ifdef cl_khr_fp64214#pragma OPENCL EXTENSION cl_khr_fp64 : enable215 216#if defined(__EMBEDDED_PROFILE__) && !defined(cles_khr_int64)217#error Embedded profile that supports cl_khr_fp64 also has to support cles_khr_int64218#endif219 220#endif221 222#ifdef cles_khr_int64223#pragma OPENCL EXTENSION cles_khr_int64 : enable224#endif225 226"""227)228 229 230#231# Default Conversions232#233# All conversions are in accordance with the OpenCL specification,234# which cites the C99 conversion rules.235#236# Casting from floating point to integer results in conversions237# with truncation, so it should be suitable for the default convert238# functions.239#240# Conversions from integer to floating-point, and floating-point to241# floating-point through casting is done with the default rounding242# mode. While C99 allows dynamically changing the rounding mode243# during runtime, it is not a supported feature in OpenCL according244# to Section 7.1 - Rounding Modes in the OpenCL 1.2 specification.245#246# Therefore, we can assume for optimization purposes that the247# rounding mode is fixed to round-to-nearest-even. Platform target248# authors should ensure that the rounding-control registers remain249# in this state, and that this invariant holds.250#251# Also note, even though the OpenCL specification isn't entirely252# clear on this matter, we implement all rounding mode combinations253# even for integer-to-integer conversions. When such a conversion254# is used, the rounding mode is ignored.255#256def print_passthru_conversion(src_ty, dst_ty, fn_name):257 print(258 f"""_CLC_DEF _CLC_OVERLOAD {dst_ty} {fn_name}({src_ty} x) {{259 return __clc_{fn_name}(x);260}}261"""262 )263 264 265def generate_default_conversion(src, dst, mode):266 close_conditional = conditional_guard(src, dst)267 268 for size in vector_sizes:269 if not size:270 if clc:271 print(272 f"""_CLC_DEF _CLC_OVERLOAD {dst} __clc_convert_{dst}{mode}({src} x) {{273 return ({dst})x;274}}275"""276 )277 else:278 print_passthru_conversion(src, dst, f"convert_{dst}{mode}")279 else:280 if clc:281 print(282 f"""_CLC_DEF _CLC_OVERLOAD {dst}{size} __clc_convert_{dst}{size}{mode}({src}{size} x) {{283 return __builtin_convertvector(x, {dst}{size});284}}285"""286 )287 else:288 print_passthru_conversion(289 f"{src}{size}", f"{dst}{size}", f"convert_{dst}{size}{mode}"290 )291 292 if close_conditional:293 print("#endif")294 295 296# Do not generate user-facing default conversions for clspv as they are handled297# natively298if not clspv:299 for src in types:300 for dst in types:301 generate_default_conversion(src, dst, "")302 303for src in int_types:304 for dst in int_types:305 for mode in rounding_modes:306 # Do not generate user-facing "_rte" conversions for clspv as they307 # are handled natively308 if clspv and mode == "_rte":309 continue310 generate_default_conversion(src, dst, mode)311 312#313# Saturated Conversions To Integers314 315 316# These functions are dependent on the unsaturated conversion functions317# generated above, and use clamp, max, min, and select to eliminate318# branching and vectorize the conversions.319#320# Again, as above, we allow all rounding modes for integer-to-integer321# conversions with saturation.322#323def generate_saturated_conversion(src, dst, size):324 # Header325 close_conditional = conditional_guard(src, dst)326 327 dstn = f"{dst}{size}"328 srcn = f"{src}{size}"329 330 if not clc:331 print_passthru_conversion(f"{srcn}", f"{dstn}", f"convert_{dstn}_sat")332 if close_conditional:333 print("#endif")334 return335 336 print(f"_CLC_DEF _CLC_OVERLOAD {dstn} __clc_convert_{dstn}_sat({srcn} x) {{")337 338 # FIXME: This is a work around for lack of select function with signed339 # third argument when the first two arguments are unsigned types. We cast340 # to the signed type for sign-extension, then do a bitcast to the unsigned341 # type.342 if dst in unsigned_types:343 bool_prefix = f"__clc_as_{dstn}(__clc_convert_{bool_type[dst]}{size}"344 bool_suffix = ")"345 else:346 bool_prefix = f"__clc_convert_{bool_type[dst]}{size}"347 bool_suffix = ""348 349 dst_max = limit_max[dst]350 dst_min = limit_min[dst]351 352 # Body353 if src == dst:354 # Conversion between same types355 print(" return x;")356 357 elif src in float_types:358 # Conversion from float to int359 print(360 f""" {dstn} y = __clc_convert_{dstn}(x);361 y = __clc_select(y, ({dstn}){dst_min}, {bool_prefix}(x <= ({srcn}){dst_min}){bool_suffix});362 y = __clc_select(y, ({dstn}){dst_max}, {bool_prefix}(x >= ({srcn}){dst_max}){bool_suffix});363 return y;"""364 )365 else:366 # Integer to integer convesion with sizeof(src) == sizeof(dst)367 if sizeof_type[src] == sizeof_type[dst]:368 if src in unsigned_types:369 print(f" x = __clc_min(x, ({src}){dst_max});")370 else:371 print(f" x = __clc_max(x, ({src})0);")372 373 # Integer to integer conversion where sizeof(src) > sizeof(dst)374 elif sizeof_type[src] > sizeof_type[dst]:375 if src in unsigned_types:376 print(f" x = __clc_min(x, ({src}){dst_max});")377 else:378 print(f" x = __clc_clamp(x, ({src}){dst_min}, ({src}){dst_max});")379 380 # Integer to integer conversion where sizeof(src) < sizeof(dst)381 elif src not in unsigned_types and dst in unsigned_types:382 print(f" x = __clc_max(x, ({src})0);")383 384 print(f" return __clc_convert_{dstn}(x);")385 386 # Footer387 print("}")388 if close_conditional:389 print("#endif")390 391 392for src in types:393 for dst in int_types:394 for size in vector_sizes:395 generate_saturated_conversion(src, dst, size)396 397 398def generate_saturated_conversion_with_rounding(src, dst, size, mode):399 # Header400 close_conditional = conditional_guard(src, dst)401 402 dstn = f"{dst}{size}"403 srcn = f"{src}{size}"404 405 if not clc:406 print_passthru_conversion(f"{srcn}", f"{dstn}", f"convert_{dstn}_sat{mode}")407 else:408 # Body409 print(410 f"""_CLC_DEF _CLC_OVERLOAD {dstn} __clc_convert_{dstn}_sat{mode}({srcn} x) {{411 return __clc_convert_{dstn}_sat(x);412}}413"""414 )415 416 # Footer417 if close_conditional:418 print("#endif")419 420 421for src in int_types:422 for dst in int_types:423 for size in vector_sizes:424 for mode in rounding_modes:425 generate_saturated_conversion_with_rounding(src, dst, size, mode)426 427 428#429# Conversions To/From Floating-Point With Rounding430#431# Note that we assume as above that casts from floating-point to432# integer are done with truncation, and that the default rounding433# mode is fixed to round-to-nearest-even, as per C99 and OpenCL434# rounding rules.435#436# These functions rely on the use of abs, ceil, fabs, floor,437# nextafter, sign, rint and the above generated conversion functions.438#439# Only conversions to integers can have saturation.440#441def generate_float_conversion(src, dst, size, mode, sat):442 # Header443 close_conditional = conditional_guard(src, dst)444 445 dstn = f"{dst}{size}"446 srcn = f"{src}{size}"447 booln = f"{bool_type[dst]}{size}"448 src_max = limit_max[src] if src in limit_max else ""449 dst_min = limit_min[dst] if dst in limit_min else ""450 451 if not clc:452 print_passthru_conversion(f"{srcn}", f"{dstn}", f"convert_{dstn}{sat}{mode}")453 # Footer454 if close_conditional:455 print("#endif")456 return457 458 print(f"_CLC_DEF _CLC_OVERLOAD {dstn} __clc_convert_{dstn}{sat}{mode}({srcn} x) {{")459 460 # Perform conversion461 if dst in int_types:462 if mode == "_rte":463 print(" x = __clc_rint(x);")464 elif mode == "_rtp":465 print(" x = __clc_ceil(x);")466 elif mode == "_rtn":467 print(" x = __clc_floor(x);")468 print(f" return __clc_convert_{dstn}{sat}(x);")469 elif mode == "_rte":470 print(f" return __clc_convert_{dstn}(x);")471 else:472 print(f" {dstn} r = __clc_convert_{dstn}(x);")473 if src in int_types:474 print(f" {srcn} y = __clc_convert_{srcn}_sat(r);")475 else:476 print(f" {srcn} y = __clc_convert_{srcn}(r);")477 if mode == "_rtz":478 if src in int_types:479 usrcn = f"{unsigned_type[src]}{size}"480 print(f" {usrcn} abs_x = __clc_abs(x);")481 print(f" {usrcn} abs_y = __clc_abs(y);")482 else:483 print(f" {srcn} abs_x = __clc_fabs(x);")484 print(f" {srcn} abs_y = __clc_fabs(y);")485 print(f" {booln} c = __clc_convert_{booln}(abs_y > abs_x);")486 if sizeof_type[src] >= sizeof_type[dst] and src in int_types:487 print(f" c = c || __clc_convert_{booln}(({srcn}){src_max} == x);")488 print(489 f" {dstn} sel = __clc_select(r, __clc_nextafter(r, __clc_sign(r) * ({dstn})-INFINITY), c);"490 )491 if dst == "half" and src in int_types and sizeof_type[src] >= 2:492 dst_max = limit_max[dst]493 # short is 16 bits signed, so the maximum value rounded to zero494 # is 0x1.ffcp+14 (0x1p+15 == 32768 > 0x7fff == 32767)495 if src == "short":496 dst_max = "0x1.ffcp+14"497 print(498 f" return __clc_clamp(sel, ({dstn}){dst_min}, ({dstn}){dst_max});"499 )500 else:501 print(" return sel;")502 if mode == "_rtp":503 print(504 f" {dstn} sel = __clc_select(r, __clc_nextafter(r, ({dstn})INFINITY), __clc_convert_{booln}(y < x));"505 )506 if dst == "half" and src in int_types and sizeof_type[src] >= 2:507 print(f" return __clc_max(sel, ({dstn}){dst_min});")508 else:509 print(" return sel;")510 if mode == "_rtn":511 print(f" {booln} c = __clc_convert_{booln}(y > x);")512 if sizeof_type[src] >= sizeof_type[dst] and src in int_types:513 print(f" c = c || __clc_convert_{booln}(({srcn}){src_max} == x);")514 print(515 f" {dstn} sel = __clc_select(r, __clc_nextafter(r, ({dstn})-INFINITY), c);"516 )517 if dst == "half" and src in int_types and sizeof_type[src] >= 2:518 dst_max = limit_max[dst]519 # short is 16 bits signed, so the maximum value rounded to520 # negative infinity is 0x1.ffcp+14 (0x1p+15 == 32768 > 0x7fff521 # == 32767)522 if src == "short":523 dst_max = "0x1.ffcp+14"524 print(f" return __clc_min(sel, ({dstn}){dst_max});")525 else:526 print(" return sel;")527 528 # Footer529 print("}")530 if close_conditional:531 print("#endif")532 533 534for src in float_types:535 for dst in int_types:536 for size in vector_sizes:537 for mode in rounding_modes:538 for sat in saturation:539 generate_float_conversion(src, dst, size, mode, sat)540 541 542for src in types:543 for dst in float_types:544 for size in vector_sizes:545 for mode in rounding_modes:546 # Do not generate user-facing "_rte" conversions for clspv as547 # they are handled natively548 if clspv and mode == "_rte":549 continue550 generate_float_conversion(src, dst, size, mode, "")551