[llvm-dev] Problem about 128bit floating-point operations in x86 machines

Dan Liew via llvm-dev llvm-dev at lists.llvm.org
Mon Dec 12 16:05:26 PST 2016


Hi Kyoungju,

On 12 December 2016 at 13:46, Kyoungju Sim via llvm-dev
<llvm-dev at lists.llvm.org> wrote:
> Hello,
>
> I'm making a compiler utilizing LLVM.
> Because I want the compiler to support 128bit floating-point operations, I
> added the code for 128bit floating-point operations and tested these
> operations in i686, x86_64, SPARCv8 and SPARCv9 machines.
> Generated codes by LLVM operated normally when using x86_64, SPARCv8 and
> SPARCv9 machines, but generated codes in a x86 machine produce wrong result.
>
> Because Clang supports __float128 type, I also tried to test using Clang 3.9
> version.
> However, I could not get the correct result of 128bit floating-point
> operations using Clang.
>
> For example, C source code that included operations of 128bit floating-point
> is as follows:
>
> #include <stdio.h>
> #include <quadmath.h>
>
> int main() {
>     __float128 a = 3.14;
>     char buf[128];
>
>     quadmath_snprintf(buf, sizeof(buf), "%QE", -a);
>     printf("%s\n", buf);
>     quadmath_snprintf(buf, sizeof(buf), "%QE", a * 5.0);
>     printf("%s\n", buf);
>     quadmath_snprintf(buf, sizeof(buf), "%QE", a + 5.0);
>     printf("%s\n", buf);
>
>     return 0;
> }
>
>
> After compilation, the correct result of the compiled program must be
>
> -3.140000E+00
> 1.570000E+01
> 8.140000E+00.
>
>
> However, I could not get the right result from the program that compiled by
> Clang 3.9.
> (the result of the program that compiled by gcc 4.8.4 was correct)
>
> I think that IR codes generated LLVM for 128bit floating-point operations
> are not wrong, but assembly generated from LLVM IR codes produced wrong
> result.
> (: Because the compiled program produced the wrong result only in x86
> machine)

I can't reproduce this on x86_64. I have Clang 3.9.0 packaged by my
distribution (Arch Linux) and apart from me having to tell Clang to
look in the right place for the header file
I get the output you show above

```
$ clang --version
clang version 3.9.0 (tags/RELEASE_390/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
$ /usr/bin/clang -O3 -lquadmath -I
/usr/lib/gcc/x86_64-pc-linux-gnu/6.2.1/include float128.c  && ./a.out
-3.140000E+00
1.570000E+01
8.140000E+00
```


> How can I get the right result of 128bit floating-point operations using
> LLVM?
>
>
> [ Environments ]
> - CPU: Intel(R) core(TM) i7-6700
> - OS: Ubuntu 14.04 (32bit)
> - LLVM target triple: i686-pc-linux-gnu
> - LLVM version: LLVM 3.8 / LLVM 3.9
> - Clang version: 3.9
> (※ use gcc 4.8.4 for building LLVM & Clang)

Huh why are you using a 32-bit version of Ubuntu. You clearly have a
64-bit cpu but your target tripple is "i686...".

If I do

```
/usr/bin/clang -O3 -m32 -lquadmath -I
/usr/lib/gcc/x86_64-pc-linux-gnu/6.2.1/include float128.c  && ./a.out
6.399878E-4935
6.374229E-4935
6.374228E-4935
```

Which is very very wrong. Non optimized output is diferent too

```
/usr/bin/clang -O0 -m32 -lquadmath -I
/usr/lib/gcc/x86_64-pc-linux-gnu/6.2.1/include float128.c && ./a.out
-8.378461E+4270
0.000000E+00
1.194574E-4942
```

I took the liberty of modifying your program slightly to print out the
bits (I don't trust printf and the like at all).

```
#include <inttypes.h>
#include <quadmath.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

void dump_float_128(__float128 f, const char *name) {
  uint64_t data[] = {0, 0};
  memcpy(&data, &f, sizeof(f));
  // Assuming little-endian
  printf("%s value: 0x%.16" PRIx64 "%.16" PRIx64 "\n", name, data[1], data[0]);
}
int main() {
    __float128 a = 3.14;
    char buf[128];
    dump_float_128(a, "a");

    __float128 negA = -a;
    quadmath_snprintf(buf, sizeof(buf), "%QE", negA);
    printf("negA: %s\n", buf);
    dump_float_128(negA, "negA");

    __float128 aTimesFive = a * 5.0;
    quadmath_snprintf(buf, sizeof(buf), "%QE", aTimesFive);
    printf("aTimeFive: %s\n", buf);
    dump_float_128(aTimesFive, "aTimesFive");

    __float128 aAddFive = a + 5.0;
    quadmath_snprintf(buf, sizeof(buf), "%QE", aAddFive);
    printf("aAddFive: %s\n", buf);
    dump_float_128(aAddFive, "aAddFive");

    return 0;
}
```

```
$ /usr/bin/clang -O0 -m32 -lquadmath  -I
/usr/lib/gcc/x86_64-pc-linux-gnu/6.2.1/include float128.c && ./a.out
a value: 0x400091eb851eb851f000000000000000
negA: -6.462522E-4538
negA value: 0x763d4e2ef743adc408048ed0400091eb
aTimeFive: 3.645761E-4937
aTimesFive value: 0xb5ed4e31f1312a6d4a36bcc8044d75ae
aAddFive: 3.220963E-4945
aAddFive value: 0x0000001af743adc408048ed08001d1eb

$ /usr/bin/clang -O0 -m64 -lquadmath  -I
/usr/lib/gcc/x86_64-pc-linux-gnu/6.2.1/include float128.c && ./a.out
a value: 0x400091eb851eb851f000000000000000
negA: -3.140000E+00
negA value: 0xc00091eb851eb851f000000000000000
aTimeFive: 1.570000E+01
aTimesFive value: 0x4002f666666666666c00000000000000
aAddFive: 8.140000E+00
aAddFive value: 0x4002047ae147ae147c00000000000000
```

The bit representation looks wrong most of the time except the initial
constant (although it gets printed wrong).

The LLVM IR looks sane to me so I'm guessing this is a codegen bug for
x86 32-bit.

Dan.


More information about the llvm-dev mailing list