[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:
parent
4c276fb4cc
commit
d1801afeac
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue