From dc18cb9e2de3b40f37b9fdf390ef5d0f21334c4b Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 23 Mar 2015 17:54:16 +0000 Subject: [PATCH] [CodeGen] Properly support the half FP type with non-native operations. On AArch64, the -fallow-half-args-and-returns option is the default. With it, the half type is considered legal (rather than the i16 used normally for __fp16), but no operation is, except conversions and load/stores and such. The previous behavior was tantamount to saying LangOpts.NativeHalfType was implied by LangOpts.HalfArgsAndReturns, which isn't true. Instead, teach the various parts of CodeGen that already know about half (using the intrinsics or not) about this weird in-between case, where the "half" type is legal, but operations on it aren't. This is a smaller intermediate step to the end-goal of removing the intrinsic, always using "half", and letting the backend legalize. Builds on r232968. rdar://20045970, rdar://17468714 Differential Revision: http://reviews.llvm.org/D8367 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@232971 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGExprScalar.cpp | 94 +++++++----- test/CodeGen/fp16-ops.c | 282 ++++++++++++++++++----------------- 2 files changed, 208 insertions(+), 168 deletions(-) diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index 85a0de2b91..11d10d27c3 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -751,20 +751,29 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType, llvm::Type *DstTy = ConvertType(DstType); - // Cast from storage-only half FP using the special intrinsic. - if (SrcType->isHalfType() && !CGF.getContext().getLangOpts().NativeHalfType && - !CGF.getContext().getLangOpts().HalfArgsAndReturns) { - if (DstTy->isFloatingPointTy()) - return Builder.CreateCall( - CGF.CGM.getIntrinsic(llvm::Intrinsic::convert_from_fp16, DstTy), Src); - - // If this isn't an FP->FP conversion, go through float. - Src = Builder.CreateCall( - CGF.CGM.getIntrinsic(llvm::Intrinsic::convert_from_fp16, - CGF.CGM.FloatTy), - Src); - SrcType = CGF.getContext().FloatTy; - SrcTy = CGF.FloatTy; + // Cast from half through float if half isn't a native type. + if (SrcType->isHalfType() && !CGF.getContext().getLangOpts().NativeHalfType) { + // Cast to FP using the intrinsic if the half type itself isn't supported. + if (DstTy->isFloatingPointTy()) { + if (!CGF.getContext().getLangOpts().HalfArgsAndReturns) + return Builder.CreateCall( + CGF.CGM.getIntrinsic(llvm::Intrinsic::convert_from_fp16, DstTy), + Src); + } else { + // Cast to other types through float, using either the intrinsic or FPExt, + // depending on whether the half type itself is supported + // (as opposed to operations on half, available with NativeHalfType). + if (!CGF.getContext().getLangOpts().HalfArgsAndReturns) { + Src = Builder.CreateCall( + CGF.CGM.getIntrinsic(llvm::Intrinsic::convert_from_fp16, + CGF.CGM.FloatTy), + Src); + } else { + Src = Builder.CreateFPExt(Src, CGF.CGM.FloatTy, "conv"); + } + SrcType = CGF.getContext().FloatTy; + SrcTy = CGF.FloatTy; + } } // Ignore conversions like int -> uint. @@ -823,12 +832,18 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType, EmitFloatConversionCheck(OrigSrc, OrigSrcType, Src, SrcType, DstType, DstTy); - // Cast to half using the intrinsic if from FP type, through float otherwise. - if (DstType->isHalfType() && !CGF.getContext().getLangOpts().NativeHalfType && - !CGF.getContext().getLangOpts().HalfArgsAndReturns) { - if (SrcTy->isFloatingPointTy()) - return Builder.CreateCall( - CGF.CGM.getIntrinsic(llvm::Intrinsic::convert_to_fp16, SrcTy), Src); + // Cast to half through float if half isn't a native type. + if (DstType->isHalfType() && !CGF.getContext().getLangOpts().NativeHalfType) { + // Make sure we cast in a single step if from another FP type. + if (SrcTy->isFloatingPointTy()) { + // Use the intrinsic if the half type itself isn't supported + // (as opposed to operations on half, available with NativeHalfType). + if (!CGF.getContext().getLangOpts().HalfArgsAndReturns) + return Builder.CreateCall( + CGF.CGM.getIntrinsic(llvm::Intrinsic::convert_to_fp16, SrcTy), Src); + // If the half type is supported, just use an fptrunc. + return Builder.CreateFPTrunc(Src, DstTy); + } DstTy = CGF.FloatTy; } @@ -856,10 +871,14 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType, } if (DstTy != ResTy) { - assert(ResTy->isIntegerTy(16) && "Only half FP requires extra conversion"); - Res = Builder.CreateCall( + if (!CGF.getContext().getLangOpts().HalfArgsAndReturns) { + assert(ResTy->isIntegerTy(16) && "Only half FP requires extra conversion"); + Res = Builder.CreateCall( CGF.CGM.getIntrinsic(llvm::Intrinsic::convert_to_fp16, CGF.CGM.FloatTy), Res); + } else { + Res = Builder.CreateFPTrunc(Res, ResTy, "conv"); + } } return Res; @@ -1762,13 +1781,16 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, // Add the inc/dec to the real part. llvm::Value *amt; - if (type->isHalfType() && !CGF.getContext().getLangOpts().NativeHalfType && - !CGF.getContext().getLangOpts().HalfArgsAndReturns) { + if (type->isHalfType() && !CGF.getContext().getLangOpts().NativeHalfType) { // Another special case: half FP increment should be done via float - value = Builder.CreateCall( - CGF.CGM.getIntrinsic(llvm::Intrinsic::convert_from_fp16, - CGF.CGM.FloatTy), - input); + if (!CGF.getContext().getLangOpts().HalfArgsAndReturns) { + value = Builder.CreateCall( + CGF.CGM.getIntrinsic(llvm::Intrinsic::convert_from_fp16, + CGF.CGM.FloatTy), + input, "incdec.conv"); + } else { + value = Builder.CreateFPExt(input, CGF.CGM.FloatTy, "incdec.conv"); + } } if (value->getType()->isFloatTy()) @@ -1786,12 +1808,16 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, } value = Builder.CreateFAdd(value, amt, isInc ? "inc" : "dec"); - if (type->isHalfType() && !CGF.getContext().getLangOpts().NativeHalfType && - !CGF.getContext().getLangOpts().HalfArgsAndReturns) - value = Builder.CreateCall( - CGF.CGM.getIntrinsic(llvm::Intrinsic::convert_to_fp16, - CGF.CGM.FloatTy), - value); + if (type->isHalfType() && !CGF.getContext().getLangOpts().NativeHalfType) { + if (!CGF.getContext().getLangOpts().HalfArgsAndReturns) { + value = Builder.CreateCall( + CGF.CGM.getIntrinsic(llvm::Intrinsic::convert_to_fp16, + CGF.CGM.FloatTy), + value, "incdec.conv"); + } else { + value = Builder.CreateFPTrunc(value, input->getType(), "incdec.conv"); + } + } // Objective-C pointer types. } else { diff --git a/test/CodeGen/fp16-ops.c b/test/CodeGen/fp16-ops.c index 5dd0b320b2..0c218be0ec 100644 --- a/test/CodeGen/fp16-ops.c +++ b/test/CodeGen/fp16-ops.c @@ -1,5 +1,8 @@ // REQUIRES: arm-registered-target -// RUN: %clang_cc1 -emit-llvm -o - -triple arm-none-linux-gnueabi %s | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -o - -triple arm-none-linux-gnueabi %s | FileCheck %s --check-prefix=NOHALF --check-prefix=CHECK +// RUN: %clang_cc1 -emit-llvm -o - -triple aarch64-none-linux-gnueabi %s | FileCheck %s --check-prefix=NOHALF --check-prefix=CHECK +// RUN: %clang_cc1 -emit-llvm -o - -triple arm-none-linux-gnueabi -fallow-half-arguments-and-returns %s | FileCheck %s --check-prefix=HALF --check-prefix=CHECK +// RUN: %clang_cc1 -emit-llvm -o - -triple aarch64-none-linux-gnueabi -fallow-half-arguments-and-returns %s | FileCheck %s --check-prefix=HALF --check-prefix=CHECK typedef unsigned cond_t; volatile cond_t test; @@ -12,289 +15,300 @@ void foo(void) { // Check unary ops - // CHECK: call float @llvm.convert.from.fp16.f32( + // NOHALF: [[F16TOF32:call float @llvm.convert.from.fp16.f32]] + // HALF: [[F16TOF32:fpext half]] // CHECK: fptoui float test = (h0); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: uitofp i32 + // NOHALF: [[F32TOF16:call i16 @llvm.convert.to.fp16.f32]] + // HALF: [[F32TOF16:fptrunc float]] + h0 = (test); + // CHECK: [[F16TOF32]] // CHECK: fcmp une float test = (!h1); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fsub float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // NOHALF: [[F32TOF16]] + // HALF: [[F32TOF16]] h1 = -h1; - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F16TOF32]] + // CHECK: [[F32TOF16]] h1 = +h1; - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fadd float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h1++; - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fadd float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] ++h1; - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fadd float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] --h1; - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fadd float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h1--; // Check binary ops with various operands - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // CHECK: [[F16TOF32]] // CHECK: fmul float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h1 = h0 * h2; - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call i16 @llvm.convert.to.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // NOHALF: [[F32TOF16]] + // NOHALF: [[F16TOF32]] // CHECK: fmul float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h1 = h0 * (__fp16) -2.0f; - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fmul float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h1 = h0 * f2; - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fmul float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h1 = f0 * h2; - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // CHECK: [[F16TOF32]] // CHECK: fdiv float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h1 = (h0 / h2); - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // NOHALF: [[F16TOF32]] // CHECK: fdiv float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h1 = (h0 / (__fp16) -2.0f); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fdiv float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h1 = (h0 / f2); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fdiv float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h1 = (f0 / h2); - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // CHECK: [[F16TOF32]] // CHECK: fadd float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h1 = (h2 + h0); - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // NOHALF: [[F16TOF32]] // CHECK: fadd float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h1 = ((__fp16)-2.0 + h0); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fadd float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h1 = (h2 + f0); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fadd float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h1 = (f2 + h0); - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // CHECK: [[F16TOF32]] // CHECK: fsub float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h1 = (h2 - h0); - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // NOHALF: [[F16TOF32]] // CHECK: fsub float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h1 = ((__fp16)-2.0f - h0); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fsub float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h1 = (h2 - f0); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fsub float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h1 = (f2 - h0); - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // CHECK: [[F16TOF32]] // CHECK: fcmp olt test = (h2 < h0); - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // NOHALF: [[F16TOF32]] // CHECK: fcmp olt test = (h2 < (__fp16)42.0); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fcmp olt test = (h2 < f0); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fcmp olt test = (f2 < h0); - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // CHECK: [[F16TOF32]] // CHECK: fcmp ogt test = (h0 > h2); - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // NOHALF: [[F16TOF32]] // CHECK: fcmp ogt test = ((__fp16)42.0 > h2); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fcmp ogt test = (h0 > f2); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fcmp ogt test = (f0 > h2); - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // CHECK: [[F16TOF32]] // CHECK: fcmp ole test = (h2 <= h0); - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // NOHALF: [[F16TOF32]] // CHECK: fcmp ole test = (h2 <= (__fp16)42.0); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fcmp ole test = (h2 <= f0); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fcmp ole test = (f2 <= h0); - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // CHECK: [[F16TOF32]] // CHECK: fcmp oge test = (h0 >= h2); - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // NOHALF: [[F16TOF32]] // CHECK: fcmp oge test = (h0 >= (__fp16)-2.0); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fcmp oge test = (h0 >= f2); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fcmp oge test = (f0 >= h2); - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // CHECK: [[F16TOF32]] // CHECK: fcmp oeq test = (h1 == h2); - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // NOHALF: [[F16TOF32]] // CHECK: fcmp oeq test = (h1 == (__fp16)1.0); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fcmp oeq test = (h1 == f1); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fcmp oeq test = (f1 == h1); - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // CHECK: [[F16TOF32]] // CHECK: fcmp une test = (h1 != h2); - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // NOHALF: [[F16TOF32]] // CHECK: fcmp une test = (h1 != (__fp16)1.0); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fcmp une test = (h1 != f1); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fcmp une test = (f1 != h1); - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fcmp une - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F16TOF32]] + // CHECK: [[F16TOF32]] + // CHECK: [[F32TOF16]] h1 = (h1 ? h2 : h0); // Check assignments (inc. compound) h0 = h1; - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // NOHALF: [[F32TOF16]] + // HALF: store {{.*}} half 0xHC000 h0 = (__fp16)-2.0f; - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h0 = f0; - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // CHECK: [[F16TOF32]] // CHECK: fadd float - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h0 += h1; - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // NOHALF: [[F16TOF32]] // CHECK: fadd - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h0 += (__fp16)1.0f; - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fadd - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h0 += f2; - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // CHECK: [[F16TOF32]] // CHECK: fsub - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h0 -= h1; - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // NOHALF: [[F16TOF32]] // CHECK: fsub - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h0 -= (__fp16)1.0; - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fsub - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h0 -= f2; - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // CHECK: [[F16TOF32]] // CHECK: fmul - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h0 *= h1; - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // NOHALF: [[F16TOF32]] // CHECK: fmul - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h0 *= (__fp16)1.0; - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fmul - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h0 *= f2; - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // CHECK: [[F16TOF32]] // CHECK: fdiv - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h0 /= h1; - // CHECK: call float @llvm.convert.from.fp16.f32( - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] + // NOHALF: [[F16TOF32]] // CHECK: fdiv - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h0 /= (__fp16)1.0; - // CHECK: call float @llvm.convert.from.fp16.f32( + // CHECK: [[F16TOF32]] // CHECK: fdiv - // CHECK: call i16 @llvm.convert.to.fp16.f32( + // CHECK: [[F32TOF16]] h0 /= f2; // Check conversions to/from double - // CHECK: call i16 @llvm.convert.to.fp16.f64( + // NOHALF: call i16 @llvm.convert.to.fp16.f64( + // HALF: fptrunc double {{.*}} to half h0 = d0; // CHECK: [[MID:%.*]] = fptrunc double {{%.*}} to float - // CHECK: call i16 @llvm.convert.to.fp16.f32(float [[MID]]) + // NOHALF: call i16 @llvm.convert.to.fp16.f32(float [[MID]]) + // HALF: fptrunc float [[MID]] to half h0 = (float)d0; - // CHECK: call double @llvm.convert.from.fp16.f64( + // NOHALF: call double @llvm.convert.from.fp16.f64( + // HALF: fpext half {{.*}} to double d0 = h0; - // CHECK: [[MID:%.*]] = call float @llvm.convert.from.fp16.f32( + // NOHALF: [[MID:%.*]] = call float @llvm.convert.from.fp16.f32( + // HALF: [[MID:%.*]] = fpext half {{.*}} to float // CHECK: fpext float [[MID]] to double d0 = (float)h0; } -- 2.40.0