[clang] [llvm] [PowerPC] Enable _Float16 (PR #208542)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 9 12:48:22 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-powerpc
Author: Ben Richard (benrichard-amd)
<details>
<summary>Changes</summary>
Hi,
This PR enables `_Float16` on PowerPC, bringing PowerPC in line with AArch64 and RISC-V which already support this type. On Power9 and later (e.g. `-mcpu=power9`) it will generate `XSCVHPDP`/`XSCVDPHP` instructions for f16<->f32 conversions.
Unit tests included.
Assisted-by: Claude <noreply@<!-- -->anthropic.com>
---
Full diff: https://github.com/llvm/llvm-project/pull/208542.diff
7 Files Affected:
- (modified) clang/lib/Basic/Targets/PPC.cpp (+6)
- (modified) clang/lib/Basic/Targets/PPC.h (+6)
- (added) clang/test/CodeGen/PowerPC/Float16.c (+45)
- (modified) clang/test/Sema/Float16.c (+2)
- (modified) clang/test/SemaCXX/Float16.cpp (+2)
- (modified) llvm/lib/Target/PowerPC/PPCISelLowering.cpp (+7)
- (added) llvm/test/CodeGen/PowerPC/float16-soft-promote.ll (+274)
``````````diff
diff --git a/clang/lib/Basic/Targets/PPC.cpp b/clang/lib/Basic/Targets/PPC.cpp
index c9a41df806aff..5d92c7d222e72 100644
--- a/clang/lib/Basic/Targets/PPC.cpp
+++ b/clang/lib/Basic/Targets/PPC.cpp
@@ -87,6 +87,12 @@ bool PPCTargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
// all.
}
+ // _Float16 is enabled by default in the constructor. Disable it on AIX
+ // (ABI not yet defined), soft-float, and SPE targets.
+ if (getTriple().isOSAIX() || FloatABI == SoftFloat || HasSPE) {
+ HasFloat16 = false;
+ }
+
return true;
}
diff --git a/clang/lib/Basic/Targets/PPC.h b/clang/lib/Basic/Targets/PPC.h
index e3bf5072d932d..2c3f6a116b8f9 100644
--- a/clang/lib/Basic/Targets/PPC.h
+++ b/clang/lib/Basic/Targets/PPC.h
@@ -86,6 +86,12 @@ class LLVM_LIBRARY_VISIBILITY PPCTargetInfo : public TargetInfo {
HasStrictFP = true;
HasIbm128 = true;
HasUnalignedAccess = true;
+ // _Float16 is supported on all PowerPC Linux targets via software
+ // promotion to float32, matching the approach used by AArch64 and RISC-V.
+ // AIX, soft-float, and SPE targets clear this flag in
+ // handleTargetFeatures() since their ABI has not yet been defined.
+ HasFloat16 = true;
+ HasFastHalfType = false;
}
// Set the language option for altivec based on our value.
diff --git a/clang/test/CodeGen/PowerPC/Float16.c b/clang/test/CodeGen/PowerPC/Float16.c
new file mode 100644
index 0000000000000..0db3d1378d149
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/Float16.c
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -triple powerpc64le-linux-gnu -emit-llvm -o - %s \
+// RUN: | FileCheck %s --check-prefix=PPC64LE
+// RUN: %clang_cc1 -triple powerpc-linux-gnu -emit-llvm -o - %s \
+// RUN: | FileCheck %s --check-prefix=PPC32
+// RUN: %clang_cc1 -triple powerpc64le-linux-gnu -target-cpu pwr9 -emit-llvm -o - %s \
+// RUN: | FileCheck %s --check-prefix=PPC64LE
+//
+// Test that _Float16 is accepted on PowerPC targets and that the Clang
+// frontend emits the expected 'half' IR type for _Float16 arithmetic.
+
+// _Float16 must be accepted (no "not supported on this target" error).
+_Float16 global_h = 1.0f16;
+
+// PPC64LE: @global_h = global half
+// PPC32: @global_h = global half
+
+// Function signatures use 'half' type.
+_Float16 add(_Float16 a, _Float16 b) {
+ return a + b;
+// PPC64LE-LABEL: define {{.*}} half @add(half noundef %a, half noundef %b)
+// PPC32-LABEL: define {{.*}} half @add(half noundef %a, half noundef %b)
+}
+
+_Float16 mul(_Float16 a, _Float16 b) {
+ return a * b;
+// PPC64LE-LABEL: define {{.*}} half @mul(half noundef %a, half noundef %b)
+// PPC32-LABEL: define {{.*}} half @mul(half noundef %a, half noundef %b)
+}
+
+// Extend/truncate round-trips.
+float to_float(_Float16 a) {
+ return (float)a;
+// PPC64LE: fpext half {{.*}} to float
+// PPC32: fpext half {{.*}} to float
+}
+
+_Float16 from_float(float a) {
+ return (_Float16)a;
+// PPC64LE: fptrunc float {{.*}} to half
+// PPC32: fptrunc float {{.*}} to half
+}
+
+// sizeof must be 2 and _Alignof must be 2.
+_Static_assert(sizeof(_Float16) == 2, "sizeof(_Float16) != 2");
+_Static_assert(_Alignof(_Float16) == 2, "_Alignof(_Float16) != 2");
diff --git a/clang/test/Sema/Float16.c b/clang/test/Sema/Float16.c
index b104cf907b3e9..978ac69371ad1 100644
--- a/clang/test/Sema/Float16.c
+++ b/clang/test/Sema/Float16.c
@@ -6,6 +6,8 @@
// RUN: %clang_cc1 -fsyntax-only -verify -triple aarch64-linux-gnu %s -DHAVE
// RUN: %clang_cc1 -fsyntax-only -verify -triple riscv32 %s -DHAVE
// RUN: %clang_cc1 -fsyntax-only -verify -triple riscv64 %s -DHAVE
+// RUN: %clang_cc1 -fsyntax-only -verify -triple powerpc-linux-gnu %s -DHAVE
+// RUN: %clang_cc1 -fsyntax-only -verify -triple powerpc64le-linux-gnu %s -DHAVE
// RUN: %clang_cc1 -fsyntax-only -verify -triple s390x-ibm-zos %s
#ifndef HAVE
diff --git a/clang/test/SemaCXX/Float16.cpp b/clang/test/SemaCXX/Float16.cpp
index 9646a8d0c317f..c6e365c252094 100644
--- a/clang/test/SemaCXX/Float16.cpp
+++ b/clang/test/SemaCXX/Float16.cpp
@@ -4,6 +4,8 @@
// RUN: %clang_cc1 -fsyntax-only -verify -triple spir-unknown-unknown %s -DHAVE
// RUN: %clang_cc1 -fsyntax-only -verify -triple armv7a-linux-gnu %s -DHAVE
// RUN: %clang_cc1 -fsyntax-only -verify -triple aarch64-linux-gnu %s -DHAVE
+// RUN: %clang_cc1 -fsyntax-only -verify -triple powerpc-linux-gnu %s -DHAVE
+// RUN: %clang_cc1 -fsyntax-only -verify -triple powerpc64le-linux-gnu %s -DHAVE
// RUN: %clang_cc1 -fsyntax-only -verify -triple s390x-ibm-zos %s
#ifdef HAVE
// expected-no-diagnostics
diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
index f6c844b6428ce..2c3d51c34bdf9 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
@@ -247,6 +247,13 @@ PPCTargetLowering::PPCTargetLowering(const PPCTargetMachine &TM,
setLoadExtAction(ISD::EXTLOAD, MVT::f32, MVT::f16, Legal);
setTruncStoreAction(MVT::f64, MVT::f16, Legal);
setTruncStoreAction(MVT::f32, MVT::f16, Legal);
+ // ISA 3.0 (Power9) has XSCVHPDP/XSCVDPHP for scalar HP<->DP conversion.
+ // Mark FP16_TO_FP / FP_TO_FP16 as Legal so the soft-promote machinery
+ // uses hardware instead of __extendhfsf2/__truncsfhf2 libcalls.
+ setOperationAction(ISD::FP16_TO_FP, MVT::f64, Legal);
+ setOperationAction(ISD::FP16_TO_FP, MVT::f32, Legal);
+ setOperationAction(ISD::FP_TO_FP16, MVT::f64, Legal);
+ setOperationAction(ISD::FP_TO_FP16, MVT::f32, Legal);
} else {
// No extending loads from f16 or HW conversions back and forth.
setLoadExtAction(ISD::EXTLOAD, MVT::f128, MVT::f16, Expand);
diff --git a/llvm/test/CodeGen/PowerPC/float16-soft-promote.ll b/llvm/test/CodeGen/PowerPC/float16-soft-promote.ll
new file mode 100644
index 0000000000000..3cff5968c7723
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/float16-soft-promote.ll
@@ -0,0 +1,274 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; Tests for _Float16 (half) soft-promotion on PowerPC.
+;
+; Sub-P9 (P8): all f16 arithmetic is promoted to f32 via __extendhfsf2 /
+; __truncsfhf2 libcalls. Loads and stores use integer lhz/sth.
+;
+; P9 (ISA 3.0): load/store use LXSIHZX+XSCVHPDP / XSCVDPHP+STXSIHX.
+; Register-to-register FP16_TO_FP / FP_TO_FP16 also use hardware.
+
+; RUN: llc -mcpu=pwr8 -mtriple=powerpc64le-unknown-unknown \
+; RUN: -verify-machineinstrs -ppc-asm-full-reg-names < %s | FileCheck %s \
+; RUN: --check-prefixes=CHECK,P8
+; RUN: llc -mcpu=pwr9 -mtriple=powerpc64le-unknown-unknown \
+; RUN: -verify-machineinstrs -ppc-asm-full-reg-names < %s | FileCheck %s \
+; RUN: --check-prefixes=CHECK,P9
+
+; ---------------------------------------------------------------------------
+; Load / store (half is carried as i16 in memory on both P8 and P9)
+; ---------------------------------------------------------------------------
+
+define void @store_f16(half %x, ptr %p) nounwind {
+; CHECK-LABEL: store_f16:
+; CHECK: # %bb.0:
+; CHECK-NEXT: sth r3, 0(r4)
+; CHECK-NEXT: blr
+ store half %x, ptr %p
+ ret void
+}
+
+define half @load_f16(ptr %p) nounwind {
+; CHECK-LABEL: load_f16:
+; CHECK: # %bb.0:
+; CHECK-NEXT: lhz r3, 0(r3)
+; CHECK-NEXT: blr
+ %v = load half, ptr %p
+ ret half %v
+}
+
+; ---------------------------------------------------------------------------
+; Arithmetic: addition (soft-promote to f32 on P8, same on P9)
+; ---------------------------------------------------------------------------
+
+define half @add_f16(half %a, half %b) nounwind {
+; P8-LABEL: add_f16:
+; P8: # %bb.0:
+; P8-NEXT: mflr r0
+; P8-NEXT: std r30, -24(r1) # 8-byte Folded Spill
+; P8-NEXT: stfd f31, -8(r1) # 8-byte Folded Spill
+; P8-NEXT: stdu r1, -64(r1)
+; P8-NEXT: mr r30, r3
+; P8-NEXT: clrldi r3, r4, 48
+; P8-NEXT: std r0, 80(r1)
+; P8-NEXT: bl __extendhfsf2
+; P8-NEXT: nop
+; P8-NEXT: clrldi r3, r30, 48
+; P8-NEXT: fmr f31, f1
+; P8-NEXT: bl __extendhfsf2
+; P8-NEXT: nop
+; P8-NEXT: xsaddsp f1, f1, f31
+; P8-NEXT: bl __truncsfhf2
+; P8-NEXT: nop
+; P8-NEXT: addi r1, r1, 64
+; P8-NEXT: ld r0, 16(r1)
+; P8-NEXT: lfd f31, -8(r1) # 8-byte Folded Reload
+; P8-NEXT: ld r30, -24(r1) # 8-byte Folded Reload
+; P8-NEXT: mtlr r0
+; P8-NEXT: blr
+;
+; P9-LABEL: add_f16:
+; P9: # %bb.0:
+; P9-NEXT: clrlwi r3, r3, 16
+; P9-NEXT: clrlwi r4, r4, 16
+; P9-NEXT: mtfprwz f0, r4
+; P9-NEXT: mtfprwz f1, r3
+; P9-NEXT: xscvhpdp f0, f0
+; P9-NEXT: xscvhpdp f1, f1
+; P9-NEXT: xsaddsp f0, f1, f0
+; P9-NEXT: xscvdphp f0, f0
+; P9-NEXT: mffprwz r3, f0
+; P9-NEXT: blr
+ %r = fadd half %a, %b
+ ret half %r
+}
+
+; ---------------------------------------------------------------------------
+; Extend f16 -> f32 (soft: libcall on P8, hardware XSCVHPDP on P9)
+; ---------------------------------------------------------------------------
+
+define float @extend_f16_to_f32(half %a) nounwind {
+; P8-LABEL: extend_f16_to_f32:
+; P8: # %bb.0:
+; P8-NEXT: mflr r0
+; P8-NEXT: stdu r1, -32(r1)
+; P8-NEXT: clrldi r3, r3, 48
+; P8-NEXT: std r0, 48(r1)
+; P8-NEXT: bl __extendhfsf2
+; P8-NEXT: nop
+; P8-NEXT: addi r1, r1, 32
+; P8-NEXT: ld r0, 16(r1)
+; P8-NEXT: mtlr r0
+; P8-NEXT: blr
+;
+; P9-LABEL: extend_f16_to_f32:
+; P9: # %bb.0:
+; P9-NEXT: clrlwi r3, r3, 16
+; P9-NEXT: mtfprwz f0, r3
+; P9-NEXT: xscvhpdp f1, f0
+; P9-NEXT: blr
+ %r = fpext half %a to float
+ ret float %r
+}
+
+; ---------------------------------------------------------------------------
+; Truncate f32 -> f16 (soft: libcall on P8, hardware XSCVDPHP on P9)
+; ---------------------------------------------------------------------------
+
+define half @trunc_f32_to_f16(float %a) nounwind {
+; P8-LABEL: trunc_f32_to_f16:
+; P8: # %bb.0:
+; P8-NEXT: mflr r0
+; P8-NEXT: stdu r1, -32(r1)
+; P8-NEXT: std r0, 48(r1)
+; P8-NEXT: bl __truncsfhf2
+; P8-NEXT: nop
+; P8-NEXT: addi r1, r1, 32
+; P8-NEXT: ld r0, 16(r1)
+; P8-NEXT: mtlr r0
+; P8-NEXT: blr
+;
+; P9-LABEL: trunc_f32_to_f16:
+; P9: # %bb.0:
+; P9-NEXT: xscvdphp f0, f1
+; P9-NEXT: mffprwz r3, f0
+; P9-NEXT: blr
+ %r = fptrunc float %a to half
+ ret half %r
+}
+
+; ---------------------------------------------------------------------------
+; Extend load: f16 mem -> f64 (LXSIHZX+XSCVHPDP on P9)
+; ---------------------------------------------------------------------------
+
+define double @extload_f16_to_f64(ptr %p) nounwind {
+; P8-LABEL: extload_f16_to_f64:
+; P8: # %bb.0:
+; P8-NEXT: mflr r0
+; P8-NEXT: stdu r1, -32(r1)
+; P8-NEXT: std r0, 48(r1)
+; P8-NEXT: lhz r3, 0(r3)
+; P8-NEXT: bl __extendhfsf2
+; P8-NEXT: nop
+; P8-NEXT: addi r1, r1, 32
+; P8-NEXT: ld r0, 16(r1)
+; P8-NEXT: mtlr r0
+; P8-NEXT: blr
+;
+; P9-LABEL: extload_f16_to_f64:
+; P9: # %bb.0:
+; P9-NEXT: lxsihzx f0, 0, r3
+; P9-NEXT: xscvhpdp f1, f0
+; P9-NEXT: blr
+ %v = load half, ptr %p
+ %r = fpext half %v to double
+ ret double %r
+}
+
+; ---------------------------------------------------------------------------
+; Truncate store: f64 -> f16 mem (XSCVDPHP+STXSIHX on P9)
+; ---------------------------------------------------------------------------
+
+define void @truncstore_f64_to_f16(double %v, ptr %p) nounwind {
+; P8-LABEL: truncstore_f64_to_f16:
+; P8: # %bb.0:
+; P8-NEXT: mflr r0
+; P8-NEXT: std r30, -16(r1) # 8-byte Folded Spill
+; P8-NEXT: stdu r1, -48(r1)
+; P8-NEXT: std r0, 64(r1)
+; P8-NEXT: mr r30, r4
+; P8-NEXT: bl __truncdfhf2
+; P8-NEXT: nop
+; P8-NEXT: sth r3, 0(r30)
+; P8-NEXT: addi r1, r1, 48
+; P8-NEXT: ld r0, 16(r1)
+; P8-NEXT: ld r30, -16(r1) # 8-byte Folded Reload
+; P8-NEXT: mtlr r0
+; P8-NEXT: blr
+;
+; P9-LABEL: truncstore_f64_to_f16:
+; P9: # %bb.0:
+; P9-NEXT: xscvdphp f0, f1
+; P9-NEXT: stxsihx f0, 0, r4
+; P9-NEXT: blr
+ %t = fptrunc double %v to half
+ store half %t, ptr %p
+ ret void
+}
+
+; ---------------------------------------------------------------------------
+; Comparison: result is i1, operands promoted to f32 first
+; ---------------------------------------------------------------------------
+
+define i1 @cmp_f16(half %a, half %b) nounwind {
+; P8-LABEL: cmp_f16:
+; P8: # %bb.0:
+; P8-NEXT: mflr r0
+; P8-NEXT: std r30, -24(r1) # 8-byte Folded Spill
+; P8-NEXT: stfd f31, -8(r1) # 8-byte Folded Spill
+; P8-NEXT: stdu r1, -64(r1)
+; P8-NEXT: mr r30, r3
+; P8-NEXT: clrldi r3, r4, 48
+; P8-NEXT: std r0, 80(r1)
+; P8-NEXT: bl __extendhfsf2
+; P8-NEXT: nop
+; P8-NEXT: clrldi r3, r30, 48
+; P8-NEXT: fmr f31, f1
+; P8-NEXT: bl __extendhfsf2
+; P8-NEXT: nop
+; P8-NEXT: fcmpu cr0, f1, f31
+; P8-NEXT: li r3, 0
+; P8-NEXT: li r4, 1
+; P8-NEXT: isellt r3, r4, r3
+; P8-NEXT: addi r1, r1, 64
+; P8-NEXT: ld r0, 16(r1)
+; P8-NEXT: lfd f31, -8(r1) # 8-byte Folded Reload
+; P8-NEXT: ld r30, -24(r1) # 8-byte Folded Reload
+; P8-NEXT: mtlr r0
+; P8-NEXT: blr
+;
+; P9-LABEL: cmp_f16:
+; P9: # %bb.0:
+; P9-NEXT: clrlwi r3, r3, 16
+; P9-NEXT: clrlwi r4, r4, 16
+; P9-NEXT: mtfprwz f0, r4
+; P9-NEXT: mtfprwz f1, r3
+; P9-NEXT: li r3, 0
+; P9-NEXT: li r4, 1
+; P9-NEXT: xscvhpdp f0, f0
+; P9-NEXT: xscvhpdp f1, f1
+; P9-NEXT: fcmpu cr0, f1, f0
+; P9-NEXT: isellt r3, r4, r3
+; P9-NEXT: blr
+ %r = fcmp olt half %a, %b
+ ret i1 %r
+}
+
+; ---------------------------------------------------------------------------
+; Integer to f16 conversion
+; ---------------------------------------------------------------------------
+
+define half @sitofp_i32_to_f16(i32 %a) nounwind {
+; P8-LABEL: sitofp_i32_to_f16:
+; P8: # %bb.0:
+; P8-NEXT: mflr r0
+; P8-NEXT: stdu r1, -32(r1)
+; P8-NEXT: mtfprwa f0, r3
+; P8-NEXT: std r0, 48(r1)
+; P8-NEXT: xscvsxdsp f1, f0
+; P8-NEXT: bl __truncsfhf2
+; P8-NEXT: nop
+; P8-NEXT: addi r1, r1, 32
+; P8-NEXT: ld r0, 16(r1)
+; P8-NEXT: mtlr r0
+; P8-NEXT: blr
+;
+; P9-LABEL: sitofp_i32_to_f16:
+; P9: # %bb.0:
+; P9-NEXT: mtfprwa f0, r3
+; P9-NEXT: xscvsxdsp f0, f0
+; P9-NEXT: xscvdphp f0, f0
+; P9-NEXT: mffprwz r3, f0
+; P9-NEXT: blr
+ %r = sitofp i32 %a to half
+ ret half %r
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/208542
More information about the cfe-commits
mailing list