[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