https://github.com/benrichard-amd created https://github.com/llvm/llvm-project/pull/208598
Hi, This PR enables `__bf16` on PowerPC via software promotion to float32, as it does not have native `bf16` support. Tests included. This change was assisted by Claude. >From 97997eb3ee2cd505d7dd2bc39b290a353b630d52 Mon Sep 17 00:00:00 2001 From: benrichard-amd <[email protected]> Date: Tue, 7 Jul 2026 20:41:59 +0000 Subject: [PATCH] [PowerPC] Enable __bf16 via software promotion to float32 Enable __bf16 on PowerPC to support machine learning and scientific workloads that rely on bfloat16 arithmetic, bringing PowerPC in line with x86 which already supports this type via software promotion. __bf16 is now accepted on PowerPC Linux targets (powerpc, powerpc64, powerpc64le) that have hardware floating-point, using the same soft-promote approach as _Float16 and matching x86 behaviour without native bf16 support. Clang front-end (PPC.h): - Initialize BFloat16Width, BFloat16Align, and BFloat16Format in the PPCTargetInfo constructor so the bfloat APFloat format is correctly reported to Clang. - Set HasBFloat16=true in the constructor, with AIX/soft-float/SPE targets clearing it in handleTargetFeatures(). LLVM backend (PPCISelLowering): - Mark all extending loads and truncating stores for MVT::bf16 as Expand. This directs TypeSoftPromoteHalf to use integer lhz/sth for bf16 memory accesses. - Mark BF16_TO_FP and FP_TO_BF16 as Expand for f32/f64/f128 so that LegalizeDAG expands them to the shift+bitcast / __truncsfbf2 libcall sequences before ISel. Without this they default to Legal and reach ISel with no matching .td pattern, causing a "Cannot select" crash. Tests: - clang/test/CodeGen/PowerPC/bfloat16.c: verify the Clang frontend accepts __bf16 and has correct sizeof/alignof. - llvm/test/CodeGen/PowerPC/bfloat16-soft-promote.ll: verify backend instruction selection for load/store, extend, truncate, arithmetic, comparison, and sitofp for P8 and P10. Assisted-by: Claude <[email protected]> --- clang/lib/Basic/Targets/PPC.cpp | 6 + clang/lib/Basic/Targets/PPC.h | 3 + clang/test/CodeGen/PowerPC/bfloat16.c | 45 +++++ llvm/lib/Target/PowerPC/PPCISelLowering.cpp | 16 ++ .../CodeGen/PowerPC/bfloat16-soft-promote.ll | 180 ++++++++++++++++++ 5 files changed, 250 insertions(+) create mode 100644 clang/test/CodeGen/PowerPC/bfloat16.c create mode 100644 llvm/test/CodeGen/PowerPC/bfloat16-soft-promote.ll diff --git a/clang/lib/Basic/Targets/PPC.cpp b/clang/lib/Basic/Targets/PPC.cpp index c9a41df806aff..c1076f699d7e8 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. } + // __bf16 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) { + HasBFloat16 = false; + } + return true; } diff --git a/clang/lib/Basic/Targets/PPC.h b/clang/lib/Basic/Targets/PPC.h index e3bf5072d932d..7ffe312d8ab16 100644 --- a/clang/lib/Basic/Targets/PPC.h +++ b/clang/lib/Basic/Targets/PPC.h @@ -83,9 +83,12 @@ class LLVM_LIBRARY_VISIBILITY PPCTargetInfo : public TargetInfo { SuitableAlign = 128; LongDoubleWidth = LongDoubleAlign = 128; LongDoubleFormat = &llvm::APFloat::PPCDoubleDouble(); + BFloat16Width = BFloat16Align = 16; + BFloat16Format = &llvm::APFloat::BFloat(); HasStrictFP = true; HasIbm128 = true; HasUnalignedAccess = true; + HasBFloat16 = true; } // Set the language option for altivec based on our value. diff --git a/clang/test/CodeGen/PowerPC/bfloat16.c b/clang/test/CodeGen/PowerPC/bfloat16.c new file mode 100644 index 0000000000000..47d3ed2e87dbc --- /dev/null +++ b/clang/test/CodeGen/PowerPC/bfloat16.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 pwr10 -emit-llvm -o - %s \ +// RUN: | FileCheck %s --check-prefix=PPC64LE +// +// Test that __bf16 is accepted on PowerPC targets and that the Clang +// frontend emits the expected bfloat IR type. + +// __bf16 must be accepted (no "not supported on this target" error). +__bf16 global_bf; + +// PPC64LE: @global_bf = global bfloat +// PPC32: @global_bf = global bfloat + +// Function signatures use bfloat type. +__bf16 add(__bf16 a, __bf16 b) { + return a + b; +// PPC64LE-LABEL: define {{.*}} bfloat @add(bfloat noundef %a, bfloat noundef %b) +// PPC32-LABEL: define {{.*}} bfloat @add(bfloat noundef %a, bfloat noundef %b) +} + +__bf16 mul(__bf16 a, __bf16 b) { + return a * b; +// PPC64LE-LABEL: define {{.*}} bfloat @mul(bfloat noundef %a, bfloat noundef %b) +// PPC32-LABEL: define {{.*}} bfloat @mul(bfloat noundef %a, bfloat noundef %b) +} + +// Extend/truncate round-trips. +float to_float(__bf16 a) { + return (float)a; +// PPC64LE: fpext bfloat {{.*}} to float +// PPC32: fpext bfloat {{.*}} to float +} + +__bf16 from_float(float a) { + return (__bf16)a; +// PPC64LE: fptrunc float {{.*}} to bfloat +// PPC32: fptrunc float {{.*}} to bfloat +} + +// sizeof and alignof must both be 2. +_Static_assert(sizeof(__bf16) == 2, "sizeof(__bf16) != 2"); +_Static_assert(_Alignof(__bf16) == 2, "_Alignof(__bf16) != 2"); diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp index f6c844b6428ce..9b49c0d150e37 100644 --- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp +++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp @@ -241,6 +241,22 @@ PPCTargetLowering::PPCTargetLowering(const PPCTargetMachine &TM, setTruncStoreAction(MVT::f128, MVT::f16, Expand); setOperationAction(ISD::FP_TO_FP16, MVT::f128, Expand); + // bf16 is soft-promoted to f32 on all PowerPC targets. + // Loads/stores use the integer i16 path (lhz/sth) via TypeSoftPromoteHalf. + // BF16_TO_FP (extend) is done inline via left-shift-16. + // FP_TO_BF16 (truncate) is done via the __truncsfbf2 libcall. + // + // BF16_TO_FP and FP_TO_BF16 must be explicitly marked Expand so that + // LegalizeDAG expands them to the shift+bitcast / libcall sequences before + // ISel. Without this they default to Legal and reach ISel with no matching + // .td pattern, causing a "Cannot select" fatal error. + for (MVT VT : {MVT::f32, MVT::f64, MVT::f128}) { + setLoadExtAction(ISD::EXTLOAD, VT, MVT::bf16, Expand); + setTruncStoreAction(VT, MVT::bf16, Expand); + setOperationAction(ISD::BF16_TO_FP, VT, Expand); + setOperationAction(ISD::FP_TO_BF16, VT, Expand); + } + if (Subtarget.isISA3_0()) { setLoadExtAction(ISD::EXTLOAD, MVT::f128, MVT::f16, Legal); setLoadExtAction(ISD::EXTLOAD, MVT::f64, MVT::f16, Legal); diff --git a/llvm/test/CodeGen/PowerPC/bfloat16-soft-promote.ll b/llvm/test/CodeGen/PowerPC/bfloat16-soft-promote.ll new file mode 100644 index 0000000000000..0574ecf0d05a3 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/bfloat16-soft-promote.ll @@ -0,0 +1,180 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; Tests for __bf16 (bfloat) soft-promotion on PowerPC. +; +; All PowerPC targets soft-promote bf16 to f32: +; - Loads/stores use integer lhz/sth (i16 path). +; - BF16_TO_FP (extend) is done inline via a left-shift of 16 bits. +; - FP_TO_BF16 (truncate) calls __truncsfbf2. +; - Arithmetic is performed in f32 and the result truncated back to bf16. + +; 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=pwr10 -mtriple=powerpc64le-unknown-unknown \ +; RUN: -verify-machineinstrs -ppc-asm-full-reg-names < %s | FileCheck %s \ +; RUN: --check-prefixes=CHECK,P10 + +define void @store_bf16(bfloat %x, ptr %p) nounwind { +; CHECK-LABEL: store_bf16: +; CHECK: # %bb.0: +; CHECK-NEXT: xsmaxdp f0, f1, f1 +; CHECK-NEXT: xscvdpspn vs0, f0 +; CHECK-NEXT: mffprwz r3, f0 +; CHECK-NEXT: srwi r3, r3, 16 +; CHECK-NEXT: sth r3, 0(r4) +; CHECK-NEXT: blr + store bfloat %x, ptr %p + ret void +} + +define bfloat @load_bf16(ptr %p) nounwind { +; CHECK-LABEL: load_bf16: +; CHECK: # %bb.0: +; CHECK-NEXT: lhz r3, 0(r3) +; CHECK-NEXT: slwi r3, r3, 16 +; CHECK-NEXT: mtfprd f0, r3 +; CHECK-NEXT: xxsldwi vs0, vs0, vs0, 1 +; CHECK-NEXT: xscvspdpn f1, vs0 +; CHECK-NEXT: blr + %v = load bfloat, ptr %p + ret bfloat %v +} + +define float @extend_bf16_to_f32(bfloat %a) nounwind { +; CHECK-LABEL: extend_bf16_to_f32: +; CHECK: # %bb.0: +; CHECK-NEXT: blr + %r = fpext bfloat %a to float + ret float %r +} + +define bfloat @trunc_f32_to_bf16(float %a) nounwind { +; CHECK-LABEL: trunc_f32_to_bf16: +; CHECK: # %bb.0: +; CHECK-NEXT: xsmaxdp f0, f1, f1 +; CHECK-NEXT: xscvdpspn vs0, f0 +; CHECK-NEXT: mffprwz r3, f0 +; CHECK-NEXT: rlwinm r3, r3, 0, 0, 15 +; CHECK-NEXT: mtfprd f0, r3 +; CHECK-NEXT: xxsldwi vs0, vs0, vs0, 1 +; CHECK-NEXT: xscvspdpn f1, vs0 +; CHECK-NEXT: blr + %r = fptrunc float %a to bfloat + ret bfloat %r +} + +define bfloat @add_bf16(bfloat %a, bfloat %b) nounwind { +; P8-LABEL: add_bf16: +; P8: # %bb.0: +; P8-NEXT: xsmaxdp f0, f1, f1 +; P8-NEXT: xscvdpspn vs0, f0 +; P8-NEXT: mffprwz r3, f0 +; P8-NEXT: xsmaxdp f0, f2, f2 +; P8-NEXT: xscvdpspn vs0, f0 +; P8-NEXT: rlwinm r3, r3, 0, 0, 15 +; P8-NEXT: mtfprd f1, r3 +; P8-NEXT: mffprwz r4, f0 +; P8-NEXT: rlwinm r4, r4, 0, 0, 15 +; P8-NEXT: mtfprd f0, r4 +; P8-NEXT: xxsldwi vs1, vs1, vs1, 1 +; P8-NEXT: xscvspdpn f1, vs1 +; P8-NEXT: xxsldwi vs0, vs0, vs0, 1 +; P8-NEXT: xscvspdpn f0, vs0 +; P8-NEXT: xsaddsp f0, f1, f0 +; P8-NEXT: xscvdpspn vs0, f0 +; P8-NEXT: mffprwz r3, f0 +; P8-NEXT: rlwinm r3, r3, 0, 0, 15 +; P8-NEXT: mtfprd f0, r3 +; P8-NEXT: xxsldwi vs0, vs0, vs0, 1 +; P8-NEXT: xscvspdpn f1, vs0 +; P8-NEXT: blr +; +; P10-LABEL: add_bf16: +; P10: # %bb.0: +; P10-NEXT: xsmaxdp f0, f1, f1 +; P10-NEXT: xscvdpspn vs0, f0 +; P10-NEXT: mffprwz r3, f0 +; P10-NEXT: xsmaxdp f0, f2, f2 +; P10-NEXT: rlwinm r3, r3, 0, 0, 15 +; P10-NEXT: xscvdpspn vs0, f0 +; P10-NEXT: mtfprd f1, r3 +; P10-NEXT: xxsldwi vs1, vs1, vs1, 1 +; P10-NEXT: xscvspdpn f1, vs1 +; P10-NEXT: mffprwz r4, f0 +; P10-NEXT: rlwinm r4, r4, 0, 0, 15 +; P10-NEXT: mtfprd f0, r4 +; P10-NEXT: xxsldwi vs0, vs0, vs0, 1 +; P10-NEXT: xscvspdpn f0, vs0 +; P10-NEXT: xsaddsp f0, f1, f0 +; P10-NEXT: xscvdpspn vs0, f0 +; P10-NEXT: mffprwz r3, f0 +; P10-NEXT: rlwinm r3, r3, 0, 0, 15 +; P10-NEXT: mtfprd f0, r3 +; P10-NEXT: xxsldwi vs0, vs0, vs0, 1 +; P10-NEXT: xscvspdpn f1, vs0 +; P10-NEXT: blr + %r = fadd bfloat %a, %b + ret bfloat %r +} + +define i1 @cmp_bf16(bfloat %a, bfloat %b) nounwind { +; P8-LABEL: cmp_bf16: +; P8: # %bb.0: +; P8-NEXT: xsmaxdp f0, f1, f1 +; P8-NEXT: xscvdpspn vs0, f0 +; P8-NEXT: mffprwz r3, f0 +; P8-NEXT: xsmaxdp f0, f2, f2 +; P8-NEXT: xscvdpspn vs0, f0 +; P8-NEXT: rlwinm r3, r3, 0, 0, 15 +; P8-NEXT: mtfprd f1, r3 +; P8-NEXT: li r3, 0 +; P8-NEXT: mffprwz r4, f0 +; P8-NEXT: rlwinm r4, r4, 0, 0, 15 +; P8-NEXT: mtfprd f0, r4 +; P8-NEXT: li r4, 1 +; P8-NEXT: xxsldwi vs1, vs1, vs1, 1 +; P8-NEXT: xscvspdpn f1, vs1 +; P8-NEXT: xxsldwi vs0, vs0, vs0, 1 +; P8-NEXT: xscvspdpn f0, vs0 +; P8-NEXT: fcmpu cr0, f1, f0 +; P8-NEXT: isellt r3, r4, r3 +; P8-NEXT: blr +; +; P10-LABEL: cmp_bf16: +; P10: # %bb.0: +; P10-NEXT: xsmaxdp f0, f1, f1 +; P10-NEXT: xscvdpspn vs0, f0 +; P10-NEXT: mffprwz r3, f0 +; P10-NEXT: xsmaxdp f0, f2, f2 +; P10-NEXT: rlwinm r3, r3, 0, 0, 15 +; P10-NEXT: xscvdpspn vs0, f0 +; P10-NEXT: mtfprd f1, r3 +; P10-NEXT: xxsldwi vs1, vs1, vs1, 1 +; P10-NEXT: xscvspdpn f1, vs1 +; P10-NEXT: mffprwz r4, f0 +; P10-NEXT: rlwinm r4, r4, 0, 0, 15 +; P10-NEXT: mtfprd f0, r4 +; P10-NEXT: xxsldwi vs0, vs0, vs0, 1 +; P10-NEXT: xscvspdpn f0, vs0 +; P10-NEXT: fcmpu cr0, f1, f0 +; P10-NEXT: setbc r3, lt +; P10-NEXT: blr + %r = fcmp olt bfloat %a, %b + ret i1 %r +} + +define bfloat @sitofp_i32_to_bf16(i32 %a) nounwind { +; CHECK-LABEL: sitofp_i32_to_bf16: +; CHECK: # %bb.0: +; CHECK-NEXT: mtfprwa f0, r3 +; CHECK-NEXT: xscvsxdsp f0, f0 +; CHECK-NEXT: xscvdpspn vs0, f0 +; CHECK-NEXT: mffprwz r3, f0 +; CHECK-NEXT: rlwinm r3, r3, 0, 0, 15 +; CHECK-NEXT: mtfprd f0, r3 +; CHECK-NEXT: xxsldwi vs0, vs0, vs0, 1 +; CHECK-NEXT: xscvspdpn f1, vs0 +; CHECK-NEXT: blr + %r = sitofp i32 %a to bfloat + ret bfloat %r +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
