-
Notifications
You must be signed in to change notification settings - Fork 18.6k
[PowerPC] fix ppc_fp128 FABS miscompile
#209286
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
2d5d6e1
74db7a4
d6d6fb5
3070f84
fa75252
5c894a9
f75acfd
d7b6b74
42736cc
efe0d3d
330b268
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3080,8 +3080,13 @@ bool TargetLowering::SimplifyDemandedBits( | |
| } | ||
| case ISD::FABS: { | ||
| SDValue Op0 = Op.getOperand(0); | ||
| APInt SignMask = APInt::getSignMask(BitWidth); | ||
| EVT SVT = Op0.getValueType(); | ||
|
|
||
| // The position of the sign bit for ppc_fp128 is endian-dependent. | ||
| if (!APFloat::hasSignBitInMSB(SVT.getFltSemantics()) || SVT == MVT::ppcf128) | ||
| break; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might be able to do slightly better here, but I'd rather be correct and slightly inefficient for a type that is almost never used than slightly more efficient and potentially subtly wrong. |
||
|
|
||
| APInt SignMask = APInt::getSignMask(BitWidth); | ||
| if (!DemandedBits.intersects(SignMask)) | ||
| return TLO.CombineTo(Op, Op0); | ||
|
|
||
|
|
@@ -3133,6 +3138,13 @@ bool TargetLowering::SimplifyDemandedBits( | |
| } | ||
| case ISD::FNEG: { | ||
| SDValue Op0 = Op.getOperand(0); | ||
| EVT SVT = Op0.getValueType(); | ||
|
|
||
| // The logic below assumes the sign bit is in the MSB. | ||
| // ppc_fp128 has two sign bits (at bits 127 and 63). | ||
| if (!APFloat::hasSignBitInMSB(SVT.getFltSemantics()) || SVT == MVT::ppcf128) | ||
| break; | ||
|
|
||
|
Comment on lines
+3143
to
+3147
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. another instance |
||
| APInt SignMask = APInt::getSignMask(BitWidth); | ||
|
|
||
| if (!DemandedBits.intersects(SignMask)) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -383,6 +383,13 @@ entry: | |
| define ppc_fp128 @copysign_signmask(ppc_fp128 %x, i1 %s) { | ||
| ; LE-LABEL: copysign_signmask: | ||
| ; LE: # %bb.0: # %entry | ||
| ; LE-NEXT: fmr 0, 1 | ||
| ; LE-NEXT: xsabsdp 1, 1 | ||
| ; LE-NEXT: xscmpudp 0, 0, 1 | ||
| ; LE-NEXT: beq 0, .LBB6_2 | ||
| ; LE-NEXT: # %bb.1: # %entry | ||
| ; LE-NEXT: xsnegdp 2, 2 | ||
| ; LE-NEXT: .LBB6_2: # %entry | ||
|
Comment on lines
+386
to
+392
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. copysign must copy both sign bits
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is that? Shouldn't it be endian based?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe that both sign bits contribute to the sign of the actual value. For instance #include <stdio.h>
#include <math.h>
union pair { long double ld; double d[2]; };
int main(void)
{
union pair a = { .ld = 1.0L - 0x1p-60L };
union pair b = { .ld = -(4.0L - 0x1p-58L) };
union pair c;
c.ld = copysignl(a.ld, b.ld);
printf("a = %g %g = %.36Lg\n", a.d[0], a.d[1], a.ld);
printf("b = %g %g = %.36Lg\n", b.d[0], b.d[1], b.ld);
printf("c = %g %g = %.36Lg\n", c.d[0], c.d[1], c.ld);
union pair wrong1 = { .d = { c.d[0], -c.d[1]} };
union pair wrong2 = { .d = { -c.d[0], c.d[1]} };
printf("wrong1 = %g %g = %.36Lg\n", wrong1.d[0], wrong1.d[1], wrong1.ld);
printf("wrong2 = %g %g = %.36Lg\n", wrong2.d[0], wrong2.d[1], wrong2.ld);
return 0;
}With $ powerpc64le-linux-gnu-gcc ppcf128_copysign.c -o cs
$ qemu-ppc64le -L /usr/powerpc64le-linux-gnu ./csGives As a The example was chosen so that the corresponding components of The
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To add: my understanding is that valid |
||
| ; LE-NEXT: mflr 0 | ||
| ; LE-NEXT: stdu 1, -32(1) | ||
| ; LE-NEXT: std 0, 48(1) | ||
|
|
@@ -400,6 +407,13 @@ define ppc_fp128 @copysign_signmask(ppc_fp128 %x, i1 %s) { | |
| ; | ||
| ; BE-LABEL: copysign_signmask: | ||
| ; BE: # %bb.0: # %entry | ||
| ; BE-NEXT: fmr 0, 1 | ||
| ; BE-NEXT: fabs 1, 1 | ||
| ; BE-NEXT: fcmpu 0, 0, 1 | ||
| ; BE-NEXT: beq 0, .LBB6_2 | ||
| ; BE-NEXT: # %bb.1: # %entry | ||
| ; BE-NEXT: fneg 2, 2 | ||
| ; BE-NEXT: .LBB6_2: # %entry | ||
| ; BE-NEXT: mflr 0 | ||
| ; BE-NEXT: stdu 1, -128(1) | ||
| ; BE-NEXT: std 0, 144(1) | ||
|
|
@@ -419,6 +433,13 @@ define ppc_fp128 @copysign_signmask(ppc_fp128 %x, i1 %s) { | |
| ; | ||
| ; BE-VSX-LABEL: copysign_signmask: | ||
| ; BE-VSX: # %bb.0: # %entry | ||
| ; BE-VSX-NEXT: fmr 0, 1 | ||
| ; BE-VSX-NEXT: xsabsdp 1, 1 | ||
| ; BE-VSX-NEXT: xscmpudp 0, 0, 1 | ||
| ; BE-VSX-NEXT: beq 0, .LBB6_2 | ||
| ; BE-VSX-NEXT: # %bb.1: # %entry | ||
| ; BE-VSX-NEXT: xsnegdp 2, 2 | ||
| ; BE-VSX-NEXT: .LBB6_2: # %entry | ||
| ; BE-VSX-NEXT: mflr 0 | ||
| ; BE-VSX-NEXT: stdu 1, -128(1) | ||
| ; BE-VSX-NEXT: std 0, 144(1) | ||
|
|
@@ -442,7 +463,13 @@ define ppc_fp128 @copysign_signmask(ppc_fp128 %x, i1 %s) { | |
| ; BE32-NEXT: stw 0, 100(1) | ||
| ; BE32-NEXT: .cfi_def_cfa_offset 96 | ||
| ; BE32-NEXT: .cfi_offset lr, 4 | ||
| ; BE32-NEXT: stfd 1, 40(1) | ||
| ; BE32-NEXT: fabs 0, 1 | ||
| ; BE32-NEXT: fcmpu 0, 1, 0 | ||
| ; BE32-NEXT: beq 0, .LBB6_2 | ||
| ; BE32-NEXT: # %bb.1: # %entry | ||
| ; BE32-NEXT: fneg 2, 2 | ||
| ; BE32-NEXT: .LBB6_2: # %entry | ||
| ; BE32-NEXT: stfd 0, 40(1) | ||
| ; BE32-NEXT: slwi 3, 3, 31 | ||
| ; BE32-NEXT: stw 3, 64(1) | ||
| ; BE32-NEXT: li 4, 0 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -145,7 +145,9 @@ entry: | |
| define i1 @fneg_msb(ppc_fp128 nofpclass(nan ninf nsub nnorm nzero) %x) { | ||
| ; LE-LABEL: fneg_msb: | ||
| ; LE: # %bb.0: # %entry | ||
| ; LE-NEXT: li 3, 1 | ||
| ; LE-NEXT: mffprd 3, 2 | ||
| ; LE-NEXT: not 3, 3 | ||
| ; LE-NEXT: rldicl 3, 3, 1, 63 | ||
|
Comment on lines
-148
to
+150
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. bit 63 determines the sign |
||
| ; LE-NEXT: blr | ||
| ; | ||
| ; BE-LABEL: fneg_msb: | ||
|
|
@@ -167,8 +169,7 @@ entry: | |
| define i1 @fneg_bit63(ppc_fp128 nofpclass(nan ninf nsub nnorm nzero) %x) { | ||
| ; LE-LABEL: fneg_bit63: | ||
| ; LE: # %bb.0: # %entry | ||
| ; LE-NEXT: mffprd 3, 1 | ||
| ; LE-NEXT: rldicl 3, 3, 1, 63 | ||
| ; LE-NEXT: li 3, 1 | ||
| ; LE-NEXT: blr | ||
| ; | ||
| ; BE-LABEL: fneg_bit63: | ||
|
|
@@ -200,8 +201,7 @@ entry: | |
| define i1 @fneg_fneg_msb(ppc_fp128 nofpclass(nan ninf nsub nnorm nzero) %x) { | ||
| ; LE-LABEL: fneg_fneg_msb: | ||
| ; LE: # %bb.0: # %entry | ||
| ; LE-NEXT: mffprd 3, 1 | ||
| ; LE-NEXT: rldicl 3, 3, 1, 63 | ||
| ; LE-NEXT: li 3, 0 | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here we now statically know the value of bit 63 |
||
| ; LE-NEXT: blr | ||
| ; | ||
| ; BE-LABEL: fneg_fneg_msb: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py | ||
| ; RUN: llc -verify-machineinstrs -mtriple=powerpc64le-unknown-linux-gnu < %s | FileCheck %s -check-prefix=LE | ||
| ; RUN: llc -verify-machineinstrs -mtriple=powerpc64-unknown-linux-gnu < %s | FileCheck %s -check-prefix=BE | ||
| ; RUN: llc -verify-machineinstrs -mtriple=powerpc-unknown-linux-gnu < %s | FileCheck %s -check-prefix=BE32 | ||
|
|
||
| ; On little-endian powerpc the bit position of the sign bit is bit 63, not | ||
| ; bit 127. However, APFloat reports that the sign bit is the MSB. Ensure | ||
| ; that we do not incorrectly optimize based on the (false!) assumption that | ||
| ; fabs just clears the MSB. | ||
|
|
||
| define i1 @msb_set(ppc_fp128 %x) { | ||
| ; LE-LABEL: msb_set: | ||
| ; LE: # %bb.0: # %entry | ||
| ; LE-NEXT: mffprd 3, 1 | ||
| ; LE-NEXT: mffprd 4, 2 | ||
| ; LE-NEXT: xor 3, 4, 3 | ||
| ; LE-NEXT: rldicl 3, 3, 1, 63 | ||
| ; LE-NEXT: blr | ||
| ; | ||
| ; BE-LABEL: msb_set: | ||
| ; BE: # %bb.0: # %entry | ||
| ; BE-NEXT: li 3, 0 | ||
| ; BE-NEXT: blr | ||
| ; | ||
| ; BE32-LABEL: msb_set: | ||
| ; BE32: # %bb.0: # %entry | ||
| ; BE32-NEXT: li 3, 0 | ||
| ; BE32-NEXT: blr | ||
| entry: | ||
| %a = call ppc_fp128 @llvm.fabs.ppcf128(ppc_fp128 %x) | ||
| %v = bitcast ppc_fp128 %a to i128 | ||
| %cmp = icmp slt i128 %v, 0 | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
| ; On BE the ppc_fp128 sign bit is stored in bit 127, this information | ||
| ; makes the function return a constant there. | ||
| define i1 @fabs_clears_sign_be(ppc_fp128 %x) { | ||
| ; LE-LABEL: fabs_clears_sign_be: | ||
| ; LE: # %bb.0: # %entry | ||
| ; LE-NEXT: mffprd 3, 1 | ||
| ; LE-NEXT: mffprd 4, 2 | ||
| ; LE-NEXT: xor 3, 4, 3 | ||
| ; LE-NEXT: rldicl 3, 3, 1, 63 | ||
| ; LE-NEXT: blr | ||
| ; | ||
| ; BE-LABEL: fabs_clears_sign_be: | ||
| ; BE: # %bb.0: # %entry | ||
| ; BE-NEXT: li 3, 0 | ||
| ; BE-NEXT: blr | ||
| ; | ||
| ; BE32-LABEL: fabs_clears_sign_be: | ||
| ; BE32: # %bb.0: # %entry | ||
| ; BE32-NEXT: li 3, 0 | ||
| ; BE32-NEXT: blr | ||
| entry: | ||
| %neg = fneg ppc_fp128 %x | ||
| %a = call ppc_fp128 @llvm.fabs.ppcf128(ppc_fp128 %neg) | ||
| %v = bitcast ppc_fp128 %a to i128 | ||
| %cmp = icmp slt i128 %v, 0 | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
| ; On LE the ppc_fp128 sign bit is stored in bit 63, this information | ||
| ; makes the function return a constant there. | ||
| define i1 @fabs_clears_sign_le(ppc_fp128 %x) { | ||
| ; LE-LABEL: fabs_clears_sign_le: | ||
| ; LE: # %bb.0: # %entry | ||
| ; LE-NEXT: li 3, 0 | ||
| ; LE-NEXT: blr | ||
| ; | ||
| ; BE-LABEL: fabs_clears_sign_le: | ||
| ; BE: # %bb.0: # %entry | ||
| ; BE-NEXT: stfd 1, -16(1) | ||
| ; BE-NEXT: stfd 2, -8(1) | ||
| ; BE-NEXT: ld 3, -16(1) | ||
| ; BE-NEXT: ld 4, -8(1) | ||
| ; BE-NEXT: xor 3, 4, 3 | ||
| ; BE-NEXT: rldicl 3, 3, 1, 63 | ||
| ; BE-NEXT: blr | ||
| ; | ||
| ; BE32-LABEL: fabs_clears_sign_le: | ||
| ; BE32: # %bb.0: # %entry | ||
| ; BE32-NEXT: stwu 1, -32(1) | ||
| ; BE32-NEXT: .cfi_def_cfa_offset 32 | ||
| ; BE32-NEXT: stfd 1, 24(1) | ||
| ; BE32-NEXT: stfd 2, 16(1) | ||
| ; BE32-NEXT: lwz 3, 24(1) | ||
| ; BE32-NEXT: lwz 4, 16(1) | ||
| ; BE32-NEXT: xor 3, 4, 3 | ||
| ; BE32-NEXT: srwi 3, 3, 31 | ||
| ; BE32-NEXT: addi 1, 1, 32 | ||
| ; BE32-NEXT: blr | ||
| entry: | ||
| %neg = fneg ppc_fp128 %x | ||
| %a = call ppc_fp128 @llvm.fabs.ppcf128(ppc_fp128 %neg) | ||
| %v = bitcast ppc_fp128 %a to i128 | ||
| %masked = and i128 %v, 9223372036854775808 ; 1 << 63 | ||
| %cmp = icmp ne i128 %masked, 0 | ||
| ret i1 %cmp | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.