x86_fp80, f80, and -m96bit-long-double

Hi,

I am working on x86-64 and am working with a C frontend that folds
sizeof(long double) into 12. For now, I am trying to avoid modifying that
part of the frontend by finding a way to tell LLVM that 12 bytes are
allocated for x86_fp80. To explore this, I tried an experiment with
llvm-gcc:

% llvm-gcc --version | head -1
llvm-gcc (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2.8)
% llvmc --version
Low Level Virtual Machine (http://llvm.org/):
  llvm version 2.8
  Optimized build.
  Built Oct 25 2010 (10:39:46).
  Host: x86_64-unknown-linux-gnu
  Host CPU: corei7

  Registered Targets:
    (none)
% cat > test.c
#include <stdio.h>
#include <stdlib.h>
#define SIZE 5
int
main(void)
{
  long double *a = malloc(sizeof(long double) * SIZE);
  for (int i = 0; i < SIZE; ++i)
    a[i] = i+1;
  for (int i = 0; i < SIZE; ++i)
    printf ("a[%d] = %Lf\n", i, a[i]);
  free (a);
  return 0;
}
% llvm-gcc -std=c99 -m96bit-long-double -emit-llvm -S -o test.ll test.c
% llvmc test.ll
% valgrind ./a.out |& grep Invalid
==3882== Invalid write of size 4
==3882== Invalid write of size 2
==3882== Invalid read of size 4
==3882== Invalid read of size 2

Looking inside test.ll, I see f80:128:128, but I also see sizeof(long
double)*5 folded into 60. Changing f80:128:128 to f80:96:96 does not fix
the errors reported by valgrind. If I instead fix the folded constant,
the errors go away, of course.

Does llvm-gcc not intend to support -m96bit-long-double?

Is there any way to instruct LLVM to assume 12 bytes are allocated for
x86_fp80? I suppose I could use [12 x i8] and then bitcast to x86_fp80
when I want to access the value. Is that the best way to handle this
(other than changing the way my frontend folds sizeof)?

Thanks.

Hi Joel,

I am working on x86-64 and am working with a C frontend that folds
sizeof(long double) into 12. For now, I am trying to avoid modifying that
part of the frontend by finding a way to tell LLVM that 12 bytes are
allocated for x86_fp80.

I think the "fact" that x86 long double is 16 byte aligned on x86-64 is
hard-wired in. I'm not sure how hard this would be to control via a
command line option (i.e. -m96bit-long-double).

Looking inside test.ll, I see f80:128:128, but I also see sizeof(long
double)*5 folded into 60. Changing f80:128:128 to f80:96:96 does not fix
the errors reported by valgrind. If I instead fix the folded constant,
the errors go away, of course.

If you had built llvm-gcc with checking enabled it would have aborted.

Does llvm-gcc not intend to support -m96bit-long-double?

Please open a bug report asking for support for -m96bit-long-double,
to ensure this issue is not forgotten.

Ciao,

Duncan.

I think the "fact" that x86 long double is 16 byte aligned on x86-64 is
hard-wired in.

Note that it's not just about alignment, but mainly about the reserved storage size.

I'm not sure how hard this would be to control via a
command line option (i.e. -m96bit-long-double).

Is there no different way to go about this? Our compiler currently supports the x87 long double type as
a) 10 bytes, for Turbo Pascal and Delphi-compatibility
b) 12 bytes, for non-Darwin ABI/C x86 compatibility
c) 16 bytes, for Darwin x86 ABI/C and x86_64 ABI/C compatibility

long doubles of type a) and c), or of type b) and c), can occur in the same compilation unit. A command line switch does not offer sufficient flexibility in this case.

Jonas

The right fix for this is to find the assumptions in the code generator and fix them.

-Chris

Hi Jonas,

I think the "fact" that x86 long double is 16 byte aligned on x86-64 is
hard-wired in.

Note that it's not just about alignment, but mainly about the reserved storage size.

actually it is about the alignment, since the reserved storage size is (in LLVM)
the number of bytes needed to store the type (i.e. 10 bytes) rounded up by the
alignment.

I'm not sure how hard this would be to control via a
command line option (i.e. -m96bit-long-double).

Is there no different way to go about this? Our compiler currently supports the x87 long double type as
a) 10 bytes, for Turbo Pascal and Delphi-compatibility

I doubt 10 bytes is supportable in LLVM without some surgery. Consider an
array of long double,
   long double A[2];
Then the following should be true in C (not sure if the C standard requires
this, but gcc and clang seem to work this way):
   sizeof(long double) == (char*)&A[1] - (char*)&A[0]
If sizeof(long double) is 10, then this means that A[1] will not be aligned
if A[0] is aligned. This is why sizeof is always a multiple of the alignment.
So to get a sizeof of 10 bytes, you would either have to ditch this rule (ouch)
or drop the alignment of long double down to 2 (ouch).

b) 12 bytes, for non-Darwin ABI/C x86 compatibility

This can be done easily by hand modifying LLVM, but I guess you don't want that.
This is the default on x86-32 linux targets. On x86-64 targets it is 16 bytes,
and you want some method of overriding that. Not sure what Chris's view on this
would be.

c) 16 bytes, for Darwin x86 ABI/C and x86_64 ABI/C compatibility

This is what is hard-wired into LLVM right now.

long doubles of type a) and c), or of type b) and c), can occur in the same compilation unit. A command line switch does not offer sufficient flexibility in this case.

Ah, I should have read this first! In that case there is not really any choice.
In the LLVM IR, you should not work with arrays of long double, you should use
for example (in case a) arrays of [10 x i8]. You can still happily store a long
double into any position in the array (on all platforms storing a long double
only stores 10 bytes). In short, on a platform where LLVM's getTypeAllocSize
(which is LLVM's notion of "sizeof") returns something different to what you
would like sizeof to be, you will have to allocate a different artificial type
to get the storage size you want.

Ciao,

Duncan.

Hi Chris,

I think the "fact" that x86 long double is 16 byte aligned on x86-64 is
hard-wired in.

Note that it's not just about alignment, but mainly about the reserved storage size.

I'm not sure how hard this would be to control via a
command line option (i.e. -m96bit-long-double).

Is there no different way to go about this? Our compiler currently supports the x87 long double type as
a) 10 bytes, for Turbo Pascal and Delphi-compatibility
b) 12 bytes, for non-Darwin ABI/C x86 compatibility
c) 16 bytes, for Darwin x86 ABI/C and x86_64 ABI/C compatibility

long doubles of type a) and c), or of type b) and c), can occur in the same compilation unit. A command line switch does not offer sufficient flexibility in this case.

The right fix for this is to find the assumptions in the code generator and fix them.

since storing a long double always writes only 10 bytes, I think it is easy
enough for Jonas to get the effect he wants by allocating some other type
which has his desired size, and bitcasting pointers to get a (long double)*
which he can use to read/write.

Ciao,

Duncan.

Ok, thanks!

Jonas

Hi Duncan,

Hi Joel,

Does llvm-gcc not intend to support -m96bit-long-double?

Please open a bug report asking for support for -m96bit-long-double,
to ensure this issue is not forgotten.

Given the thread that followed this post, I'm not sure whether you still
want a bug report. Is the plan to fix -m96bit-long-double or just remove
it and expect users to bitcast a [12 x i8] instead?

I don't think anyone really has a plan :slight_smile: However it seems wrong that the
compiler should either crash (if built with checking enabled) or produce
wrong code (if built with checking disabled) when passed -m96bit-long-double,
so please open a bug report anyway. Probably the implementation would be to
generate [12 x i8] for long double memory when passed the flag.

Ciao,

Duncan.

Hi Duncan,