[llvm] [PowerPC][SDAG] fix bitcast on ppc_f128 swapping the two halves (PR #208969)

Folkert de Vries via llvm-commits llvm-commits at lists.llvm.org
Sat Oct 3 04:40:31 PDT 2026


https://github.com/folkertdev updated https://github.com/llvm/llvm-project/pull/208969

>From 2bf04f40aebcb6e4e3511bb8d80051b95147f5e0 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Sun, 12 Jul 2026 00:52:59 +0200
Subject: [PATCH 1/3] [PowerPC][SDAG] fix bitcast on ppc_f128 swapping the two
 halves

This is a long-standing issue https://github.com/llvm/llvm-project/issues/44482.

The representation of ppc_fp128 is endian-agnostic in that its
high-order double is stored at the lower address.

For example, 27.0 is represented in memory as [0x403b000000000000, 0x0000000000000000].
Interpreting these 128 bit as an i128 on little-endian means the upper 64
bits are 0x0000000000000000, the lower are 0x403b000000000000.

But on big-endian the interpretation of the bytes is flipped, the upper bits are
now 0x403b000000000000 while the lower are 0x0000000000000000.
---
 .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 18 ++++++-
 .../PowerPC/ppcf128-constant-bitcast.ll       | 54 +++++++++++++++++++
 2 files changed, 71 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/CodeGen/PowerPC/ppcf128-constant-bitcast.ll

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 626803ed92a409..a7c8b38587932f 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -3363,7 +3363,23 @@ KnownBits SelectionDAG::computeKnownBits(SDValue Op, const APInt &DemandedElts,
 
   if (auto OptAPInt = Op->bitcastToAPInt()) {
     // We know all of the bits for a constant!
-    return KnownBits::makeConstant(*std::move(OptAPInt));
+    APInt V = *std::move(OptAPInt);
+
+    // Swap the low-order and high-order double of a ppc_fp128 when casting to
+    // i128, see #44482.
+    //
+    // A ppc_fp128 is two doubles, with the high-order double stored at the
+    // lower address. Reading that as an integer therefore puts the high-order
+    // double in the high 64 bits on big-endian targets and in the low 64 bits
+    // on little-endian targets.
+    //
+    // But APFloat::bitcastToAPInt is endianness-agnostic and always places the
+    // high-order double in the low 64 bits. Hence the two doubles must be
+    // flipped on big-endian targets.
+    if (getDataLayout().isBigEndian() && Op.getValueType() == MVT::ppcf128)
+      V = V.rotl(64);
+
+    return KnownBits::makeConstant(V);
   }
 
   if (Depth >= MaxRecursionDepth)
diff --git a/llvm/test/CodeGen/PowerPC/ppcf128-constant-bitcast.ll b/llvm/test/CodeGen/PowerPC/ppcf128-constant-bitcast.ll
new file mode 100644
index 00000000000000..6979c572c2013e
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/ppcf128-constant-bitcast.ll
@@ -0,0 +1,54 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -verify-machineinstrs -mtriple=powerpc64-unknown-linux-gnu < %s | FileCheck %s --check-prefix=BE
+; RUN: llc -verify-machineinstrs -mtriple=powerpc64le-unknown-linux-gnu < %s | FileCheck %s --check-prefix=LE
+
+; Regression test for #44482.
+;
+; A ppc_fp128 is two doubles, with the high-order double stored at the
+; lower address. Reading that as an integer therefore puts the high-order
+; double in the high 64 bits on big-endian targets and in the low 64 bits
+; on little-endian targets.
+;
+; But APFloat::bitcastToAPInt is endianness-agnostic and always places the
+; high-order double in the low 64 bits. Hence the two doubles must be
+; flipped on big-endian targets.
+
+; 27.0 is represented in memory as [0x403b000000000000, 0x0000000000000000].
+; Interpreting these 128 bit as an i128 on little-endian means the upper 64
+; bits are 0x0000000000000000, the lower are 0x403b000000000000.
+;
+; But on big-endian the interpretation of the bytes is flipped, and now the
+; upper bits are now 0x403b000000000000 while the lower are 0x0000000000000000.
+
+define i64 @extract_high64() {
+; BE-LABEL: extract_high64:
+; BE:       # %bb.0:
+; BE-NEXT:    li 3, 16443
+; BE-NEXT:    rldic 3, 3, 48, 1
+; BE-NEXT:    blr
+;
+; LE-LABEL: extract_high64:
+; LE:       # %bb.0:
+; LE-NEXT:    li 3, 0
+; LE-NEXT:    blr
+  %cast = bitcast ppc_fp128 27.0 to i128
+  %shift = lshr i128 %cast, 64
+  %trunc = trunc i128 %shift to i64
+  ret i64 %trunc
+}
+
+define i64 @extract_low64() {
+; BE-LABEL: extract_low64:
+; BE:       # %bb.0:
+; BE-NEXT:    li 3, 0
+; BE-NEXT:    blr
+;
+; LE-LABEL: extract_low64:
+; LE:       # %bb.0:
+; LE-NEXT:    li 3, 16443
+; LE-NEXT:    rldic 3, 3, 48, 1
+; LE-NEXT:    blr
+  %cast = bitcast ppc_fp128 27.0 to i128
+  %trunc = trunc i128 %cast to i64
+  ret i64 %trunc
+}

>From 8003a1b17dc2690c9812a6a51ee2adc295d84bd2 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <flokkievids at gmail.com>
Date: Sat, 3 Oct 2026 12:45:32 +0200
Subject: [PATCH 2/3] Apply batched suggestions from code review

Co-authored-by: Amy Kwan <amy.kwan1 at ibm.com>
---
 llvm/test/CodeGen/PowerPC/ppcf128-constant-bitcast.ll | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/test/CodeGen/PowerPC/ppcf128-constant-bitcast.ll b/llvm/test/CodeGen/PowerPC/ppcf128-constant-bitcast.ll
index 6979c572c2013e..680ffa5d1682f3 100644
--- a/llvm/test/CodeGen/PowerPC/ppcf128-constant-bitcast.ll
+++ b/llvm/test/CodeGen/PowerPC/ppcf128-constant-bitcast.ll
@@ -14,10 +14,10 @@
 ; flipped on big-endian targets.
 
 ; 27.0 is represented in memory as [0x403b000000000000, 0x0000000000000000].
-; Interpreting these 128 bit as an i128 on little-endian means the upper 64
-; bits are 0x0000000000000000, the lower are 0x403b000000000000.
+; Interpreting these 128 bits as an i128 on little-endian means the upper 64
+; bits are 0x0000000000000000, the lower 64 bits are 0x403b000000000000.
 ;
-; But on big-endian the interpretation of the bytes is flipped, and now the
+; But on big-endian the interpretation of the bytes is flipped, and the
 ; upper bits are now 0x403b000000000000 while the lower are 0x0000000000000000.
 
 define i64 @extract_high64() {

>From 2936ca06d242bb1afbbb424d648c9fb9e8a34257 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Sat, 3 Oct 2026 13:40:02 +0200
Subject: [PATCH 3/3] add nonzero tests

---
 .../PowerPC/ppcf128-constant-bitcast.ll       | 35 +++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/llvm/test/CodeGen/PowerPC/ppcf128-constant-bitcast.ll b/llvm/test/CodeGen/PowerPC/ppcf128-constant-bitcast.ll
index 680ffa5d1682f3..692baece364230 100644
--- a/llvm/test/CodeGen/PowerPC/ppcf128-constant-bitcast.ll
+++ b/llvm/test/CodeGen/PowerPC/ppcf128-constant-bitcast.ll
@@ -52,3 +52,38 @@ define i64 @extract_low64() {
   %trunc = trunc i128 %cast to i64
   ret i64 %trunc
 }
+
+define i64 @extract_high64_both_nonzero() {
+; BE-LABEL: extract_high64_both_nonzero:
+; BE:       # %bb.0:
+; BE-NEXT:    li 3, 1023
+; BE-NEXT:    rldic 3, 3, 52, 2
+; BE-NEXT:    blr
+;
+; LE-LABEL: extract_high64_both_nonzero:
+; LE:       # %bb.0:
+; LE-NEXT:    li 3, 963
+; LE-NEXT:    rldic 3, 3, 52, 2
+; LE-NEXT:    blr
+  %cast = bitcast ppc_fp128 0xM3FF00000000000003C30000000000000 to i128
+  %shift = lshr i128 %cast, 64
+  %trunc = trunc i128 %shift to i64
+  ret i64 %trunc
+}
+
+define i64 @extract_low64_both_nonzero() {
+; BE-LABEL: extract_low64_both_nonzero:
+; BE:       # %bb.0:
+; BE-NEXT:    li 3, 963
+; BE-NEXT:    rldic 3, 3, 52, 2
+; BE-NEXT:    blr
+;
+; LE-LABEL: extract_low64_both_nonzero:
+; LE:       # %bb.0:
+; LE-NEXT:    li 3, 1023
+; LE-NEXT:    rldic 3, 3, 52, 2
+; LE-NEXT:    blr
+  %cast = bitcast ppc_fp128 0xM3FF00000000000003C30000000000000 to i128
+  %trunc = trunc i128 %cast to i64
+  ret i64 %trunc
+}



More information about the llvm-commits mailing list