[llvm] [PowerPC] fix `ppc_fp128` `FABS` miscompile (PR #209286)
Folkert de Vries via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 28 04:19:16 PDT 2026
https://github.com/folkertdev updated https://github.com/llvm/llvm-project/pull/209286
>From 2d5d6e10aa1ac45d00376fa14034fd423cd6e2f4 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Mon, 13 Jul 2026 21:42:40 +0200
Subject: [PATCH 1/8] [PowerPC] fix `ppc_fp128` `FABS` bug
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.
This causes no end of subtle miscompilations, commit fixes just one. All
three fix locations are required to get the correct output for this
reproducer.
---
llvm/lib/Analysis/ValueTracking.cpp | 5 +++
.../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 17 +++++++--
.../CodeGen/SelectionDAG/TargetLowering.cpp | 7 +++-
llvm/test/CodeGen/PowerPC/fp128-fabs.ll | 36 +++++++++++++++++++
.../test/Transforms/InstCombine/known-bits.ll | 5 ++-
5 files changed, 66 insertions(+), 4 deletions(-)
create mode 100644 llvm/test/CodeGen/PowerPC/fp128-fabs.ll
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 59631873305d4..fd1a4b5b4d401 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -1528,6 +1528,11 @@ static void computeKnownBitsFromOperator(const Operator *I,
computeKnownFPClass(V, DemandedElts, fcAllFlags, Q, Depth + 1);
FPClassTest FPClasses = Result.KnownFPClasses;
+ // The position of the sign bit for ppc_fp128 is endian-dependent.
+ if (!APFloat::hasSignBitInMSB(FPType->getFltSemantics()) ||
+ FPType->isPPC_FP128Ty())
+ break;
+
// TODO: Treat it as zero/poison if the use of I is unreachable.
if (FPClasses == fcNone)
break;
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 626803ed92a40..a6306f9f57747 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -4157,11 +4157,24 @@ KnownBits SelectionDAG::computeKnownBits(SDValue Op, const APInt &DemandedElts,
break;
}
- case ISD::FABS:
- // fabs clears the sign bit
+ case ISD::FABS: {
Known = computeKnownBits(Op.getOperand(0), DemandedElts, Depth + 1);
Known.makeNonNegative();
+ EVT SVT = Op.getValueType().getScalarType();
+ if (SVT == MVT::ppcf128) {
+ // The sign bit position depends on endianness: ppc_fp128 is two doubles
+ // in a trenchcoat, fabs only clears the sign bit of the high-order
+ // double.
+ Known.resetAll();
+ Known.Zero.setBit(getDataLayout().isBigEndian() ? 127 : 63);
+ } else if (APFloat::hasSignBitInMSB(SVT.getFltSemantics())) {
+ // IEEE-like formats, bf16, x86_fp80: the sign bit is the integer MSB,
+ // fabs clears that sign bit.
+ Known.makeNonNegative();
+ }
+
break;
+ }
case ISD::FGETSIGN:
// All bits are zero except the low bit.
Known.Zero.setBitsFrom(1);
diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index bca34c5c347ee..c25e12d45c2ed 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -3058,8 +3058,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;
+ APInt SignMask = APInt::getSignMask(BitWidth);
if (!DemandedBits.intersects(SignMask))
return TLO.CombineTo(Op, Op0);
diff --git a/llvm/test/CodeGen/PowerPC/fp128-fabs.ll b/llvm/test/CodeGen/PowerPC/fp128-fabs.ll
new file mode 100644
index 0000000000000..ef4932c70deb7
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/fp128-fabs.ll
@@ -0,0 +1,36 @@
+; 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
+}
+
+declare ppc_fp128 @llvm.fabs.ppcf128(ppc_fp128)
diff --git a/llvm/test/Transforms/InstCombine/known-bits.ll b/llvm/test/Transforms/InstCombine/known-bits.ll
index acf09bc03c1eb..30eb431644397 100644
--- a/llvm/test/Transforms/InstCombine/known-bits.ll
+++ b/llvm/test/Transforms/InstCombine/known-bits.ll
@@ -1537,9 +1537,12 @@ define i16 @test_inf_only_bfloat(bfloat nofpclass(nan sub norm zero) %x) {
ret i16 %and
}
+; A bitcast from ppc_fp128 to i128 is endian-dependent.
define i128 @test_inf_only_ppc_fp128(ppc_fp128 nofpclass(nan sub norm zero) %x) {
; CHECK-LABEL: @test_inf_only_ppc_fp128(
-; CHECK-NEXT: ret i128 9218868437227405312
+; CHECK-NEXT: [[TMP1:%.*]] = call ppc_fp128 @llvm.fabs.ppcf128(ppc_fp128 [[X:%.*]])
+; CHECK-NEXT: [[AND:%.*]] = bitcast ppc_fp128 [[TMP1]] to i128
+; CHECK-NEXT: ret i128 [[AND]]
;
%y = bitcast ppc_fp128 %x to i128
%and = and i128 %y, 170141183460469231731687303715884105727
>From 74db7a46af32a6f61b6b94156771d948a4501f14 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Thu, 30 Jul 2026 11:04:08 +0200
Subject: [PATCH 2/8] nits and add test
---
llvm/lib/Analysis/ValueTracking.cpp | 8 ++---
.../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 1 -
llvm/test/CodeGen/PowerPC/fp128-fabs.ll | 30 +++++++++++++++++--
3 files changed, 32 insertions(+), 7 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index fd1a4b5b4d401..00de73ad7cd59 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -1528,15 +1528,15 @@ static void computeKnownBitsFromOperator(const Operator *I,
computeKnownFPClass(V, DemandedElts, fcAllFlags, Q, Depth + 1);
FPClassTest FPClasses = Result.KnownFPClasses;
+ // TODO: Treat it as zero/poison if the use of I is unreachable.
+ if (FPClasses == fcNone)
+ break;
+
// The position of the sign bit for ppc_fp128 is endian-dependent.
if (!APFloat::hasSignBitInMSB(FPType->getFltSemantics()) ||
FPType->isPPC_FP128Ty())
break;
- // TODO: Treat it as zero/poison if the use of I is unreachable.
- if (FPClasses == fcNone)
- break;
-
if (Result.isKnownNever(fcNormal | fcSubnormal | fcNan)) {
Known.setAllConflict();
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index a6306f9f57747..9d441a19453df 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -4159,7 +4159,6 @@ KnownBits SelectionDAG::computeKnownBits(SDValue Op, const APInt &DemandedElts,
}
case ISD::FABS: {
Known = computeKnownBits(Op.getOperand(0), DemandedElts, Depth + 1);
- Known.makeNonNegative();
EVT SVT = Op.getValueType().getScalarType();
if (SVT == MVT::ppcf128) {
// The sign bit position depends on endianness: ppc_fp128 is two doubles
diff --git a/llvm/test/CodeGen/PowerPC/fp128-fabs.ll b/llvm/test/CodeGen/PowerPC/fp128-fabs.ll
index ef4932c70deb7..f01b8540decda 100644
--- a/llvm/test/CodeGen/PowerPC/fp128-fabs.ll
+++ b/llvm/test/CodeGen/PowerPC/fp128-fabs.ll
@@ -5,7 +5,7 @@
; 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
+; that we do not incorrectly optimize based on the (false!) assumption that
; fabs just clears the MSB.
define i1 @msb_set(ppc_fp128 %x) {
@@ -33,4 +33,30 @@ entry:
ret i1 %cmp
}
-declare ppc_fp128 @llvm.fabs.ppcf128(ppc_fp128)
+; Check that fabs on negative ppc_fp128 produces a non-negative value.
+define i1 @fabs_clears_sign_le(ppc_fp128 %x) {
+; LE-LABEL: fabs_clears_sign_le:
+; 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_le:
+; BE: # %bb.0: # %entry
+; BE-NEXT: li 3, 0
+; BE-NEXT: blr
+;
+; BE32-LABEL: fabs_clears_sign_le:
+; 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
+}
+
>From d6d6fb5ec6c4ffa00b7e7c4016a4bfebc3e17057 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Thu, 30 Jul 2026 23:51:48 +0200
Subject: [PATCH 3/8] fix endiannes bug in `AssertNoFPClass` too
---
.../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 17 ++++-
llvm/test/CodeGen/PowerPC/fp128-fabs.ll | 1 -
llvm/test/CodeGen/PowerPC/nofpclass.ll | 64 ++++++++++++++++---
3 files changed, 69 insertions(+), 13 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 9d441a19453df..6b0744392bb28 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -4143,16 +4143,29 @@ KnownBits SelectionDAG::computeKnownBits(SDValue Op, const APInt &DemandedElts,
FPClassTest NoFPClass =
static_cast<FPClassTest>(Op.getConstantOperandVal(1));
+
+ EVT SVT = Op.getValueType().getScalarType();
+ unsigned SignBitPos;
+ if (SVT == MVT::ppcf128)
+ // The sign bit position depends on endianness: ppc_fp128 is two doubles
+ // in a trenchcoat, and it is the high-order double that carries the sign.
+ SignBitPos = getDataLayout().isBigEndian() ? 127 : 63;
+ else if (APFloat::hasSignBitInMSB(SVT.getFltSemantics()))
+ // IEEE-like formats, bf16, x86_fp80: the sign bit is the integer MSB.
+ SignBitPos = BitWidth - 1;
+ else
+ break;
+
const FPClassTest NegativeTestMask = fcNan | fcNegative;
if ((NoFPClass & NegativeTestMask) == NegativeTestMask) {
// Cannot be negative.
- Known.makeNonNegative();
+ Known.Zero.setBit(SignBitPos);
}
const FPClassTest PositiveTestMask = fcNan | fcPositive;
if ((NoFPClass & PositiveTestMask) == PositiveTestMask) {
// Cannot be positive.
- Known.makeNegative();
+ Known.One.setBit(SignBitPos);
}
break;
diff --git a/llvm/test/CodeGen/PowerPC/fp128-fabs.ll b/llvm/test/CodeGen/PowerPC/fp128-fabs.ll
index f01b8540decda..7f60f4db2c6c0 100644
--- a/llvm/test/CodeGen/PowerPC/fp128-fabs.ll
+++ b/llvm/test/CodeGen/PowerPC/fp128-fabs.ll
@@ -59,4 +59,3 @@ entry:
%cmp = icmp slt i128 %v, 0
ret i1 %cmp
}
-
diff --git a/llvm/test/CodeGen/PowerPC/nofpclass.ll b/llvm/test/CodeGen/PowerPC/nofpclass.ll
index b08e810cd1cca..03482127ff7f1 100644
--- a/llvm/test/CodeGen/PowerPC/nofpclass.ll
+++ b/llvm/test/CodeGen/PowerPC/nofpclass.ll
@@ -1,15 +1,59 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
-; RUN: llc -mtriple=powerpc64le-unknown-linux-gnu < %s | FileCheck %s
-; RUN: llc -mtriple=powerpc64-ibm-aix-xcoff < %s | FileCheck %s
+; 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
+; RUN: llc -verify-machineinstrs -mtriple=powerpc64-ibm-aix-xcoff < %s | FileCheck %s -check-prefix=AIX
-; TODO: Update this test after adding the proper expansion of nofpclass for
-; ppc_fp128 to test with more masks and to demonstrate preserving nofpclass
-; after legalization.
+define i1 @negative(ppc_fp128 nofpclass(nan pinf psub pnorm pzero) %x) {
+; LE-LABEL: negative:
+; LE: # %bb.0: # %entry
+; LE-NEXT: mffprd 3, 2
+; LE-NEXT: rldicl 3, 3, 1, 63
+; LE-NEXT: blr
+;
+; BE-LABEL: negative:
+; BE: # %bb.0: # %entry
+; BE-NEXT: li 3, 1
+; BE-NEXT: blr
+;
+; BE32-LABEL: negative:
+; BE32: # %bb.0: # %entry
+; BE32-NEXT: li 3, 1
+; BE32-NEXT: blr
+;
+; AIX-LABEL: negative:
+; AIX: # %bb.0: # %entry
+; AIX-NEXT: li 3, 1
+; AIX-NEXT: blr
+entry:
+ %v = bitcast ppc_fp128 %x to i128
+ %cmp = icmp slt i128 %v, 0
+ ret i1 %cmp
+}
-define ppc_fp128 @f(ppc_fp128 nofpclass(nan) %s) {
-; CHECK-LABEL: f:
-; CHECK: # %bb.0: # %entry
-; CHECK-NEXT: blr
+define i1 @nonnegative(ppc_fp128 nofpclass(nan ninf nsub nnorm nzero) %x) {
+; LE-LABEL: nonnegative:
+; LE: # %bb.0: # %entry
+; LE-NEXT: mffprd 3, 2
+; LE-NEXT: rldicl 3, 3, 1, 63
+; LE-NEXT: blr
+;
+; BE-LABEL: nonnegative:
+; BE: # %bb.0: # %entry
+; BE-NEXT: li 3, 0
+; BE-NEXT: blr
+;
+; BE32-LABEL: nonnegative:
+; BE32: # %bb.0: # %entry
+; BE32-NEXT: li 3, 0
+; BE32-NEXT: blr
+;
+; AIX-LABEL: nonnegative:
+; AIX: # %bb.0: # %entry
+; AIX-NEXT: li 3, 0
+; AIX-NEXT: blr
entry:
- ret ppc_fp128 %s
+ %v = bitcast ppc_fp128 %x to i128
+ %cmp = icmp slt i128 %v, 0
+ ret i1 %cmp
}
>From 3070f84b1711fdfe799bda08e10875eb5bc35a3b Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Fri, 31 Jul 2026 00:21:18 +0200
Subject: [PATCH 4/8] check that sign knowledge is used on LE too
---
llvm/test/CodeGen/PowerPC/fp128-fabs.ll | 50 ++++++++++++++++++++++---
1 file changed, 45 insertions(+), 5 deletions(-)
diff --git a/llvm/test/CodeGen/PowerPC/fp128-fabs.ll b/llvm/test/CodeGen/PowerPC/fp128-fabs.ll
index 7f60f4db2c6c0..49a14bd7c0015 100644
--- a/llvm/test/CodeGen/PowerPC/fp128-fabs.ll
+++ b/llvm/test/CodeGen/PowerPC/fp128-fabs.ll
@@ -33,9 +33,10 @@ entry:
ret i1 %cmp
}
-; Check that fabs on negative ppc_fp128 produces a non-negative value.
-define i1 @fabs_clears_sign_le(ppc_fp128 %x) {
-; LE-LABEL: fabs_clears_sign_le:
+; 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
@@ -43,12 +44,12 @@ define i1 @fabs_clears_sign_le(ppc_fp128 %x) {
; LE-NEXT: rldicl 3, 3, 1, 63
; LE-NEXT: blr
;
-; BE-LABEL: fabs_clears_sign_le:
+; BE-LABEL: fabs_clears_sign_be:
; BE: # %bb.0: # %entry
; BE-NEXT: li 3, 0
; BE-NEXT: blr
;
-; BE32-LABEL: fabs_clears_sign_le:
+; BE32-LABEL: fabs_clears_sign_be:
; BE32: # %bb.0: # %entry
; BE32-NEXT: li 3, 0
; BE32-NEXT: blr
@@ -59,3 +60,42 @@ entry:
%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
+}
>From fa7525255473134ef850ed1d3dce87f51f03c931 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Fri, 31 Jul 2026 11:47:04 +0200
Subject: [PATCH 5/8] fix `copysign` for `ppc_fp128`
---
llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index c81c43c94b0f0..d2b0179bb261d 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -20062,6 +20062,11 @@ SDValue DAGCombiner::visitFCOPYSIGN(SDNode *N) {
if (VT != N1.getValueType())
return SDValue();
+ // ppcf128 has two sign bits (in bits 127 and 63), the logic below is invalid.
+ EVT SVT = VT.getScalarType();
+ if (!APFloat::hasSignBitInMSB(SVT.getFltSemantics()) || SVT == MVT::ppcf128)
+ return SDValue();
+
// If this is equivalent to a disjoint or, replace it with one. This can
// happen if the sign operand is a sign mask (i.e., x << sign_bit_position).
if (DAG.SignBitIsZeroFP(N0) &&
>From d7b6b7485fa18ed99c14ca6f994a15d23ce54c75 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Fri, 31 Jul 2026 21:42:43 +0200
Subject: [PATCH 6/8] update copysign tests
---
llvm/test/CodeGen/PowerPC/copysignl.ll | 29 +++++++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)
diff --git a/llvm/test/CodeGen/PowerPC/copysignl.ll b/llvm/test/CodeGen/PowerPC/copysignl.ll
index e78038ab183c6..5261c27e5ca15 100644
--- a/llvm/test/CodeGen/PowerPC/copysignl.ll
+++ b/llvm/test/CodeGen/PowerPC/copysignl.ll
@@ -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
; 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
>From 42736cc97f9056c521b178c1ddc5032b916ad3d5 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Fri, 31 Jul 2026 21:45:56 +0200
Subject: [PATCH 7/8] fix another ppcf128 bug in fneg
---
llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp | 7 +++++++
llvm/test/CodeGen/PowerPC/fneg.ll | 10 +++++-----
2 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index 97022d0ab5450..e72be964a729a 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -3125,6 +3125,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;
+
APInt SignMask = APInt::getSignMask(BitWidth);
if (!DemandedBits.intersects(SignMask))
diff --git a/llvm/test/CodeGen/PowerPC/fneg.ll b/llvm/test/CodeGen/PowerPC/fneg.ll
index 191594ab72316..a16df9122a321 100644
--- a/llvm/test/CodeGen/PowerPC/fneg.ll
+++ b/llvm/test/CodeGen/PowerPC/fneg.ll
@@ -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
; 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
; LE-NEXT: blr
;
; BE-LABEL: fneg_fneg_msb:
>From efe0d3d9918f98e8fc19ce2cc9186614c2b8b455 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Fri, 31 Jul 2026 21:49:34 +0200
Subject: [PATCH 8/8] formatting
---
llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index e72be964a729a..f7f98882d6bae 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -3128,7 +3128,7 @@ bool TargetLowering::SimplifyDemandedBits(
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).
+ // ppc_fp128 has two sign bits (at bits 127 and 63).
if (!APFloat::hasSignBitInMSB(SVT.getFltSemantics()) || SVT == MVT::ppcf128)
break;
More information about the llvm-commits
mailing list