Problem about 128bit floating-point operations in x86 machines

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)

How can I get the right result of 128bit floating-point operations using LLVM?

[ Environments ]

  • CPU: Intel(R) core™ 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)

Thank you. :slight_smile:

Sincerely,
Stella

Hi Kyoungju,

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.