This is an archive of the discontinued LLVM Phabricator instance.

[Clang] Support conversion between PPC double-double and IEEE float128
AbandonedPublic

Authored by qiucf on Sep 14 2021, 2:54 AM.

Details

Reviewers
hubert.reinterpretcast
nemanjai
shchenz
jsji
Group Reviewers
Restricted Project
Summary

The motivation is that we may encounter cases determining if 'long double' is compatible with either __float128 or __ibm128 type. Marking such conversion as legal helps when handling such cases.

Diff Detail

Event Timeline

qiucf created this revision.Sep 14 2021, 2:54 AM
qiucf requested review of this revision.Sep 14 2021, 2:54 AM
Herald added a project: Restricted Project. · View Herald TranscriptSep 14 2021, 2:54 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
clang/test/CodeGen/ibm128-cast.c
59

It's controversial enough to consider, as well-formed, a case requiring application of the usual arithmetic conversions to a pair of operands with floating types that each have distinct values not representable by the type of the other operand; it is even more controversial to consider the type with the narrower finite range to be the common real type.

qiucf updated this revision to Diff 385709.Nov 8 2021, 11:50 PM
qiucf marked an inline comment as done.

Restrict implicit conversion in arithmetic and comparison

clang/lib/Sema/SemaExpr.cpp
1542

GCC does not allow compound assignments either and that is is a context where the "usual arithmetic conversions" apply. Allowing them means this patch is going to need to be inventive with the semantics, because the type in with the calculation is to be performed is supposed to be the same one that would occur for the non-assigning version of the arithmetic operation.

Noting as well that "Conditional" is also a case where the "usual arithmetic conversions" apply. GCC seems to be happy with that particular case for some reason, but I don't think it makes sense to allow it for Clang: GCC's implementation has questionable semantics like choosing whatever type is not the same format as the ABI's 128-bit long double as the common type (in other words, the common type is IEEE for -mabi=ibmlongdouble and IBM for -mabi=ieeelongdouble).

GCC behaviour: x is "long double" and the conditional is __float128:

gcc -fsyntax-only -xc -<<<$'__ibm128 x;\n__float128 y;\nvoid q(int b) {\n  x + (b ? x : y);\n}'
<stdin>: In function ‘q’:
<stdin>:4:5: error: __float128 and long double cannot be used in the same expression

GCC behaviour: y is "long double" and the conditional is __ibm128:

gcc -fsyntax-only -xc -<<<$'__ibm128 x;\n__float128 y;\nvoid q(int b) {\n  y + (b ? x : y);\n}' -mabi=ieeelongdouble
cc1: warning: Using IEEE extended precision ‘long double’ [-Wpsabi]
<stdin>: In function ‘q’:
<stdin>:4:5: error: __ibm128 and long double cannot be used in the same expression

If we go with the GCC behaviour, there should be lots of explaining done. If we don't then, making the case an error avoids the inconsistency of making a choice regarding the common type for conditionals but not other applications of the usual arithmetic conversions.

clang/test/Sema/float128-ld-incompatibility.cpp
39

There's no tests for compound assignment...

It doesn't seem like the cases of implicit conversion that are expected to be allowed have been tested with C++?

$ ./bin/clang -fsyntax-only -mcpu=pwr9 -mfloat128 -xc -<<<$'__ibm128 x; __float128 y; void f(void) { y = x; }'
Return:  0x00:0   Tue Nov  9 11:53:23 2021 EST
$ ./bin/clang -fsyntax-only -mcpu=pwr9 -mfloat128 -xc++ -<<<$'__ibm128 x; __float128 y; void f(void) { y = x; }'
<stdin>:1:46: error: assigning to '__float128' from incompatible type '__ibm128'
__ibm128 x; __float128 y; void f(void) { y = x; }
                                             ^
1 error generated.
Return:  0x01:1   Tue Nov  9 11:53:43 2021 EST

@qiucf, I'm on vacation (for 3 weeks). For the C++ side, I suggest having a test for the narrowing conversion diagnostic in C++. My recollection is that GCC's odd behaviour around the conditional operator has an analogue in its implementation of narrowing conversion detection (it's probably the same bug).

nemanjai requested changes to this revision.Nov 15 2021, 4:54 AM

Please provide a description for this patch which includes justification for why we want to allow conversion between the two types.
I am of the impression that allowing the two types to coexist in completely disjoint code should be fine, but I really don't see a compelling reason to allow conversions between the two types.

This revision now requires changes to proceed.Nov 15 2021, 4:54 AM
qiucf added a comment.EditedNov 16 2021, 9:16 PM

Please provide a description for this patch which includes justification for why we want to allow conversion between the two types.
I am of the impression that allowing the two types to coexist in completely disjoint code should be fine, but I really don't see a compelling reason to allow conversions between the two types.

The motivating case is failure after D92815: clang compiler-rt/test/builtins/Unit/multc3_test.c -I compiler-rt/lib/builtins/ -mfloat128 -mcpu=power9 -lm

compiler-rt/test/builtins/Unit/multc3_test.c:24:15: error: passing 'long double' to parameter of incompatible type '_Float128' (aka '__float128')

Because the piece of code will be expanded to:

long double _Complex x;

if ((__builtin_types_compatible_p(__typeof(creall(x)), _Float128)
         ? __isinff128(creall(x))
         : __builtin_isinf_sign(creall(x))) ||
    (__builtin_types_compatible_p(__typeof(cimagl(x)), _Float128)
         ? __isinff128(cimagl(x))
         : __builtin_isinf_sign(cimagl(x))))
  return inf;

which requires 'long double' (the same semantics to __ibm128 by default) and '_Float128' are compatible.

qiucf updated this revision to Diff 387819.Nov 16 2021, 9:25 PM

Disallow conversion in compound and conditional

qiucf updated this revision to Diff 387851.Nov 17 2021, 12:10 AM
qiucf marked an inline comment as done.

Fix C++ tests

qiucf edited the summary of this revision. (Show Details)Nov 17 2021, 12:12 AM

Because the piece of code will be expanded to:

long double _Complex x;

if ((__builtin_types_compatible_p(__typeof(creall(x)), _Float128)
         ? __isinff128(creall(x))
         : __builtin_isinf_sign(creall(x))) ||
    (__builtin_types_compatible_p(__typeof(cimagl(x)), _Float128)
         ? __isinff128(cimagl(x))
         : __builtin_isinf_sign(cimagl(x))))
  return inf;

which requires 'long double' (the same semantics to __ibm128 by default) and '_Float128' are compatible.

Noting that the way the types are implemented in Clang, the conversion isn't the first problem with the above code. The types with the same representation are not considered "compatible" by Clang:

extern char x[__builtin_types_compatible_p(long double, __ibm128) ? 1 : -1]; // errors
extern char x[__builtin_types_compatible_p(long double, __ieee128) ? 1 : -1]; // errors too

Compiler Explorer link: https://godbolt.org/z/fP3MfdexM

clang/lib/Sema/SemaExpr.cpp
1540

The ternary operator seems to be something other than (straightforward) "arithmetic or comparison".

clang/test/CodeGen/ibm128-cast.c
115

Should have compound assignment tests for C as well.

115

Should have compound assignment tests for C as well.

clang/test/Sema/float128-ld-incompatibility.cpp
14

Should also test __ibm128 cases.

clang/test/Sema/float128-ld-incompatibility.cpp
14

The C++ committee has advised that this implicit conversion should be considered ill-formed (see other comment).

Note that the allowed implicit conversion from __ibm128 to long double (and vice versa) is still a conversion, which means that overload resolution is still a problem:

void f(__ibm128);
void f(int);
void g(long double d) { return f(d); } // okay with GCC but not Clang; https://godbolt.org/z/fonsEbbY1
37–38

The C++ committee has advised that these are not okay as implicit conversions:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1467r7.html#implicit

Additional lines testing static_cast would be appropriate.

clang/test/Sema/float128-ld-incompatibility.cpp
14

Even if the implicit conversion is to be allowed, there is not much reason why this is not considered narrowing (given the revised definition of narrowing conversions).

GCC's behaviour around the narrowing check has limited value as a reference point: Which of __float128 or __ibm128 is considered by GCC on Power to be the "wider type" depends on the -mabi option. That is, the behaviour is not consistent with there being a principled decision made by the GCC developers as to which representation format is "wider".

37–38

I guess this could be controversial since converting from __float128 to float (for example) would also be prohibited (if __float128 were to be considered to be an extended floating-point type).

qiucf updated this revision to Diff 392638.Dec 7 2021, 9:12 PM
qiucf marked 5 inline comments as done.

Update some cases

qiucf added a comment.Dec 7 2021, 9:12 PM

Because the piece of code will be expanded to:

long double _Complex x;

if ((__builtin_types_compatible_p(__typeof(creall(x)), _Float128)
         ? __isinff128(creall(x))
         : __builtin_isinf_sign(creall(x))) ||
    (__builtin_types_compatible_p(__typeof(cimagl(x)), _Float128)
         ? __isinff128(cimagl(x))
         : __builtin_isinf_sign(cimagl(x))))
  return inf;

which requires 'long double' (the same semantics to __ibm128 by default) and '_Float128' are compatible.

Noting that the way the types are implemented in Clang, the conversion isn't the first problem with the above code. The types with the same representation are not considered "compatible" by Clang:

extern char x[__builtin_types_compatible_p(long double, __ibm128) ? 1 : -1]; // errors
extern char x[__builtin_types_compatible_p(long double, __ieee128) ? 1 : -1]; // errors too

Compiler Explorer link: https://godbolt.org/z/fP3MfdexM

Thanks for the reminder. Here GCC and Clang diverges in the handling of __ibm128/__float128 and long double. Not sure whether GCC will 'fix' the behavior, but here (and in most of the use case in glibc headers) it's __builtin_types_compatible_p(..., _Float128) where GCC/Clang behaves the same.

clang/test/Sema/float128-ld-incompatibility.cpp
14

Thanks for the information. The behavior of GCC looks somewhat reasonable but I notice the naming convention of support functions is interesting: __ibm128 to __float128 is 'extend', __float128 to __ibm128 'truncate'.

Thanks for the reminder. Here GCC and Clang diverges in the handling of __ibm128/__float128 and long double. Not sure whether GCC will 'fix' the behavior, but here (and in most of the use case in glibc headers) it's __builtin_types_compatible_p(..., _Float128) where GCC/Clang behaves the same.

I thought Clang doesn't have _Float128 yet; that's D111382, which makes _Float128 act like __float128 (and, in turn, like __ieee128).

With -mabi=ieeelongdouble:

extern char x[__builtin_types_compatible_p(long double, __float128) ? 1 : -1]; // GCC accepts; Clang rejects

https://godbolt.org/z/fGbY1Y1PT

clang/test/Sema/float128-ld-incompatibility.cpp
14

Did you try GCC with -mabi=ieeelongdouble and -pedantic-errors?

extern __ibm128 x;
long double q{ x }; // narrowing error

https://godbolt.org/z/3YazKv9f7

@qiucf, fyi, have vacation until January.

qiucf added a comment.Dec 28 2021, 9:27 PM

Thanks for the reminder. Here GCC and Clang diverges in the handling of __ibm128/__float128 and long double. Not sure whether GCC will 'fix' the behavior, but here (and in most of the use case in glibc headers) it's __builtin_types_compatible_p(..., _Float128) where GCC/Clang behaves the same.

I thought Clang doesn't have _Float128 yet; that's D111382, which makes _Float128 act like __float128 (and, in turn, like __ieee128).

With -mabi=ieeelongdouble:

extern char x[__builtin_types_compatible_p(long double, __float128) ? 1 : -1]; // GCC accepts; Clang rejects

https://godbolt.org/z/fGbY1Y1PT

I tried making them 'compatible', but that only affects to C (and C++ doesn't have this builtin), std::is_same<long double, __float128> still says they're different. Should that be an issue?

jsji resigned from this revision.Jun 2 2022, 7:57 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 2 2022, 7:57 AM
qiucf abandoned this revision.Jun 13 2023, 10:17 PM
qiucf added a subscriber: tuliom.

There's a glibc patch in review to fix the motivation error in math.h (thanks to @tuliom ): https://patchwork.sourceware.org/project/glibc/patch/20230613215633.3179708-1-tuliom@ascii.art.br/ I think this revision can be closed.