https://github.com/benrichard-amd created 
https://github.com/llvm/llvm-project/pull/208542

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 <[email protected]>

>From 382f91374b4e4b14e7ad8c850839343eb319de12 Mon Sep 17 00:00:00 2001
From: benrichard-amd <[email protected]>
Date: Tue, 7 Jul 2026 19:39:34 +0000
Subject: [PATCH] [PowerPC] Enable _Float16 via software promotion to float32

Enable _Float16 on PowerPC to support machine learning and scientific
workloads that rely on half-precision arithmetic, bringing PowerPC in
line with AArch64 and RISC-V which already support this type.

_Float16 is now accepted on PowerPC Linux targets (powerpc, powerpc64,
powerpc64le) that have hardware floating-point. Arithmetic is performed
in float32 and converted back, matching the approach used by AArch64
and RISC-V.

Clang front-end (PPC.h):
- Set HasFloat16=true and HasFastHalfType=false in the PPCTargetInfo
  constructor so the defaults are correct before any feature processing.
  AIX, soft-float, and SPE targets clear this flag in
  handleTargetFeatures().

LLVM backend (PPCISelLowering):
- On ISA 3.0 (Power9), mark FP16_TO_FP and FP_TO_FP16 as Legal for
  f32 and f64. This activates the existing XSCVHPDP/XSCVDPHP .td
  patterns for register-to-register f16<->fp conversions. Load/store
  with LXSIHZX/STXSIHX was already wired up.
- On sub-P9, TypeSoftPromoteHalf handles all f16 operations via
  __extendhfsf2/__truncsfhf2 libcalls; lhz/sth for memory accesses.

Tests:
- clang/test/Sema/Float16.c, clang/test/SemaCXX/Float16.cpp: add
  powerpc and powerpc64le triples to the -DHAVE passing list.
- clang/test/CodeGen/PowerPC/Float16.c: verify the Clang frontend
  emits 'half' IR type for _Float16 arithmetic on ppc64le.
- llvm/test/CodeGen/PowerPC/float16-soft-promote.ll: CodeGen test
  covering load/store, arithmetic, extend, truncate, extending load,
  truncating store, comparison, and sitofp for P8 and P9.

Assisted-by: Claude <[email protected]>
---
 clang/lib/Basic/Targets/PPC.cpp               |   6 +
 clang/lib/Basic/Targets/PPC.h                 |   6 +
 clang/test/CodeGen/PowerPC/Float16.c          |  45 +++
 clang/test/Sema/Float16.c                     |   2 +
 clang/test/SemaCXX/Float16.cpp                |   2 +
 llvm/lib/Target/PowerPC/PPCISelLowering.cpp   |   7 +
 .../CodeGen/PowerPC/float16-soft-promote.ll   | 274 ++++++++++++++++++
 7 files changed, 342 insertions(+)
 create mode 100644 clang/test/CodeGen/PowerPC/Float16.c
 create mode 100644 llvm/test/CodeGen/PowerPC/float16-soft-promote.ll

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
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to