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
