[libclc] Avoid out-of-range float-to-int. (#145698)
For a kernel such as
kernel void foo(__global double3 *z) {
double3 x = {0.6631661088,0.6612268107,0.1513627528};
int3 y = {-1980459213,-660855407,615708204};
*z = pown(x, y);
}
we were not storing anything to z, because the implementation of pown
relied on an floating-point-to-integer conversion where the
floating-point value was outside of the integer's range. Although in
LLVM IR we permit that operation so long as we end up ignoring its
result -- that is the general rule for poison -- one thing we are not
permitted to do is have conditional branches that depend on it, and
through the call to __clc_ldexp, we did have that.
To fix this, rather than changing expv at the end to INFINITY/0, we can
change v at the start to values that we know will produce INFINITY/0
without performing such out-of-range conversions.
Tested with
clang --target=nvptx64 -S -O3 -o - test.cl \
-Xclang -mlink-builtin-bitcode \
-Xclang runtimes/runtimes-bins/libclc/nvptx64--.bc
A grep showed that this exact same code existed in three more places, so
I changed it there too, though I did not do a broader search for other
similar code that potentially has the same problem.
This commit is contained in:
@@ -330,6 +330,15 @@ _CLC_DEF _CLC_OVERLOAD __CLC_GENTYPE __clc_pow(__CLC_GENTYPE x,
|
||||
const __CLC_GENTYPE lnof2_by_64_head = 0.010830424260348081;
|
||||
const __CLC_GENTYPE lnof2_by_64_tail = -4.359010638708991e-10;
|
||||
|
||||
// If v is so large that we need to return INFINITY, or so small that we
|
||||
// need to return 0, set v to known values that will produce that result. Do
|
||||
// not try to continue the computation with the original v and patch it up
|
||||
// afterwards because v may be so large that temp is out of range of int, in
|
||||
// which case that conversion, and a value based on that conversion being
|
||||
// passed to __clc_ldexp, results in undefined behavior.
|
||||
v = v > max_exp_arg ? 1000.0 : v;
|
||||
v = v < min_exp_arg ? -1000.0 : v;
|
||||
|
||||
__CLC_GENTYPE temp = v * sixtyfour_by_lnof2;
|
||||
__CLC_INTN n = __CLC_CONVERT_INTN(temp);
|
||||
__CLC_GENTYPE dn = __CLC_CONVERT_GENTYPE(n);
|
||||
@@ -357,10 +366,6 @@ _CLC_DEF _CLC_OVERLOAD __CLC_GENTYPE __clc_pow(__CLC_GENTYPE x,
|
||||
|
||||
expv = __clc_fma(f, q, f2) + f1;
|
||||
expv = __clc_ldexp(expv, m);
|
||||
|
||||
expv = v > max_exp_arg ? __CLC_AS_GENTYPE((__CLC_ULONGN)0x7FF0000000000000L)
|
||||
: expv;
|
||||
expv = v < min_exp_arg ? 0.0 : expv;
|
||||
}
|
||||
|
||||
// See whether y is an integer.
|
||||
|
||||
@@ -317,6 +317,15 @@ _CLC_DEF _CLC_OVERLOAD __CLC_GENTYPE __clc_pown(__CLC_GENTYPE x,
|
||||
const __CLC_GENTYPE lnof2_by_64_head = 0.010830424260348081;
|
||||
const __CLC_GENTYPE lnof2_by_64_tail = -4.359010638708991e-10;
|
||||
|
||||
// If v is so large that we need to return INFINITY, or so small that we
|
||||
// need to return 0, set v to known values that will produce that result. Do
|
||||
// not try to continue the computation with the original v and patch it up
|
||||
// afterwards because v may be so large that temp is out of range of int, in
|
||||
// which case that conversion, and a value based on that conversion being
|
||||
// passed to __clc_ldexp, results in undefined behavior.
|
||||
v = v > max_exp_arg ? 1000.0 : v;
|
||||
v = v < min_exp_arg ? -1000.0 : v;
|
||||
|
||||
__CLC_GENTYPE temp = v * sixtyfour_by_lnof2;
|
||||
__CLC_INTN n = __CLC_CONVERT_INTN(temp);
|
||||
__CLC_GENTYPE dn = __CLC_CONVERT_GENTYPE(n);
|
||||
@@ -344,10 +353,6 @@ _CLC_DEF _CLC_OVERLOAD __CLC_GENTYPE __clc_pown(__CLC_GENTYPE x,
|
||||
|
||||
expv = __clc_fma(f, q, f2) + f1;
|
||||
expv = __clc_ldexp(expv, m);
|
||||
|
||||
expv = v > max_exp_arg ? __CLC_AS_GENTYPE((__CLC_ULONGN)0x7FF0000000000000L)
|
||||
: expv;
|
||||
expv = v < min_exp_arg ? 0.0 : expv;
|
||||
}
|
||||
|
||||
// See whether y is an integer.
|
||||
|
||||
@@ -316,6 +316,15 @@ _CLC_DEF _CLC_OVERLOAD __CLC_GENTYPE __clc_powr(__CLC_GENTYPE x,
|
||||
const __CLC_GENTYPE lnof2_by_64_head = 0.010830424260348081;
|
||||
const __CLC_GENTYPE lnof2_by_64_tail = -4.359010638708991e-10;
|
||||
|
||||
// If v is so large that we need to return INFINITY, or so small that we
|
||||
// need to return 0, set v to known values that will produce that result. Do
|
||||
// not try to continue the computation with the original v and patch it up
|
||||
// afterwards because v may be so large that temp is out of range of int, in
|
||||
// which case that conversion, and a value based on that conversion being
|
||||
// passed to __clc_ldexp, results in undefined behavior.
|
||||
v = v > max_exp_arg ? 1000.0 : v;
|
||||
v = v < min_exp_arg ? -1000.0 : v;
|
||||
|
||||
__CLC_GENTYPE temp = v * sixtyfour_by_lnof2;
|
||||
__CLC_INTN n = __CLC_CONVERT_INTN(temp);
|
||||
__CLC_GENTYPE dn = __CLC_CONVERT_GENTYPE(n);
|
||||
@@ -343,10 +352,6 @@ _CLC_DEF _CLC_OVERLOAD __CLC_GENTYPE __clc_powr(__CLC_GENTYPE x,
|
||||
|
||||
expv = __clc_fma(f, q, f2) + f1;
|
||||
expv = __clc_ldexp(expv, m);
|
||||
|
||||
expv = v > max_exp_arg ? __CLC_AS_GENTYPE((__CLC_ULONGN)0x7FF0000000000000L)
|
||||
: expv;
|
||||
expv = v < min_exp_arg ? 0.0 : expv;
|
||||
}
|
||||
|
||||
// See whether y is an integer.
|
||||
|
||||
@@ -323,6 +323,15 @@ _CLC_DEF _CLC_OVERLOAD __CLC_GENTYPE __clc_rootn(__CLC_GENTYPE x,
|
||||
const __CLC_GENTYPE lnof2_by_64_head = 0.010830424260348081;
|
||||
const __CLC_GENTYPE lnof2_by_64_tail = -4.359010638708991e-10;
|
||||
|
||||
// If v is so large that we need to return INFINITY, or so small that we
|
||||
// need to return 0, set v to known values that will produce that result. Do
|
||||
// not try to continue the computation with the original v and patch it up
|
||||
// afterwards because v may be so large that temp is out of range of int, in
|
||||
// which case that conversion, and a value based on that conversion being
|
||||
// passed to __clc_ldexp, results in undefined behavior.
|
||||
v = v > max_exp_arg ? 1000.0 : v;
|
||||
v = v < min_exp_arg ? -1000.0 : v;
|
||||
|
||||
__CLC_GENTYPE temp = v * sixtyfour_by_lnof2;
|
||||
__CLC_INTN n = __CLC_CONVERT_INTN(temp);
|
||||
__CLC_GENTYPE dn = __CLC_CONVERT_GENTYPE(n);
|
||||
@@ -350,10 +359,6 @@ _CLC_DEF _CLC_OVERLOAD __CLC_GENTYPE __clc_rootn(__CLC_GENTYPE x,
|
||||
|
||||
expv = __clc_fma(f, q, f2) + f1;
|
||||
expv = __clc_ldexp(expv, m);
|
||||
|
||||
expv = v > max_exp_arg ? __CLC_AS_GENTYPE((__CLC_ULONGN)0x7FF0000000000000L)
|
||||
: expv;
|
||||
expv = v < min_exp_arg ? 0.0 : expv;
|
||||
}
|
||||
|
||||
// See whether y is an integer.
|
||||
|
||||
Reference in New Issue
Block a user