[clang] [llvm] [PowerPC] Add _Float16 datatype support for Power8+ with -mfloat16 flag (PR #196559)
via cfe-commits
cfe-commits at lists.llvm.org
Fri May 8 08:53:43 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-powerpc
Author: Tony Varghese (tonykuttai)
<details>
<summary>Changes</summary>
This patch introduces experimental half-precision floating-point (_Float16)
support for PowerPC Power8 and later architectures, enabled via the new
-mfloat16 compiler flag.
Key changes:
- Add -mfloat16 flag to enable half-precision floating-point support
- Register f16 as a legal type using VHFRCRegClass (VSX half-precision registers)
- Implement f16 operations with promotion to f32 for arithmetic operations
- Add hardware conversion support for Power9+ (xscvhpdp/xscvdphp instructions)
- Use libcall conversions for Power8 (__extendhfsf2, __truncsfhf2, etc.)
- Support direct load/store operations for f16 values
- Handle f16 in calling conventions with proper register allocation
- Add comprehensive test coverage for AIX and Linux platforms
The implementation addresses correctness issues from GitHub issues #<!-- -->97975
(intermediate precision) and #<!-- -->97981 (NaN payload corruption) by treating
f16 as a first-class type rather than using lossy conversion functions.
Power9+ uses native hardware instructions for conversions, while Power8
falls back to compiler-rt library functions for type conversions.
---
Patch is 172.85 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/196559.diff
15 Files Affected:
- (modified) clang/include/clang/Options/Options.td (+2)
- (modified) clang/lib/Basic/Targets/PPC.cpp (+11)
- (modified) clang/lib/Basic/Targets/PPC.h (+5)
- (modified) clang/lib/Driver/ToolChains/Arch/PPC.cpp (+5)
- (added) clang/test/CodeGen/PowerPC/half-float16-ppc.c (+154)
- (modified) llvm/lib/Target/PowerPC/GISel/PPCRegisterBankInfo.cpp (+2-1)
- (modified) llvm/lib/Target/PowerPC/PPC.td (+5)
- (modified) llvm/lib/Target/PowerPC/PPCCallingConv.td (+10-3)
- (modified) llvm/lib/Target/PowerPC/PPCISelLowering.cpp (+90-11)
- (modified) llvm/lib/Target/PowerPC/PPCISelLowering.h (+5)
- (modified) llvm/lib/Target/PowerPC/PPCInstrVSX.td (+82)
- (modified) llvm/lib/Target/PowerPC/PPCRegisterInfo.td (+7)
- (added) llvm/test/CodeGen/PowerPC/f16-aix-psa.ll (+96)
- (added) llvm/test/CodeGen/PowerPC/half-float16-ppc.ll (+4198)
- (modified) llvm/test/CodeGen/PowerPC/shrink-wrap.mir (+1-1)
``````````diff
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 5eeabf4c33b76..3e4f189637e0a 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -5919,6 +5919,8 @@ def mcrbits : Flag<["-"], "mcrbits">, Group<m_ppc_Features_Group>,
"(the enablement of CR-bit tracking support) is the default for "
"POWER8 and above, as well as for all other CPUs when "
"optimization is applied (-O2 and above).">;
+def mfloat16 : Flag<["-"], "mfloat16">, Group<m_ppc_Features_Group>,
+ HelpText<"Enable half-precision floating point (experimental).">;
def mno_crbits : Flag<["-"], "mno-crbits">, Group<m_ppc_Features_Group>;
def minvariant_function_descriptors :
Flag<["-"], "minvariant-function-descriptors">, Group<m_ppc_Features_Group>;
diff --git a/clang/lib/Basic/Targets/PPC.cpp b/clang/lib/Basic/Targets/PPC.cpp
index c9a41df806aff..f999372f31a94 100644
--- a/clang/lib/Basic/Targets/PPC.cpp
+++ b/clang/lib/Basic/Targets/PPC.cpp
@@ -82,6 +82,8 @@ bool PPCTargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
HasQuadwordAtomics = true;
} else if (Feature == "+longcall") {
UseLongCalls = true;
+ } else if (Feature == "+float16") {
+ HasFloat16 = true;
}
// TODO: Finish this list and add an assert that we've handled them
// all.
@@ -504,6 +506,7 @@ static bool ppcUserFeaturesCheck(DiagnosticsEngine &Diags,
Found |= FindVSXSubfeature("+crypto", "-mcrypto", "-msoft-float");
Found |= FindVSXSubfeature("+power10-vector", "-mpower10-vector",
"-msoft-float");
+ Found |= FindVSXSubfeature("+float16", "-mfloat16", "-msoft-float");
}
if (Found)
return false;
@@ -555,6 +558,14 @@ bool PPCTargetInfo::initFeatureMap(
return false;
}
+ if (!(ArchDefs & ArchDefinePwr8)) {
+ if (llvm::is_contained(FeaturesVec, "+float16")) {
+ // Reject -mfloat16 on pre-Power8 CPUs.
+ Diags.Report(diag::err_opt_not_valid_with_opt) << "-mfloat16" << CPU;
+ return false;
+ }
+ }
+
if (!(ArchDefs & ArchDefinePwr10)) {
if (llvm::is_contained(FeaturesVec, "+mma")) {
// MMA operations are not available pre-Power10.
diff --git a/clang/lib/Basic/Targets/PPC.h b/clang/lib/Basic/Targets/PPC.h
index a9f49aa3aebe1..df263eac0d6de 100644
--- a/clang/lib/Basic/Targets/PPC.h
+++ b/clang/lib/Basic/Targets/PPC.h
@@ -73,6 +73,7 @@ class LLVM_LIBRARY_VISIBILITY PPCTargetInfo : public TargetInfo {
bool HasPCRelativeMemops = false;
bool HasQuadwordAtomics = false;
bool UseLongCalls = false;
+ bool HasFloat16 = false;
protected:
std::string ABI;
@@ -362,6 +363,10 @@ class LLVM_LIBRARY_VISIBILITY PPCTargetInfo : public TargetInfo {
bool hasBitIntType() const override { return true; }
+ // Returns true when _Float16 is available as a native type. Requires the
+ // explicit opt-in (-mfloat16).
+ bool hasFloat16Type() const override { return HasFloat16; }
+
bool isSPRegName(StringRef RegName) const override {
return RegName == "r1" || RegName == "x1";
}
diff --git a/clang/lib/Driver/ToolChains/Arch/PPC.cpp b/clang/lib/Driver/ToolChains/Arch/PPC.cpp
index 17051980f34fb..f06645038cfca 100644
--- a/clang/lib/Driver/ToolChains/Arch/PPC.cpp
+++ b/clang/lib/Driver/ToolChains/Arch/PPC.cpp
@@ -77,6 +77,11 @@ void ppc::getPPCTargetFeatures(const Driver &D, const llvm::Triple &Triple,
D.Diag(diag::err_opt_not_valid_on_target)
<< "-maix-shared-lib-tls-model-opt";
+ // The _Float16 datatype is supported throguh the -mfloat16 flag.
+ if (Args.hasArg(options::OPT_mfloat16)) {
+ Features.push_back("+float16");
+ }
+
// The integrated assembler counts as a "modern AIX assembler" for the
// purposes of the modern-aix-as.
if (Args.hasFlag(options::OPT_fintegrated_as, options::OPT_fno_integrated_as,
diff --git a/clang/test/CodeGen/PowerPC/half-float16-ppc.c b/clang/test/CodeGen/PowerPC/half-float16-ppc.c
new file mode 100644
index 0000000000000..45b336d03f7db
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/half-float16-ppc.c
@@ -0,0 +1,154 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
+// RUN: %clang_cc1 -triple powerpc-unknown-unknown -O2 -emit-llvm %s -o - \
+// RUN: -target-cpu pwr8 -target-feature +float16 | FileCheck %s
+
+typedef __INT8_TYPE__ int8_t;
+typedef __INT16_TYPE__ int16_t;
+typedef __INT32_TYPE__ int32_t;
+typedef __INT64_TYPE__ int64_t;
+typedef __UINT8_TYPE__ uint8_t;
+typedef __UINT16_TYPE__ uint16_t;
+typedef __UINT32_TYPE__ uint32_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+// ============================================
+// Arithmetic Operations
+// ============================================
+// CHECK-LABEL: define dso_local noundef half @c_add(
+// CHECK-SAME: half noundef [[A:%.*]], half noundef [[B:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[UNPROMOTION:%.*]] = fadd half [[A]], [[B]]
+// CHECK-NEXT: ret half [[UNPROMOTION]]
+//
+_Float16 c_add(_Float16 a, _Float16 b) { return a + b; }
+
+
+// CHECK-LABEL: define dso_local noundef half @c_sub(
+// CHECK-SAME: half noundef [[A:%.*]], half noundef [[B:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[UNPROMOTION:%.*]] = fsub half [[A]], [[B]]
+// CHECK-NEXT: ret half [[UNPROMOTION]]
+//
+_Float16 c_sub(_Float16 a, _Float16 b) { return a - b; }
+
+// CHECK-LABEL: define dso_local noundef half @c_mul(
+// CHECK-SAME: half noundef [[A:%.*]], half noundef [[B:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[UNPROMOTION:%.*]] = fmul half [[A]], [[B]]
+// CHECK-NEXT: ret half [[UNPROMOTION]]
+//
+_Float16 c_mul(_Float16 a, _Float16 b) { return a * b; }
+
+// CHECK-LABEL: define dso_local noundef half @c_div(
+// CHECK-SAME: half noundef [[A:%.*]], half noundef [[B:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[UNPROMOTION:%.*]] = fdiv half [[A]], [[B]]
+// CHECK-NEXT: ret half [[UNPROMOTION]]
+//
+_Float16 c_div(_Float16 a, _Float16 b) { return a / b; }
+
+// ============================================
+// Unary Operations
+// ============================================
+
+// CHECK-LABEL: define dso_local noundef half @c_neg(
+// CHECK-SAME: half noundef [[A:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[UNPROMOTION:%.*]] = fneg half [[A]]
+// CHECK-NEXT: ret half [[UNPROMOTION]]
+//
+_Float16 c_neg(_Float16 a) { return -a; }
+
+// ============================================
+// Compare Operations
+// ============================================
+
+// CHECK-LABEL: define dso_local range(i32 0, 2) i32 @c_eq(
+// CHECK-SAME: half noundef [[A:%.*]], half noundef [[B:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[CMP:%.*]] = fcmp oeq half [[A]], [[B]]
+// CHECK-NEXT: [[CONV:%.*]] = zext i1 [[CMP]] to i32
+// CHECK-NEXT: ret i32 [[CONV]]
+//
+int c_eq(_Float16 a, _Float16 b){ return a == b; }
+
+// CHECK-LABEL: define dso_local range(i32 0, 2) i32 @c_ne(
+// CHECK-SAME: half noundef [[A:%.*]], half noundef [[B:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[CMP:%.*]] = fcmp une half [[A]], [[B]]
+// CHECK-NEXT: [[CONV:%.*]] = zext i1 [[CMP]] to i32
+// CHECK-NEXT: ret i32 [[CONV]]
+//
+int c_ne(_Float16 a, _Float16 b){ return a != b; }
+
+// CHECK-LABEL: define dso_local range(i32 0, 2) i32 @c_lt(
+// CHECK-SAME: half noundef [[A:%.*]], half noundef [[B:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[CMP:%.*]] = fcmp olt half [[A]], [[B]]
+// CHECK-NEXT: [[CONV:%.*]] = zext i1 [[CMP]] to i32
+// CHECK-NEXT: ret i32 [[CONV]]
+//
+int c_lt(_Float16 a, _Float16 b){ return a < b; }
+
+// CHECK-LABEL: define dso_local range(i32 0, 2) i32 @c_le(
+// CHECK-SAME: half noundef [[A:%.*]], half noundef [[B:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[CMP:%.*]] = fcmp ole half [[A]], [[B]]
+// CHECK-NEXT: [[CONV:%.*]] = zext i1 [[CMP]] to i32
+// CHECK-NEXT: ret i32 [[CONV]]
+//
+int c_le(_Float16 a, _Float16 b){ return a <= b; }
+
+// CHECK-LABEL: define dso_local range(i32 0, 2) i32 @c_gt(
+// CHECK-SAME: half noundef [[A:%.*]], half noundef [[B:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[CMP:%.*]] = fcmp ogt half [[A]], [[B]]
+// CHECK-NEXT: [[CONV:%.*]] = zext i1 [[CMP]] to i32
+// CHECK-NEXT: ret i32 [[CONV]]
+//
+int c_gt(_Float16 a, _Float16 b){ return a > b; }
+
+// CHECK-LABEL: define dso_local range(i32 0, 2) i32 @c_ge(
+// CHECK-SAME: half noundef [[A:%.*]], half noundef [[B:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[CMP:%.*]] = fcmp oge half [[A]], [[B]]
+// CHECK-NEXT: [[CONV:%.*]] = zext i1 [[CMP]] to i32
+// CHECK-NEXT: ret i32 [[CONV]]
+//
+int c_ge(_Float16 a, _Float16 b){ return a >= b; }
+
+// ============================================
+// Conversion Operations: half <-> float/double
+// ============================================
+
+// CHECK-LABEL: define dso_local noundef float @to_f32(
+// CHECK-SAME: half noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[CONV:%.*]] = fpext half [[X]] to float
+// CHECK-NEXT: ret float [[CONV]]
+//
+float to_f32(_Float16 x){ return (float)x; }
+
+// CHECK-LABEL: define dso_local noundef double @to_f64(
+// CHECK-SAME: half noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[CONV:%.*]] = fpext half [[X]] to double
+// CHECK-NEXT: ret double [[CONV]]
+//
+double to_f64(_Float16 x){ return (double)x; }
+
+// CHECK-LABEL: define dso_local noundef half @from_f32(
+// CHECK-SAME: float noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[CONV:%.*]] = fptrunc float [[X]] to half
+// CHECK-NEXT: ret half [[CONV]]
+//
+_Float16 from_f32(float x){ return (_Float16)x; }
+
+// CHECK-LABEL: define dso_local noundef half @from_f64(
+// CHECK-SAME: double noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[CONV:%.*]] = fptrunc double [[X]] to half
+// CHECK-NEXT: ret half [[CONV]]
+//
+_Float16 from_f64(double x){ return (_Float16)x; }
diff --git a/llvm/lib/Target/PowerPC/GISel/PPCRegisterBankInfo.cpp b/llvm/lib/Target/PowerPC/GISel/PPCRegisterBankInfo.cpp
index 3370dedbecb16..fb6eccfe043b5 100644
--- a/llvm/lib/Target/PowerPC/GISel/PPCRegisterBankInfo.cpp
+++ b/llvm/lib/Target/PowerPC/GISel/PPCRegisterBankInfo.cpp
@@ -34,7 +34,8 @@ PPCRegisterBankInfo::getRegBankFromRegClass(const TargetRegisterClass &RC,
LLT Ty) const {
switch (RC.getID()) {
case PPC::VSFRCRegClassID:
- case PPC::SPILLTOVSRRC_and_VSFRCRegClassID:
+ case PPC::VHFRCRegClassID:
+ case PPC::SPILLTOVSRRC_and_VHFRCRegClassID:
case PPC::SPILLTOVSRRC_and_VFRCRegClassID:
case PPC::SPILLTOVSRRC_and_F4RCRegClassID:
case PPC::F8RCRegClassID:
diff --git a/llvm/lib/Target/PowerPC/PPC.td b/llvm/lib/Target/PowerPC/PPC.td
index 7b4bae60f7e74..43982c4c5c5fb 100644
--- a/llvm/lib/Target/PowerPC/PPC.td
+++ b/llvm/lib/Target/PowerPC/PPC.td
@@ -71,6 +71,10 @@ def FeatureModernAIXAs
"AIX system assembler is modern enough to support new mnes">;
def FeatureHardFloat : SubtargetFeature<"hard-float", "HasHardFloat", "true",
"Enable floating-point instructions">;
+def FeatureFloat16 :
+ SubtargetFeature<"float16",
+ "HasFloat16", "true",
+ "Enable half-precision floating point support (experimental)">;
// Specifies that we are in 64-bit mode or that we should use 64-bit registers
// in 32-bit mode when possible. Requires Feature64Bit to be enabled.
@@ -405,6 +409,7 @@ def HasOnlySwappingMemOps : Predicate<"!Subtarget->hasP9Vector()">;
def NoP10Vector : Predicate<"!Subtarget->hasP10Vector()">;
def HasP10Vector : Predicate<"Subtarget->hasP10Vector()">;
def HasFutureVector : Predicate<"Subtarget->hasFutureVector()">;
+def HasFloat16 : Predicate<"Subtarget->hasFloat16()">;
// Predicates used to differenciate between different ISAs.
def IsISA2_06 : Predicate<"Subtarget->isISA2_06()">;
diff --git a/llvm/lib/Target/PowerPC/PPCCallingConv.td b/llvm/lib/Target/PowerPC/PPCCallingConv.td
index 5d4fe06ebdddd..961da883748dc 100644
--- a/llvm/lib/Target/PowerPC/PPCCallingConv.td
+++ b/llvm/lib/Target/PowerPC/PPCCallingConv.td
@@ -75,6 +75,9 @@ def RetCC_PPC : CallingConv<[
CCIfType<[i64], CCAssignToReg<[X3, X4, X5, X6]>>,
CCIfType<[i128], CCAssignToReg<[X3, X4, X5, X6]>>,
+ // Return f16 in FPR as 16 bit value.
+ CCIfType<[f16], CCAssignToReg<[F1, F2, F3, F4, F5, F6, F7, F8]>>,
+
// Floating point types returned as "direct" go into F1 .. F8; note that
// only the ELFv2 ABI fully utilizes all these registers.
CCIfNotSubtarget<"hasSPE()",
@@ -125,8 +128,8 @@ def CC_PPC64_ELF : CallingConv<[
CCIfType<[i64], CCAssignToReg<[X3, X4, X5, X6, X7, X8, X9, X10]>>,
// Handle fp types and shadow the corresponding registers as necessary.
- CCIfType<[f32, f64], CCIfNotVarArg<CCCustom<"CC_PPC64_ELF_Shadow_GPR_Regs">>>,
- CCIfType<[f32, f64],
+ CCIfType<[f16, f32, f64], CCIfNotVarArg<CCCustom<"CC_PPC64_ELF_Shadow_GPR_Regs">>>,
+ CCIfType<[f16, f32, f64],
CCIfNotVarArg<CCAssignToReg<[F1, F2, F3, F4, F5, F6, F7, F8, F9, F10,
F11, F12, F13]>>>,
@@ -161,7 +164,7 @@ def CC_PPC64_ELF_FIS : CallingConv<[
CCIfType<[i16], CCPromoteToType<i64>>,
CCIfType<[i32], CCPromoteToType<i64>>,
CCIfType<[i64], CCAssignToReg<[X3, X4, X5, X6, X7, X8, X9, X10]>>,
- CCIfType<[f32, f64], CCAssignToReg<[F1, F2, F3, F4, F5, F6, F7, F8]>>
+ CCIfType<[f16, f32, f64], CCAssignToReg<[F1, F2, F3, F4, F5, F6, F7, F8]>>
]>;
// Simple return-value convention for 64-bit ELF PowerPC fast isel.
@@ -178,6 +181,7 @@ def RetCC_PPC64_ELF_FIS : CallingConv<[
CCIfType<[i32], CCPromoteToType<i64>>,
CCIfType<[i64], CCAssignToReg<[X3, X4, X5, X6]>>,
CCIfType<[i128], CCAssignToReg<[X3, X4, X5, X6]>>,
+ CCIfType<[f16], CCAssignToReg<[F1, F2, F3, F4, F5, F6, F7, F8]>>,
CCIfType<[f32], CCAssignToReg<[F1, F2, F3, F4, F5, F6, F7, F8]>>,
CCIfType<[f64], CCAssignToReg<[F1, F2, F3, F4, F5, F6, F7, F8]>>,
CCIfType<[f128],
@@ -218,6 +222,9 @@ def CC_PPC32_SVR4_Common : CallingConv<[
// The first 8 integer arguments are passed in integer registers.
CCIfType<[i32], CCAssignToReg<[R3, R4, R5, R6, R7, R8, R9, R10]>>,
+ // Pass f16 in FPRs.
+ CCIfType<[f16], CCAssignToReg<[F1, F2, F3, F4, F5, F6, F7, F8]>>,
+
// Make sure the i64 words from a long double are either both passed in
// registers or both passed on the stack.
CCIfType<[f64], CCIfSplit<CCCustom<"CC_PPC32_SVR4_Custom_AlignFPArgRegs">>>,
diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
index 407093fd2b849..3a79e46e51bed 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
@@ -261,6 +261,68 @@ PPCTargetLowering::PPCTargetLowering(const PPCTargetMachine &TM,
setTruncStoreAction(MVT::f32, MVT::f16, Expand);
}
+ // Only active when -mfloat16 is passed and hard float is enabled.
+ // This block intentionally overrides some actions set above
+ // because when f16 is a first-class type we handle load/store
+ // directly rather than through extending loads.
+ if (Subtarget.hasFloat16() && Subtarget.hasHardFloat()) {
+ // Make f16 a legal type.
+ addRegisterClass(MVT::f16, &PPC::VHFRCRegClass);
+
+ // PowerPC has no native f16 arithmetic instructions. All arithmetic,
+ // comparisons, rounding, transcendentals, and min/max must be promoted
+ // to f32 for computation. On P8 this means libcalls (__extendhfsf2 /
+ // __truncsfhf2); on P9 this means xscvhpdp/xsaddsp.../xscvdphp sequences.
+ static const unsigned F16PromoteOps[] = {
+ ISD::FMINNUM, ISD::FMAXNUM, ISD::FMAXIMUMNUM, ISD::FMINIMUMNUM,
+ ISD::FMAXIMUM, ISD::FMINIMUM, ISD::FADD, ISD::FSUB,
+ ISD::FMUL, ISD::FMA, ISD::FDIV, ISD::FSQRT,
+ ISD::FREM, ISD::FPOW, ISD::FLOG, ISD::FLOG2,
+ ISD::FLOG10, ISD::FEXP, ISD::FEXP2, ISD::FEXP10,
+ ISD::FCEIL, ISD::FFLOOR, ISD::FTRUNC, ISD::FRINT,
+ ISD::FNEARBYINT, ISD::FROUND, ISD::FROUNDEVEN, ISD::FCANONICALIZE,
+ ISD::FSIN, ISD::FCOS, ISD::SETCC, ISD::SELECT_CC,
+ ISD::SELECT};
+
+ // Promote all the arithmetic operations defined above to f32.
+ setOperationAction(F16PromoteOps, MVT::f16, Promote);
+
+ setOperationAction(ISD::LOAD, MVT::f16, Legal);
+ setOperationAction(ISD::STORE, MVT::f16, Legal);
+
+ // Legal handling for bit manipulation.
+ setOperationAction(ISD::FABS, MVT::f16, Legal);
+ setOperationAction(ISD::FNEG, MVT::f16, Legal);
+ setOperationAction(ISD::FCOPYSIGN, MVT::f16, Legal);
+
+ // Expand constant FP.
+ setOperationAction(ISD::ConstantFP, MVT::f16, Expand);
+
+ // Expand extending loads and truncating stores.
+ for (MVT VT : {MVT::f32, MVT::f64}) {
+ setLoadExtAction(ISD::EXTLOAD, VT, MVT::f16, Expand);
+ setTruncStoreAction(VT, MVT::f16, Expand);
+ }
+
+ if (Subtarget.hasP9Vector()) {
+ // P9+: Hardware support for conversions.
+ setOperationAction(ISD::FP_EXTEND, MVT::f32, Legal);
+ setOperationAction(ISD::FP_EXTEND, MVT::f64, Legal);
+ setOperationAction(ISD::STRICT_FP_EXTEND, MVT::f32, Legal);
+ setOperationAction(ISD::STRICT_FP_EXTEND, MVT::f64, Legal);
+ setOperationAction(ISD::FP_ROUND, MVT::f16, Legal);
+ setOperationAction(ISD::STRICT_FP_ROUND, MVT::f16, Legal);
+ } else {
+ // P8: Conversions via libcalls
+ setOperationAction(ISD::FP_EXTEND, MVT::f32, Expand);
+ setOperationAction(ISD::FP_EXTEND, MVT::f64, Expand);
+ setOperationAction(ISD::STRICT_FP_EXTEND, MVT::f32, Expand);
+ setOperationAction(ISD::STRICT_FP_EXTEND, MVT::f64, Expand);
+ setOperationAction(ISD::FP_ROUND, MVT::f16, Expand);
+ setOperationAction(ISD::STRICT_FP_ROUND, MVT::f16, Expand);
+ }
+ }
+
setTruncStoreAction(MVT::f64, MVT::f32, Expand);
// PowerPC has pre-inc load and store's.
@@ -1675,6 +1737,12 @@ bool PPCTargetLowering::hasSPE() const {
return Subtarget.hasSPE();
}
+/// Tell the ABI lowering infrastructure to use FPRs for _Float16 parameters
+/// and return values rather than GPRs. Active only when -mfloat16 is enabled.
+bool PPCTargetLowering::useFPRegsForHalfType() const {
+ return Subtarget.hasFloat16() && Subtarget.hasHardFloat();
+}
+
bool PPCTargetLowering::preferIncOfAddToSubOfNot(EVT VT) const {
return VT.isScalarInteger();
}
@@ -4630,6 +4698,7 @@ SDValue PPCTargetLowering::LowerFormalArguments_64SVR4(
ArgOffset += 8;
break;
+ case MVT::f16:
case MVT::f32:
case MVT::f64:
// These can be scalar arguments or elements of a float array type
@@ -6497,6 +6566,7 @@ SDValue PPCTargetLowering::LowerCall_64SVR4(
if (!IsFastCall)
ArgOffset += PtrByteSize;
break;
+ case MVT::f16:
case MVT::f32:
case MVT::f64: {
// These can be scalar arguments or elements of a float array type
@@ -6843,6 +6913,7 @@ static bool CC_AIX(unsigned ValNo, MVT ValVT, MVT LocVT,
return false;
}
+ case MVT::f16:
case MVT::f32:
case MVT::f64: {
// Parameter save area (PSA) is reserved even if the float passes in fpr.
@@ -6986,10 +7057,9 @@ static bool CC_AIX(unsigned ValNo, MVT ValVT, MVT LocVT,
}
// So far, this function is only used by LowerFormalArguments_AIX()
-static const TargetRegisterClass *getRegClassForSVT(MVT::SimpleValueType SVT,
- bool IsPPC64,
- bool HasP8Vector,
- bool HasVSX) {
+static const TargetRegisterClass *
+getRegClassFo...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/196559
More information about the cfe-commits
mailing list