LLVM Bugzilla is read-only and represents the historical archive of all LLVM issues filled before November 26, 2021. Use github to submit LLVM bugs

Bug 44873 - Wrong optimization: instability of x87 floating-point results leads to nonsense
Summary: Wrong optimization: instability of x87 floating-point results leads to nonsense
Status: NEW
Alias: None
Product: libraries
Classification: Unclassified
Component: Backend: X86 (show other bugs)
Version: trunk
Hardware: PC Linux
: P enhancement
Assignee: Unassigned LLVM Bugs
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-02-11 08:33 PST by Alexander Cherepanov
Modified: 2020-02-26 03:42 PST (History)
7 users (show)

See Also:
Fixed By Commit(s):


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Alexander Cherepanov 2020-02-11 08:33:45 PST
x87 floating-point results are effectively unstable due to possible excess precision. Without extra care, this instability can taint everything around and lead to nonsense.

Instability is not limited to FP numbers, it extends to integers too:

----------------------------------------------------------------------
#include <stdio.h>

__attribute__((noipa,optnone)) // imagine it in a separate TU
static int opaque(int i) { return i; }

int main()
{
    int z = opaque(1) + 0x1p-60 == 1;

    printf("z = %d\n", z);
    if (z) 
        puts("z is one");
}
----------------------------------------------------------------------
$ clang -std=c11 -pedantic -Wall -Wextra -Wno-unknown-attributes -m32 -march=i686 -O3 test.c && ./a.out
z = 0
z is one
----------------------------------------------------------------------
clang x86-64 version: clang version 11.0.0 (https://github.com/llvm/llvm-project 14ecbd7b8ded18af6c95f6a9957da541d1ec0e80)
----------------------------------------------------------------------
Comment 1 Alexander Cherepanov 2020-02-11 08:35:39 PST
And instability of integers then easily taints surrounding code:

----------------------------------------------------------------------
#include <stdio.h>

__attribute__((optnone)) // imagine it in a separate TU
static int opaque(int i) { return i; }

static void f(int a)
{
    int z = opaque(0);

    printf("z = %d\n", z);
    if (z == a && a == 1)
        printf("z = %d\n", z);
}

int main()
{
    f(opaque(1) + 0x1p-60 == 1);
}
----------------------------------------------------------------------
$ clang -std=c11 -pedantic -Wall -Wextra -m32 -march=i686 -O3 test.c && ./a.out
z = 0
z = 1
----------------------------------------------------------------------
clang x86-64 version: clang version 11.0.0 (https://github.com/llvm/llvm-project 14ecbd7b8ded18af6c95f6a9957da541d1ec0e80)
----------------------------------------------------------------------

gcc bug -- https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93681
Comment 2 Roman Lebedev 2020-02-11 08:40:11 PST
(In reply to Alexander Cherepanov from comment #0)
> x87 floating-point results are effectively unstable due to possible excess
> precision. Without extra care, this instability can taint everything around
> and lead to nonsense.
> 
> Instability is not limited to FP numbers, it extends to integers too:
> 
> ----------------------------------------------------------------------
> #include <stdio.h>
> 
> __attribute__((noipa,optnone)) // imagine it in a separate TU
> static int opaque(int i) { return i; }
> 
> int main()
> {
>     int z = opaque(1) + 0x1p-60 == 1;
`0x1p-60` there is just a `double`
https://godbolt.org/z/mJS3RN
It's as expected if it is `long double`: https://godbolt.org/z/NVvJht

>     printf("z = %d\n", z);
>     if (z) 
>         puts("z is one");
> }
> ----------------------------------------------------------------------
> $ clang -std=c11 -pedantic -Wall -Wextra -Wno-unknown-attributes -m32
> -march=i686 -O3 test.c && ./a.out
> z = 0
> z is one
> ----------------------------------------------------------------------
> clang x86-64 version: clang version 11.0.0
> (https://github.com/llvm/llvm-project
> 14ecbd7b8ded18af6c95f6a9957da541d1ec0e80)
> ----------------------------------------------------------------------
Comment 3 Alexander Cherepanov 2020-02-11 14:37:36 PST
(In reply to Roman Lebedev from comment #2)
> >     int z = opaque(1) + 0x1p-60 == 1;
> `0x1p-60` there is just a `double`
> https://godbolt.org/z/mJS3RN
> It's as expected if it is `long double`: https://godbolt.org/z/NVvJht

Yeah, 1 + 0x1p-60 is the simplest expression I could think of where both operands fit into `double` exactly but the result doesn't.
x87 evaluates it as `long double` and gets the exact result. But if the result is spilled into memory (as `double`) it's rounded to just 1.

If you force the expression to honest `long double` (e.g., by casting any of the operands to it) then everything becomes stable -- the result both in a register and in memory has the same type and the same value. The same happens with SSE -- the result is stored as `double` everywhere and there are no discrepancies.