1/* arithmetic.c -- Builtins for HSAIL arithmetic instructions for which
2   there is no feasible direct gcc GENERIC expression.
3
4   Copyright (C) 2015-2020 Free Software Foundation, Inc.
5   Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com>
6   for General Processor Tech.
7
8   Permission is hereby granted, free of charge, to any person obtaining a
9   copy of this software and associated documentation files
10   (the "Software"), to deal in the Software without restriction, including
11   without limitation the rights to use, copy, modify, merge, publish,
12   distribute, sublicense, and/or sell copies of the Software, and to
13   permit persons to whom the Software is furnished to do so, subject to
14   the following conditions:
15
16   The above copyright notice and this permission notice shall be included
17   in all copies or substantial portions of the Software.
18
19   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
23   DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25   USE OR OTHER DEALINGS IN THE SOFTWARE.
26*/
27
28#include <stdio.h>
29#include <stdint.h>
30#include <limits.h>
31#include <math.h>
32#include <float.h>
33
34/* HSAIL defines INT_MIN % -1 to be 0 while with C it's undefined,
35   and causes an overflow exception at least with gcc and C on IA-32.  */
36
37int32_t
38__hsail_rem_s32 (int32_t dividend, int32_t divisor)
39{
40  if (dividend == INT_MIN && divisor == -1)
41    return 0;
42  else
43    return dividend % divisor;
44}
45
46int64_t
47__hsail_rem_s64 (int64_t dividend, int64_t divisor)
48{
49  if (dividend == INT64_MIN && divisor == -1)
50    return 0;
51  else
52    return dividend % divisor;
53}
54
55/* HSAIL has defined behavior for min and max when one of the operands is
56   NaN: in that case the other operand is returned.  In C and with gcc's
57   MIN_EXPR/MAX_EXPR, the returned operand is undefined.  */
58
59float
60__hsail_min_f32 (float a, float b)
61{
62  if (isnan (a))
63    return b;
64  else if (isnan (b))
65    return a;
66  else if (a == 0.0f && b == 0.0f)
67    return signbit (a) ? a : b;
68  else if (a > b)
69    return b;
70  else
71    return a;
72}
73
74double
75__hsail_min_f64 (double a, double b)
76{
77  if (isnan (a))
78    return b;
79  else if (isnan (b))
80    return a;
81  else if (a > b)
82    return b;
83  else
84    return a;
85}
86
87float
88__hsail_max_f32 (float a, float b)
89{
90  if (isnan (a))
91    return b;
92  else if (isnan (b))
93    return a;
94  else if (a == 0.0f && b == 0.0f && signbit (a))
95    return b;
96  else if (a < b)
97    return b;
98  else
99    return a;
100}
101
102double
103__hsail_max_f64 (double a, double b)
104{
105  if (isnan (a))
106    return b;
107  else if (isnan (b))
108    return a;
109  else if (a == 0.0 && b == 0.0 && signbit (a))
110    return b;
111  else if (a < b)
112    return b;
113  else
114    return a;
115}
116
117uint8_t
118__hsail_cvt_zeroi_sat_u8_f32 (float a)
119{
120  if (isnan (a))
121    return 0;
122  if (a >= (float) UINT8_MAX)
123    return UINT8_MAX;
124  else if (a <= 0.0f)
125    return 0;
126  return (uint8_t) a;
127}
128
129int8_t
130__hsail_cvt_zeroi_sat_s8_f32 (float a)
131{
132  if (isnan (a))
133    return 0;
134  if (a >= (float) INT8_MAX)
135    return INT8_MAX;
136  if (a <= (float) INT8_MIN)
137    return INT8_MIN;
138  return (int8_t) a;
139}
140
141uint16_t
142__hsail_cvt_zeroi_sat_u16_f32 (float a)
143{
144  if (isnan (a))
145    return 0;
146  if (a >= (float) UINT16_MAX)
147    return UINT16_MAX;
148  else if (a <= 0.0f)
149    return 0;
150  return (uint16_t) a;
151}
152
153int16_t
154__hsail_cvt_zeroi_sat_s16_f32 (float a)
155{
156  if (isnan (a))
157    return 0;
158  if (a >= (float) INT16_MAX)
159    return INT16_MAX;
160  if (a <= (float) INT16_MIN)
161    return INT16_MIN;
162  return (int16_t) a;
163}
164
165uint32_t
166__hsail_cvt_zeroi_sat_u32_f32 (float a)
167{
168  if (isnan (a))
169    return 0;
170  if (a >= (float) UINT32_MAX)
171    return UINT32_MAX;
172  else if (a <= 0.0f)
173    return 0;
174  return (uint32_t) a;
175}
176
177int32_t
178__hsail_cvt_zeroi_sat_s32_f32 (float a)
179{
180  if (isnan (a))
181    return 0;
182  if (a >= (float) INT32_MAX)
183    return INT32_MAX;
184  if (a <= (float) INT32_MIN)
185    return INT32_MIN;
186  return (int32_t) a;
187}
188
189uint64_t
190__hsail_cvt_zeroi_sat_u64_f32 (float a)
191{
192  if (isnan (a))
193    return 0;
194  if (a >= (float) UINT64_MAX)
195    return UINT64_MAX;
196  else if (a <= 0.0f)
197    return 0;
198  return (uint64_t) a;
199}
200
201int64_t
202__hsail_cvt_zeroi_sat_s64_f32 (float a)
203{
204  if (isnan (a))
205    return 0;
206  if (a >= (float) INT64_MAX)
207    return INT64_MAX;
208  if (a <= (float) INT64_MIN)
209    return INT64_MIN;
210  return (int64_t) a;
211}
212
213uint8_t
214__hsail_cvt_zeroi_sat_u8_f64 (double a)
215{
216  if (isnan (a))
217    return 0;
218  if (a >= (double) UINT8_MAX)
219    return UINT8_MAX;
220  else if (a <= 0.0f)
221    return 0;
222  return (uint8_t) a;
223}
224
225int8_t
226__hsail_cvt_zeroi_sat_s8_f64 (double a)
227{
228  if (isnan (a))
229    return 0;
230  if (a >= (double) INT8_MAX)
231    return INT8_MAX;
232  if (a <= (double) INT8_MIN)
233    return INT8_MIN;
234  return (int8_t) a;
235}
236
237uint16_t
238__hsail_cvt_zeroi_sat_u16_f64 (double a)
239{
240  if (isnan (a))
241    return 0;
242  if (a >= (double) UINT16_MAX)
243    return UINT16_MAX;
244  else if (a <= 0.0f)
245    return 0;
246  return (uint16_t) a;
247}
248
249int16_t
250__hsail_cvt_zeroi_sat_s16_f64 (double a)
251{
252  if (isnan (a))
253    return 0;
254  if (a >= (double) INT16_MAX)
255    return INT16_MAX;
256  if (a <= (double) INT16_MIN)
257    return INT16_MIN;
258  return (int16_t) a;
259}
260
261uint32_t
262__hsail_cvt_zeroi_sat_u32_f64 (double a)
263{
264  if (isnan (a))
265    return 0;
266  if (a >= (double) UINT32_MAX)
267    return UINT32_MAX;
268  else if (a <= 0.0f)
269    return 0;
270  return (uint32_t) a;
271}
272
273int32_t
274__hsail_cvt_zeroi_sat_s32_f64 (double a)
275{
276  if (isnan (a))
277    return 0;
278  if (a >= (double) INT32_MAX)
279    return INT32_MAX;
280  if (a <= (double) INT32_MIN)
281    return INT32_MIN;
282  return (int32_t) a;
283}
284
285uint64_t
286__hsail_cvt_zeroi_sat_u64_f64 (double a)
287{
288  if (isnan (a))
289    return 0;
290  if (a >= (double) UINT64_MAX)
291    return UINT64_MAX;
292  else if (a <= 0.0f)
293    return 0;
294  return (uint64_t) a;
295}
296
297int64_t
298__hsail_cvt_zeroi_sat_s64_f64 (double a)
299{
300  if (isnan (a))
301    return 0;
302  if (a >= (double) INT64_MAX)
303    return INT64_MAX;
304  if (a <= (double) INT64_MIN)
305    return INT64_MIN;
306  return (int64_t) a;
307}
308
309
310/* Flush the operand to zero in case it's a denormalized number.
311   Do not cause any exceptions in case of NaNs.  */
312
313float
314__hsail_ftz_f32 (float a)
315{
316  if (isnan (a) || isinf (a) || a == 0.0f)
317    return a;
318
319  if (a < 0.0f)
320    {
321      if (-a < FLT_MIN)
322	return -0.0f;
323    }
324  else
325    {
326      if (a < FLT_MIN)
327	return 0.0f;
328    }
329  return a;
330}
331
332#define F16_MIN (6.10e-5)
333
334/* Flush the single precision operand to zero in case it's considered
335   a denormalized number in case it was a f16.  Do not cause any exceptions
336   in case of NaNs.  */
337
338float
339__hsail_ftz_f32_f16 (float a)
340{
341  if (isnan (a) || isinf (a) || a == 0.0f)
342    return a;
343
344  if (a < 0.0f)
345    {
346      if (-a < F16_MIN)
347	return -0.0f;
348    }
349  else
350    {
351      if (a < F16_MIN)
352	return 0.0f;
353    }
354  return a;
355}
356
357double
358__hsail_ftz_f64 (double a)
359{
360  if (isnan (a) || isinf (a) || a == 0.0d)
361    return a;
362
363  if (a < 0.0d)
364    {
365      if (-a < DBL_MIN)
366	return -0.0d;
367    }
368  else
369    {
370      if (a < DBL_MIN)
371	return 0.0d;
372    }
373  return a;
374}
375
376uint32_t
377__hsail_borrow_u32 (uint32_t a, uint32_t b)
378{
379  return __builtin_sub_overflow_p (a, b, a);
380}
381
382uint64_t
383__hsail_borrow_u64 (uint64_t a, uint64_t b)
384{
385  return __builtin_sub_overflow_p (a, b, a);
386}
387
388uint32_t
389__hsail_carry_u32 (uint32_t a, uint32_t b)
390{
391  return __builtin_add_overflow_p (a, b, a);
392}
393
394uint64_t
395__hsail_carry_u64 (uint64_t a, uint64_t b)
396{
397  return __builtin_add_overflow_p (a, b, a);
398}
399
400float
401__hsail_fract_f32 (float a)
402{
403  int exp;
404  if (isinf (a))
405    return signbit (a) == 0 ? 0.0f : -0.0f;
406  if (isnan (a) || a == 0.0f)
407    return a;
408  else
409    return fminf (a - floorf (a), 0x1.fffffep-1f);
410}
411
412double
413__hsail_fract_f64 (double a)
414{
415  int exp;
416  if (isinf (a))
417    return 0.0f * isinf (a);
418  if (isnan (a) || a == 0.0f)
419    return a;
420  else
421    return fmin (a - floor (a), 0x1.fffffffffffffp-1d);
422}
423
424uint32_t
425__hsail_class_f32 (float a, uint32_t flags)
426{
427  return (flags & 0x0001 && isnan (a) && !(*(uint32_t *) &a & (1ul << 22)))
428    || (flags & 0x0002 && isnan (a) && (*(uint32_t *) &a & (1ul << 22)))
429    || (flags & 0x0004 && isinf (a) && a < 0.0f)
430    || (flags & 0x0008 && isnormal (a) && signbit (a))
431    || (flags & 0x0010 && a < 0.0f && a > -FLT_MIN)
432    || (flags & 0x0020 && a == 0.0f && signbit (a))
433    || (flags & 0x0040 && a == 0.0f && !signbit (a))
434    || (flags & 0x0080 && a > 0.0f && a < FLT_MIN)
435    || (flags & 0x0100 && isnormal (a) && !signbit (a))
436    || (flags & 0x0200 && isinf (a) && a >= 0.0f);
437}
438
439uint32_t
440__hsail_class_f64 (double a, uint32_t flags)
441{
442  return (flags & 0x0001 && isnan (a) && !(*(uint64_t *) &a & (1ul << 51)))
443    || (flags & 0x0002 && isnan (a) && (*(uint64_t *) &a & (1ul << 51)))
444    || (flags & 0x0004 && isinf (a) && a < 0.0f)
445    || (flags & 0x0008 && isnormal (a) && signbit (a))
446    || (flags & 0x0010 && a < 0.0f && a > -FLT_MIN)
447    || (flags & 0x0020 && a == 0.0f && signbit (a))
448    || (flags & 0x0040 && a == 0.0f && !signbit (a))
449    || (flags & 0x0080 && a > 0.0f && a < FLT_MIN)
450    || (flags & 0x0100 && isnormal (a) && !signbit (a))
451    || (flags & 0x0200 && isinf (a) && a >= 0.0f);
452}
453
454
455/* 'class' for a f32-converted f16 which should otherwise be treated like f32
456 except for its limits.  */
457
458uint32_t
459__hsail_class_f32_f16 (float a, uint32_t flags)
460{
461  return (flags & 0x0001 && isnan (a) && !(*(uint32_t *) &a & 0x40000000))
462	 || (flags & 0x0002 && isnan (a) && (*(uint32_t *) &a & 0x40000000))
463	 || (flags & 0x0004 && isinf (a) && a < 0.0f)
464	 || (flags & 0x0008 && a != 0.0f && !isinf (a) && !isnan (a)
465	     && a <= -F16_MIN)
466	 || (flags & 0x0010 && a != 0.0f && !isinf (a) && !isnan (a) && a < 0.0f
467	     && a > -F16_MIN)
468	 || (flags & 0x0020 && a == 0.0f && signbit (a))
469	 || (flags & 0x0040 && a == 0.0f && !signbit (a))
470	 || (flags & 0x0080 && a != 0.0f && !isinf (a) && !isnan (a) && a > 0.0f
471	     && a < F16_MIN)
472	 || (flags & 0x0100 && a != 0.0f && !isinf (a) && !isnan (a)
473	     && a >= F16_MIN)
474	 || (flags & 0x0200 && isinf (a) && a >= 0.0f);
475}
476