[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

llvm-svn: 232971
This commit is contained in:
Ahmed Bougacha 2015-03-23 17:54:16 +00:00
parent 4c276fb4cc
commit d1801afeac
2 changed files with 208 additions and 168 deletions

View File

@ -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 {

View File

@ -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;
}