Skip to content

Commit e438bc8

Browse files
authored
Added missing math APIs for devicelib. (#2558)
The new APIs are mostly for Windows, but I added double-precision scalbn for both Linux and Windows. The Microsoft APIs are defined here: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019 There are still some Microsoft APIs missing. Signed-off-by: Vyacheslav Zakharin [email protected]
1 parent ca33f7f commit e438bc8

File tree

10 files changed

+365
-31
lines changed

10 files changed

+365
-31
lines changed

libdevice/cmath_wrapper.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,47 @@ short _FDtest(float *px) { // categorize *px
181181
return ret;
182182
}
183183

184+
// Returns _FP_LT, _FP_GT or _FP_EQ based on the ordering
185+
// relationship between x and y. '0' means unordered.
186+
DEVICE_EXTERN_C
187+
int _fdpcomp(float x, float y) {
188+
int res = 0;
189+
if (_FDtest(&x) == _NANCODE || _FDtest(&y) == _NANCODE) {
190+
// '0' means unordered.
191+
return res;
192+
}
193+
194+
if (x < y)
195+
res |= _FP_LT;
196+
else if (x > y)
197+
res |= _FP_GT;
198+
else
199+
res |= _FP_EQ;
200+
201+
return res;
202+
}
203+
204+
// Returns 0, if the sign bit is not set, and non-zero otherwise.
205+
DEVICE_EXTERN_C
206+
int _fdsign(float x) { return FSIGN(x); }
207+
208+
// fpclassify() equivalent with a pointer argument.
209+
DEVICE_EXTERN_C
210+
short _fdtest(float *px) {
211+
switch (_FDtest(px)) {
212+
case _DENORM:
213+
return FP_SUBNORMAL;
214+
case _FINITE:
215+
return FP_NORMAL;
216+
case _INFCODE:
217+
return FP_INFINITE;
218+
case _NANCODE:
219+
return FP_NAN;
220+
}
221+
222+
return FP_ZERO;
223+
}
224+
184225
DEVICE_EXTERN_C
185226
short _FDnorm(_Fval *ps) { // normalize float fraction
186227
short xchar;

libdevice/cmath_wrapper_fp64.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ double asinh(double x) { return __devicelib_asinh(x); }
133133
DEVICE_EXTERN_C
134134
double atanh(double x) { return __devicelib_atanh(x); }
135135

136+
DEVICE_EXTERN_C
137+
double scalbn(double x, int exp) { return __devicelib_scalbn(x, exp); }
138+
136139
#if defined(_WIN32)
137140
#include <math.h>
138141
// FLOAT PROPERTIES
@@ -180,6 +183,47 @@ short _Dtest(double *px) { // categorize *px
180183
return ret;
181184
}
182185

186+
// Returns _FP_LT, _FP_GT or _FP_EQ based on the ordering
187+
// relationship between x and y.
188+
DEVICE_EXTERN_C
189+
int _dpcomp(double x, double y) {
190+
int res = 0;
191+
if (_Dtest(&x) == _NANCODE || _Dtest(&y) == _NANCODE) {
192+
// '0' means unordered.
193+
return res;
194+
}
195+
196+
if (x < y)
197+
res |= _FP_LT;
198+
else if (x > y)
199+
res |= _FP_GT;
200+
else
201+
res |= _FP_EQ;
202+
203+
return res;
204+
}
205+
206+
// Returns 0, if the sign bit is not set, and non-zero otherwise.
207+
DEVICE_EXTERN_C
208+
int _dsign(double x) { return DSIGN(x); }
209+
210+
// fpclassify() equivalent with a pointer argument.
211+
DEVICE_EXTERN_C
212+
short _dtest(double *px) {
213+
switch (_Dtest(px)) {
214+
case _DENORM:
215+
return FP_SUBNORMAL;
216+
case _FINITE:
217+
return FP_NORMAL;
218+
case _INFCODE:
219+
return FP_INFINITE;
220+
case _NANCODE:
221+
return FP_NAN;
222+
}
223+
224+
return FP_ZERO;
225+
}
226+
183227
DEVICE_EXTERN_C
184228
short _Dnorm(_Dval *ps) { // normalize double fraction
185229
short xchar;

libdevice/device_math.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,5 +249,8 @@ float __devicelib_logbf(float x);
249249

250250
DEVICE_EXTERN_C
251251
float __devicelib_scalbnf(float x, int n);
252+
253+
DEVICE_EXTERN_C
254+
double __devicelib_scalbn(double x, int exp);
252255
#endif // __SPIR__
253256
#endif // __LIBDEVICE_DEVICE_MATH_H__

libdevice/fallback-cmath-fp64.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,9 @@ double __devicelib_asinh(double x) { return __spirv_ocl_asinh(x); }
143143

144144
DEVICE_EXTERN_C
145145
double __devicelib_atanh(double x) { return __spirv_ocl_atanh(x); }
146+
147+
DEVICE_EXTERN_C
148+
double __devicelib_scalbn(double x, int exp) {
149+
return __spirv_ocl_ldexp(x, exp);
150+
}
146151
#endif // __SPIR__

llvm/tools/sycl-post-link/sycl-post-link.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ static std::unordered_map<std::string, DeviceLibExt> DeviceLibFuncMap = {
167167
{"__devicelib_powf", DeviceLibExt::cl_intel_devicelib_math},
168168
{"__devicelib_remainderf", DeviceLibExt::cl_intel_devicelib_math},
169169
{"__devicelib_remquof", DeviceLibExt::cl_intel_devicelib_math},
170+
{"__devicelib_scalbnf", DeviceLibExt::cl_intel_devicelib_math},
170171
{"__devicelib_sinf", DeviceLibExt::cl_intel_devicelib_math},
171172
{"__devicelib_sinhf", DeviceLibExt::cl_intel_devicelib_math},
172173
{"__devicelib_sqrtf", DeviceLibExt::cl_intel_devicelib_math},
@@ -206,6 +207,7 @@ static std::unordered_map<std::string, DeviceLibExt> DeviceLibFuncMap = {
206207
{"__devicelib_pow", DeviceLibExt::cl_intel_devicelib_math_fp64},
207208
{"__devicelib_remainder", DeviceLibExt::cl_intel_devicelib_math_fp64},
208209
{"__devicelib_remquo", DeviceLibExt::cl_intel_devicelib_math_fp64},
210+
{"__devicelib_scalbn", DeviceLibExt::cl_intel_devicelib_math_fp64},
209211
{"__devicelib_sin", DeviceLibExt::cl_intel_devicelib_math_fp64},
210212
{"__devicelib_sinh", DeviceLibExt::cl_intel_devicelib_math_fp64},
211213
{"__devicelib_sqrt", DeviceLibExt::cl_intel_devicelib_math_fp64},

sycl/include/CL/sycl/builtins.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,11 +1551,23 @@ extern SYCL_EXTERNAL void __assert_fail(const char *expr, const char *file,
15511551
}
15521552
#elif defined(_WIN32)
15531553
extern "C" {
1554+
// TODO: documented C runtime library APIs must be recognized as
1555+
// builtins by FE. This includes _dpcomp, _dsign, _dtest,
1556+
// _fdpcomp, _fdsign, _fdtest, _hypotf, _wassert.
1557+
// APIs used by STL, such as _Cosh, are undocumented, even though
1558+
// they are open-sourced. Recognizing them as builtins is not
1559+
// straightforward currently.
15541560
extern SYCL_EXTERNAL double _Cosh(double x, double y);
1561+
extern SYCL_EXTERNAL int _dpcomp(double x, double y);
1562+
extern SYCL_EXTERNAL int _dsign(double x);
15551563
extern SYCL_EXTERNAL short _Dtest(double *px);
1564+
extern SYCL_EXTERNAL short _dtest(double *px);
15561565
extern SYCL_EXTERNAL short _Exp(double *px, double y, short eoff);
15571566
extern SYCL_EXTERNAL float _FCosh(float x, float y);
1567+
extern SYCL_EXTERNAL int _fdpcomp(float x, float y);
1568+
extern SYCL_EXTERNAL int _fdsign(float x);
15581569
extern SYCL_EXTERNAL short _FDtest(float *px);
1570+
extern SYCL_EXTERNAL short _fdtest(float *px);
15591571
extern SYCL_EXTERNAL short _FExp(float *px, float y, short eoff);
15601572
extern SYCL_EXTERNAL float _FSinh(float x, float y);
15611573
extern SYCL_EXTERNAL double _Sinh(double x, double y);

sycl/test/devicelib/cmath_fp64_test.cpp

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,22 @@
33
// RUN: %CPU_RUN_PLACEHOLDER %t.out
44
// RUN: %ACC_RUN_PLACEHOLDER %t.out
55

6+
#include "math_utils.hpp"
67
#include <CL/sycl.hpp>
78
#include <cmath>
9+
#include <cstdint>
810
#include <iostream>
9-
#include "math_utils.hpp"
1011

1112
namespace s = cl::sycl;
1213
constexpr s::access::mode sycl_read = s::access::mode::read;
1314
constexpr s::access::mode sycl_write = s::access::mode::write;
1415

15-
#define TEST_NUM 38
16+
#define TEST_NUM 63
1617

1718
double ref[TEST_NUM] = {
18-
1, 0, 0, 0, 0, 0, 0, 1, 1, 0.5,
19-
0, 2, 0, 0, 1, 0, 2, 0, 0, 0,
20-
0, 0, 1, 0, 1, 2, 0, 1, 2, 5,
21-
0, 0, 0, 0, 0.5, 0.5, NAN, NAN,};
19+
1, 0, 0, 0, 0, 0, 0, 1, 1, 0.5, 0, 2, 0, 0, 1, 0, 2, 0, 0, 0, 0,
20+
0, 1, 0, 1, 2, 0, 1, 2, 5, 0, 0, 0, 0, 0.5, 0.5, NAN, NAN, 2, 0, 0, 0,
21+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2222

2323
double refIptr = 1;
2424

@@ -47,6 +47,12 @@ void device_cmath_test(s::queue &deviceQueue) {
4747
auto quo_access = buffer4.template get_access<sycl_write>(cgh);
4848
cgh.single_task<class DeviceMathTest>([=]() {
4949
int i = 0;
50+
T nan = NAN;
51+
T minus_nan = -NAN;
52+
T infinity = INFINITY;
53+
T minus_infinity = -INFINITY;
54+
double subnormal;
55+
*((uint64_t *)&subnormal) = 0xFFFFFFFFFFFFFULL;
5056
res_access[i++] = std::cos(0.0);
5157
res_access[i++] = std::sin(0.0);
5258
res_access[i++] = std::log(1.0);
@@ -83,9 +89,58 @@ void device_cmath_test(s::queue &deviceQueue) {
8389
res_access[i++] = std::logb(1.0);
8490
res_access[i++] = std::remainder(0.5, 1.0);
8591
res_access[i++] = std::remquo(0.5, 1.0, &quo_access[0]);
86-
T a = NAN;
87-
res_access[i++] = std::tgamma(a);
88-
res_access[i++] = std::lgamma(a);
92+
res_access[i++] = std::tgamma(nan);
93+
res_access[i++] = std::lgamma(nan);
94+
res_access[i++] = std::scalbn(1.0, 1);
95+
96+
res_access[i++] = !(std::signbit(infinity) == 0);
97+
res_access[i++] = !(std::signbit(minus_infinity) != 0);
98+
res_access[i++] = !(std::signbit(nan) == 0);
99+
res_access[i++] = !(std::signbit(minus_nan) != 0);
100+
101+
res_access[i++] = !(std::isunordered(minus_nan, nan) != 0);
102+
res_access[i++] = !(std::isunordered(minus_infinity, infinity) == 0);
103+
res_access[i++] = !(std::isgreater(minus_infinity, infinity) == 0);
104+
res_access[i++] = !(std::isgreater(0.0f, minus_nan) == 0);
105+
#ifdef _WIN32
106+
res_access[i++] = !(std::isfinite(0.0f) != 0);
107+
res_access[i++] = !(std::isfinite(nan) == 0);
108+
res_access[i++] = !(std::isfinite(infinity) == 0);
109+
res_access[i++] = !(std::isfinite(minus_infinity) == 0);
110+
111+
res_access[i++] = !(std::isinf(0.0f) == 0);
112+
res_access[i++] = !(std::isinf(nan) == 0);
113+
res_access[i++] = !(std::isinf(infinity) != 0);
114+
res_access[i++] = !(std::isinf(minus_infinity) != 0);
115+
#else // !_WIN32
116+
// __builtin_isfinite is unsupported.
117+
res_access[i++] = 0;
118+
res_access[i++] = 0;
119+
res_access[i++] = 0;
120+
res_access[i++] = 0;
121+
122+
// __builtin_isinf is unsupported.
123+
res_access[i++] = 0;
124+
res_access[i++] = 0;
125+
res_access[i++] = 0;
126+
res_access[i++] = 0;
127+
#endif // !_WIN32
128+
res_access[i++] = !(std::isnan(0.0f) == 0);
129+
res_access[i++] = !(std::isnan(nan) != 0);
130+
res_access[i++] = !(std::isnan(infinity) == 0);
131+
res_access[i++] = !(std::isnan(minus_infinity) == 0);
132+
#ifdef _WIN32
133+
res_access[i++] = !(std::isnormal(nan) == 0);
134+
res_access[i++] = !(std::isnormal(minus_infinity) == 0);
135+
res_access[i++] = !(std::isnormal(subnormal) == 0);
136+
res_access[i++] = !(std::isnormal(1.0f) != 0);
137+
#else // !_WIN32
138+
// __builtin_isnormal() is unsupported.
139+
res_access[i++] = 0;
140+
res_access[i++] = 0;
141+
res_access[i++] = 0;
142+
res_access[i++] = 0;
143+
#endif // !_WIN32
89144
});
90145
});
91146
}

sycl/test/devicelib/cmath_test.cpp

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@
66
#include "math_utils.hpp"
77
#include <CL/sycl.hpp>
88
#include <cmath>
9+
#include <cstdint>
910
#include <iostream>
1011

1112
namespace s = cl::sycl;
1213
constexpr s::access::mode sycl_read = s::access::mode::read;
1314
constexpr s::access::mode sycl_write = s::access::mode::write;
1415

15-
#define TEST_NUM 36
16+
#define TEST_NUM 61
1617

17-
float ref[TEST_NUM] = {1, 0, 0, 0, 0, 0, 0, 1, 1, 0.5, 0, 0,
18-
1, 0, 2, 0, 0, 0, 0, 0, 1, 0, 1, 2,
19-
0, 1, 2, 5, 0, 0, 0, 0, 0.5, 0.5, NAN, NAN};
18+
float ref[TEST_NUM] = {1, 0, 0, 0, 0, 0, 0, 1, 1, 0.5, 0, 0, 1, 0, 2, 0,
19+
0, 0, 0, 0, 1, 0, 1, 2, 0, 1, 2, 5, 0, 0, 0, 0,
20+
0.5, 0.5, NAN, NAN, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
21+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2022

2123
float refIptr = 1;
2224

@@ -39,6 +41,13 @@ template <class T> void device_cmath_test_1(s::queue &deviceQueue) {
3941
auto quo_access = buffer3.template get_access<sycl_write>(cgh);
4042
cgh.single_task<class DeviceMathTest1>([=]() {
4143
int i = 0;
44+
T nan = NAN;
45+
T minus_nan = -NAN;
46+
T infinity = INFINITY;
47+
T minus_infinity = -INFINITY;
48+
float subnormal;
49+
*((uint32_t *)&subnormal) = 0x7FFFFF;
50+
4251
res_access[i++] = std::cos(0.0f);
4352
res_access[i++] = std::sin(0.0f);
4453
res_access[i++] = std::log(1.0f);
@@ -73,9 +82,58 @@ template <class T> void device_cmath_test_1(s::queue &deviceQueue) {
7382
res_access[i++] = std::logb(1.0f);
7483
res_access[i++] = std::remainder(0.5f, 1.0f);
7584
res_access[i++] = std::remquo(0.5f, 1.0f, &quo_access[0]);
76-
T a = NAN;
77-
res_access[i++] = std::tgamma(a);
78-
res_access[i++] = std::lgamma(a);
85+
res_access[i++] = std::tgamma(nan);
86+
res_access[i++] = std::lgamma(nan);
87+
res_access[i++] = std::scalbn(1.0f, 1);
88+
89+
res_access[i++] = !(std::signbit(infinity) == 0);
90+
res_access[i++] = !(std::signbit(minus_infinity) != 0);
91+
res_access[i++] = !(std::signbit(nan) == 0);
92+
res_access[i++] = !(std::signbit(minus_nan) != 0);
93+
94+
res_access[i++] = !(std::isunordered(minus_nan, nan) != 0);
95+
res_access[i++] = !(std::isunordered(minus_infinity, infinity) == 0);
96+
res_access[i++] = !(std::isgreater(minus_infinity, infinity) == 0);
97+
res_access[i++] = !(std::isgreater(0.0f, minus_nan) == 0);
98+
#ifdef _WIN32
99+
res_access[i++] = !(std::isfinite(0.0f) != 0);
100+
res_access[i++] = !(std::isfinite(nan) == 0);
101+
res_access[i++] = !(std::isfinite(infinity) == 0);
102+
res_access[i++] = !(std::isfinite(minus_infinity) == 0);
103+
104+
res_access[i++] = !(std::isinf(0.0f) == 0);
105+
res_access[i++] = !(std::isinf(nan) == 0);
106+
res_access[i++] = !(std::isinf(infinity) != 0);
107+
res_access[i++] = !(std::isinf(minus_infinity) != 0);
108+
#else // !_WIN32
109+
// __builtin_isfinite is unsupported.
110+
res_access[i++] = 0;
111+
res_access[i++] = 0;
112+
res_access[i++] = 0;
113+
res_access[i++] = 0;
114+
115+
// __builtin_isinf is unsupported.
116+
res_access[i++] = 0;
117+
res_access[i++] = 0;
118+
res_access[i++] = 0;
119+
res_access[i++] = 0;
120+
#endif // !_WIN32
121+
res_access[i++] = !(std::isnan(0.0f) == 0);
122+
res_access[i++] = !(std::isnan(nan) != 0);
123+
res_access[i++] = !(std::isnan(infinity) == 0);
124+
res_access[i++] = !(std::isnan(minus_infinity) == 0);
125+
#ifdef _WIN32
126+
res_access[i++] = !(std::isnormal(nan) == 0);
127+
res_access[i++] = !(std::isnormal(minus_infinity) == 0);
128+
res_access[i++] = !(std::isnormal(subnormal) == 0);
129+
res_access[i++] = !(std::isnormal(1.0f) != 0);
130+
#else // !_WIN32
131+
// __builtin_isnormal() is unsupported.
132+
res_access[i++] = 0;
133+
res_access[i++] = 0;
134+
res_access[i++] = 0;
135+
res_access[i++] = 0;
136+
#endif // !_WIN32
79137
});
80138
});
81139
}

0 commit comments

Comments
 (0)