Files
clang-p2996/clang/test/CodeGen/fp16-ops.c
Ahmed Bougacha d1801afeac [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
2015-03-23 17:54:16 +00:00

315 lines
7.4 KiB
C

// REQUIRES: arm-registered-target
// 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;
volatile __fp16 h0 = 0.0, h1 = 1.0, h2;
volatile float f0, f1, f2;
volatile double d0;
void foo(void) {
// CHECK-LABEL: define void @foo()
// Check unary ops
// NOHALF: [[F16TOF32:call float @llvm.convert.from.fp16.f32]]
// HALF: [[F16TOF32:fpext half]]
// CHECK: fptoui float
test = (h0);
// 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: [[F16TOF32]]
// CHECK: fsub float
// NOHALF: [[F32TOF16]]
// HALF: [[F32TOF16]]
h1 = -h1;
// CHECK: [[F16TOF32]]
// CHECK: [[F32TOF16]]
h1 = +h1;
// CHECK: [[F16TOF32]]
// CHECK: fadd float
// CHECK: [[F32TOF16]]
h1++;
// CHECK: [[F16TOF32]]
// CHECK: fadd float
// CHECK: [[F32TOF16]]
++h1;
// CHECK: [[F16TOF32]]
// CHECK: fadd float
// CHECK: [[F32TOF16]]
--h1;
// CHECK: [[F16TOF32]]
// CHECK: fadd float
// CHECK: [[F32TOF16]]
h1--;
// Check binary ops with various operands
// CHECK: [[F16TOF32]]
// CHECK: [[F16TOF32]]
// CHECK: fmul float
// CHECK: [[F32TOF16]]
h1 = h0 * h2;
// CHECK: [[F16TOF32]]
// NOHALF: [[F32TOF16]]
// NOHALF: [[F16TOF32]]
// CHECK: fmul float
// CHECK: [[F32TOF16]]
h1 = h0 * (__fp16) -2.0f;
// CHECK: [[F16TOF32]]
// CHECK: fmul float
// CHECK: [[F32TOF16]]
h1 = h0 * f2;
// CHECK: [[F16TOF32]]
// CHECK: fmul float
// CHECK: [[F32TOF16]]
h1 = f0 * h2;
// CHECK: [[F16TOF32]]
// CHECK: [[F16TOF32]]
// CHECK: fdiv float
// CHECK: [[F32TOF16]]
h1 = (h0 / h2);
// CHECK: [[F16TOF32]]
// NOHALF: [[F16TOF32]]
// CHECK: fdiv float
// CHECK: [[F32TOF16]]
h1 = (h0 / (__fp16) -2.0f);
// CHECK: [[F16TOF32]]
// CHECK: fdiv float
// CHECK: [[F32TOF16]]
h1 = (h0 / f2);
// CHECK: [[F16TOF32]]
// CHECK: fdiv float
// CHECK: [[F32TOF16]]
h1 = (f0 / h2);
// CHECK: [[F16TOF32]]
// CHECK: [[F16TOF32]]
// CHECK: fadd float
// CHECK: [[F32TOF16]]
h1 = (h2 + h0);
// CHECK: [[F16TOF32]]
// NOHALF: [[F16TOF32]]
// CHECK: fadd float
// CHECK: [[F32TOF16]]
h1 = ((__fp16)-2.0 + h0);
// CHECK: [[F16TOF32]]
// CHECK: fadd float
// CHECK: [[F32TOF16]]
h1 = (h2 + f0);
// CHECK: [[F16TOF32]]
// CHECK: fadd float
// CHECK: [[F32TOF16]]
h1 = (f2 + h0);
// CHECK: [[F16TOF32]]
// CHECK: [[F16TOF32]]
// CHECK: fsub float
// CHECK: [[F32TOF16]]
h1 = (h2 - h0);
// CHECK: [[F16TOF32]]
// NOHALF: [[F16TOF32]]
// CHECK: fsub float
// CHECK: [[F32TOF16]]
h1 = ((__fp16)-2.0f - h0);
// CHECK: [[F16TOF32]]
// CHECK: fsub float
// CHECK: [[F32TOF16]]
h1 = (h2 - f0);
// CHECK: [[F16TOF32]]
// CHECK: fsub float
// CHECK: [[F32TOF16]]
h1 = (f2 - h0);
// CHECK: [[F16TOF32]]
// CHECK: [[F16TOF32]]
// CHECK: fcmp olt
test = (h2 < h0);
// CHECK: [[F16TOF32]]
// NOHALF: [[F16TOF32]]
// CHECK: fcmp olt
test = (h2 < (__fp16)42.0);
// CHECK: [[F16TOF32]]
// CHECK: fcmp olt
test = (h2 < f0);
// CHECK: [[F16TOF32]]
// CHECK: fcmp olt
test = (f2 < h0);
// CHECK: [[F16TOF32]]
// CHECK: [[F16TOF32]]
// CHECK: fcmp ogt
test = (h0 > h2);
// CHECK: [[F16TOF32]]
// NOHALF: [[F16TOF32]]
// CHECK: fcmp ogt
test = ((__fp16)42.0 > h2);
// CHECK: [[F16TOF32]]
// CHECK: fcmp ogt
test = (h0 > f2);
// CHECK: [[F16TOF32]]
// CHECK: fcmp ogt
test = (f0 > h2);
// CHECK: [[F16TOF32]]
// CHECK: [[F16TOF32]]
// CHECK: fcmp ole
test = (h2 <= h0);
// CHECK: [[F16TOF32]]
// NOHALF: [[F16TOF32]]
// CHECK: fcmp ole
test = (h2 <= (__fp16)42.0);
// CHECK: [[F16TOF32]]
// CHECK: fcmp ole
test = (h2 <= f0);
// CHECK: [[F16TOF32]]
// CHECK: fcmp ole
test = (f2 <= h0);
// CHECK: [[F16TOF32]]
// CHECK: [[F16TOF32]]
// CHECK: fcmp oge
test = (h0 >= h2);
// CHECK: [[F16TOF32]]
// NOHALF: [[F16TOF32]]
// CHECK: fcmp oge
test = (h0 >= (__fp16)-2.0);
// CHECK: [[F16TOF32]]
// CHECK: fcmp oge
test = (h0 >= f2);
// CHECK: [[F16TOF32]]
// CHECK: fcmp oge
test = (f0 >= h2);
// CHECK: [[F16TOF32]]
// CHECK: [[F16TOF32]]
// CHECK: fcmp oeq
test = (h1 == h2);
// CHECK: [[F16TOF32]]
// NOHALF: [[F16TOF32]]
// CHECK: fcmp oeq
test = (h1 == (__fp16)1.0);
// CHECK: [[F16TOF32]]
// CHECK: fcmp oeq
test = (h1 == f1);
// CHECK: [[F16TOF32]]
// CHECK: fcmp oeq
test = (f1 == h1);
// CHECK: [[F16TOF32]]
// CHECK: [[F16TOF32]]
// CHECK: fcmp une
test = (h1 != h2);
// CHECK: [[F16TOF32]]
// NOHALF: [[F16TOF32]]
// CHECK: fcmp une
test = (h1 != (__fp16)1.0);
// CHECK: [[F16TOF32]]
// CHECK: fcmp une
test = (h1 != f1);
// CHECK: [[F16TOF32]]
// CHECK: fcmp une
test = (f1 != h1);
// CHECK: [[F16TOF32]]
// CHECK: fcmp une
// CHECK: [[F16TOF32]]
// CHECK: [[F16TOF32]]
// CHECK: [[F32TOF16]]
h1 = (h1 ? h2 : h0);
// Check assignments (inc. compound)
h0 = h1;
// NOHALF: [[F32TOF16]]
// HALF: store {{.*}} half 0xHC000
h0 = (__fp16)-2.0f;
// CHECK: [[F32TOF16]]
h0 = f0;
// CHECK: [[F16TOF32]]
// CHECK: [[F16TOF32]]
// CHECK: fadd float
// CHECK: [[F32TOF16]]
h0 += h1;
// CHECK: [[F16TOF32]]
// NOHALF: [[F16TOF32]]
// CHECK: fadd
// CHECK: [[F32TOF16]]
h0 += (__fp16)1.0f;
// CHECK: [[F16TOF32]]
// CHECK: fadd
// CHECK: [[F32TOF16]]
h0 += f2;
// CHECK: [[F16TOF32]]
// CHECK: [[F16TOF32]]
// CHECK: fsub
// CHECK: [[F32TOF16]]
h0 -= h1;
// CHECK: [[F16TOF32]]
// NOHALF: [[F16TOF32]]
// CHECK: fsub
// CHECK: [[F32TOF16]]
h0 -= (__fp16)1.0;
// CHECK: [[F16TOF32]]
// CHECK: fsub
// CHECK: [[F32TOF16]]
h0 -= f2;
// CHECK: [[F16TOF32]]
// CHECK: [[F16TOF32]]
// CHECK: fmul
// CHECK: [[F32TOF16]]
h0 *= h1;
// CHECK: [[F16TOF32]]
// NOHALF: [[F16TOF32]]
// CHECK: fmul
// CHECK: [[F32TOF16]]
h0 *= (__fp16)1.0;
// CHECK: [[F16TOF32]]
// CHECK: fmul
// CHECK: [[F32TOF16]]
h0 *= f2;
// CHECK: [[F16TOF32]]
// CHECK: [[F16TOF32]]
// CHECK: fdiv
// CHECK: [[F32TOF16]]
h0 /= h1;
// CHECK: [[F16TOF32]]
// NOHALF: [[F16TOF32]]
// CHECK: fdiv
// CHECK: [[F32TOF16]]
h0 /= (__fp16)1.0;
// CHECK: [[F16TOF32]]
// CHECK: fdiv
// CHECK: [[F32TOF16]]
h0 /= f2;
// Check conversions to/from double
// NOHALF: call i16 @llvm.convert.to.fp16.f64(
// HALF: fptrunc double {{.*}} to half
h0 = d0;
// CHECK: [[MID:%.*]] = fptrunc double {{%.*}} to float
// NOHALF: call i16 @llvm.convert.to.fp16.f32(float [[MID]])
// HALF: fptrunc float [[MID]] to half
h0 = (float)d0;
// NOHALF: call double @llvm.convert.from.fp16.f64(
// HALF: fpext half {{.*}} to double
d0 = h0;
// NOHALF: [[MID:%.*]] = call float @llvm.convert.from.fp16.f32(
// HALF: [[MID:%.*]] = fpext half {{.*}} to float
// CHECK: fpext float [[MID]] to double
d0 = (float)h0;
}