llvm / llvm/llvm-project

Possible wrong code generation when building glibc ldbl-128 powl

Open
#173,080 0 comments 0 reactions 0 assignees View on GitHub
diverges-from:gcc floating-point miscompilation needs-reduction
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

After enabling experimental clang build support on glibc, the _Float128 powl support shows some regressions:

$ math/test-float128-pow
Failure: pow (-0x1.000002p+0, 0xf.ffffffffffff8p+1020): Exception "Underflow" set
Failure: pow (-0x1.000002p+0, 0xf.ffffffffffffbffffffffffffcp+1020): Exception "Underflow" set
Failure: pow (-0x1.000002p+0, 0xf.fffffffffffffffffffffffffff8p+16380): Exception "Underflow" set
Failure: pow (-0x1.000002p+0, 0xf.fffffffffffffffp+16380): Exception "Underflow" set
Failure: pow (-0x1.000002p+0, 0xf.fffffp+124): Exception "Underflow" set
Failure: pow (-0x1.00000ep+0, 0xf.ffffffffffff8p+1020): Exception "Underflow" set
Failure: pow (-0x1.00000ep+0, 0xf.ffffffffffffbffffffffffffcp+1020): Exception "Underflow" set
Failure: pow (-0x1.00000ep+0, 0xf.fffffffffffffffffffffffffff8p+16380): Exception "Underflow" set
Failure: pow (-0x1.00000ep+0, 0xf.fffffffffffffffp+16380): Exception "Underflow" set
Failure: pow (-0x1.00000ep+0, 0xf.fffffp+124): Exception "Underflow" set
[...]
Test suite completed:
Maximum error found of `2' ulp
4 max error test cases,
11912 input tests,
- with 47792 tests for exception flags,
- with 9350 tests for errno executed.
680 errors occurred.

The issues seems that clang is moving out the the multiplication used to raise overflow/underflow (huge*huge and tiny*tiny) before the check 'iy' check [1]:

```
/* |y| is huge.
2^-16495 = 1/2 of smallest representable value.
If (1 - 1/131072)^y underflows, y > 1.4986e9 */
if (iy > 0x401d654b)
{
/* if (1 - 2^-113)^y underflows, y > 1.1873e38 */
if (iy > 0x407d654b)
{
if (ix <= 0x3ffeffff)
return (hy < 0) ? huge * huge : tiny * tiny;
if (ix >= 0x3fff0000)
return (hy > 0) ? huge * huge : tiny * tiny;
}
/* over/underflow if x is not close to one */
if (ix < 0x3ffeffff)
return (hy < 0) ? sgn * huge * huge : sgn * tiny * tiny;
if (ix > 0x3fff0000)
return (hy > 0) ? sgn * huge * huge : sgn * tiny * tiny;
}
```
If I force the multiplication to be optimized away with the math_opt_barrier barrier, it works as expected.

To reproduce it build the commit be48ae12b10b0556895e356c3ae34b8ca229e61b or higher with clang-18 or higher. Currently only x86_64 and aarch64 is supported, but both exports the implementation (either by powl on aarch64 or powf128 on x86_64). Keep in mind that glibc explicit builds libm and all tests with -ftrapping-math.

I will try to come-up with a more contrieved example.

[1] https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/ieee754/ldbl-128/e_powl.c;h=4e20616705633885c661f4bda3175aac7adc0375;hb=HEAD#l276

Contributor guide

Open the contributing guide

Research direction

Reproduce with clang-18 or later using glibc commit be48ae12b10b0556895e356c3ae34b8ca229e61b or higher, then run math/test-float128-pow. Read sysdeps/ieee754/ldbl-128/e_powl.c around the linked overflow and underflow paths and compare generated behavior with and without math_opt_barrier. Done means the reported exception-flag regressions are explained and the test suite passes.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
compilers, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.