mirror of
https://github.com/RPCSX/llvm.git
synced 2024-12-16 16:37:30 +00:00
b32894764b
We don't currently have any runtime library functions for operations on f16 values (other than conversions to and from f32 and f64), so we should always promote it to f32, even if that is not a legal type. In that case, the f32 values would be softened to f32 library calls. SoftenFloatRes_FP_EXTEND now needs to check the promoted operand's type, as it may ne a no-op or require a different library call. getCopyFromParts and getCopyToParts now need to cope with a floating-point value stored in a larger integer part, as is the case for any target that needs to store an f16 value in a 32-bit integer register. Differential Revision: http://reviews.llvm.org/D12856 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@252459 91177308-0d34-0410-b5e6-96231b3b80d8
893 lines
24 KiB
LLVM
893 lines
24 KiB
LLVM
; RUN: llc -asm-verbose=false < %s -mattr=+vfp3,+fp16 | FileCheck %s -check-prefix=CHECK-FP16 --check-prefix=CHECK-VFP -check-prefix=CHECK-ALL
|
|
; RUN: llc -asm-verbose=false < %s | FileCheck %s -check-prefix=CHECK-LIBCALL --check-prefix=CHECK-VFP -check-prefix=CHECK-ALL --check-prefix=CHECK-LIBCALL-VFP
|
|
; RUN: llc -asm-verbose=false < %s -mattr=-vfp2 | FileCheck %s --check-prefix=CHECK-LIBCALL -check-prefix=CHECK-NOVFP -check-prefix=CHECK-ALL
|
|
|
|
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n32"
|
|
target triple = "armv7---eabihf"
|
|
|
|
; CHECK-ALL-LABEL: test_fadd:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-VFP: vadd.f32
|
|
; CHECK-NOVFP: bl __aeabi_fadd
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_fadd(half* %p, half* %q) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%b = load half, half* %q, align 2
|
|
%r = fadd half %a, %b
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_fsub:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-VFP: vsub.f32
|
|
; CHECK-NOVFP: bl __aeabi_fsub
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_fsub(half* %p, half* %q) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%b = load half, half* %q, align 2
|
|
%r = fsub half %a, %b
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_fmul:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-VFP: vmul.f32
|
|
; CHECK-NOVFP: bl __aeabi_fmul
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_fmul(half* %p, half* %q) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%b = load half, half* %q, align 2
|
|
%r = fmul half %a, %b
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_fdiv:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-VFP: vdiv.f32
|
|
; CHECK-NOVFP: bl __aeabi_fdiv
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_fdiv(half* %p, half* %q) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%b = load half, half* %q, align 2
|
|
%r = fdiv half %a, %b
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_frem:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl fmodf
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_frem(half* %p, half* %q) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%b = load half, half* %q, align 2
|
|
%r = frem half %a, %b
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_load_store:
|
|
; CHECK-ALL-NEXT: .fnstart
|
|
; CHECK-ALL: ldrh {{r[0-9]+}}, [{{r[0-9]+}}]
|
|
; CHECK-ALL: strh {{r[0-9]+}}, [{{r[0-9]+}}]
|
|
define void @test_load_store(half* %p, half* %q) #0 {
|
|
%a = load half, half* %p, align 2
|
|
store half %a, half* %q
|
|
ret void
|
|
}
|
|
|
|
; Testing only successfull compilation of function calls. In ARM ABI, half
|
|
; args and returns are handled as f32.
|
|
|
|
declare half @test_callee(half %a, half %b) #0
|
|
|
|
; CHECK-ALL-LABEL: test_call:
|
|
; CHECK-ALL-NEXT: .fnstart
|
|
; CHECK-ALL-NEXT: .save {r11, lr}
|
|
; CHECK-ALL-NEXT: push {r11, lr}
|
|
; CHECK-ALL-NEXT: bl test_callee
|
|
; CHECK-ALL-NEXT: pop {r11, pc}
|
|
define half @test_call(half %a, half %b) #0 {
|
|
%r = call half @test_callee(half %a, half %b)
|
|
ret half %r
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_call_flipped:
|
|
; CHECK-ALL-NEXT: .fnstart
|
|
; CHECK-ALL-NEXT: .save {r11, lr}
|
|
; CHECK-ALL-NEXT: push {r11, lr}
|
|
; CHECK-VFP-NEXT: vmov.f32 s2, s0
|
|
; CHECK-VFP-NEXT: vmov.f32 s0, s1
|
|
; CHECK-VFP-NEXT: vmov.f32 s1, s2
|
|
; CHECK-NOVFP-NEXT: mov r2, r0
|
|
; CHECK-NOVFP-NEXT: mov r0, r1
|
|
; CHECK-NOVFP-NEXT: mov r1, r2
|
|
; CHECK-ALL-NEXT: bl test_callee
|
|
; CHECK-ALL-NEXT: pop {r11, pc}
|
|
define half @test_call_flipped(half %a, half %b) #0 {
|
|
%r = call half @test_callee(half %b, half %a)
|
|
ret half %r
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_tailcall_flipped:
|
|
; CHECK-ALL-NEXT: .fnstart
|
|
; CHECK-VFP-NEXT: vmov.f32 s2, s0
|
|
; CHECK-VFP-NEXT: vmov.f32 s0, s1
|
|
; CHECK-VFP-NEXT: vmov.f32 s1, s2
|
|
; CHECK-NOVFP-NEXT: mov r2, r0
|
|
; CHECK-NOVFP-NEXT: mov r0, r1
|
|
; CHECK-NOVFP-NEXT: mov r1, r2
|
|
; CHECK-ALL-NEXT: b test_callee
|
|
define half @test_tailcall_flipped(half %a, half %b) #0 {
|
|
%r = tail call half @test_callee(half %b, half %a)
|
|
ret half %r
|
|
}
|
|
|
|
; Optimizer picks %p or %q based on %c and only loads that value
|
|
; No conversion is needed
|
|
; CHECK-ALL-LABEL: test_select:
|
|
; CHECK-ALL: cmp {{r[0-9]+}}, #0
|
|
; CHECK-ALL: movne {{r[0-9]+}}, {{r[0-9]+}}
|
|
; CHECK-ALL: ldrh {{r[0-9]+}}, [{{r[0-9]+}}]
|
|
; CHECK-ALL: strh {{r[0-9]+}}, [{{r[0-9]+}}]
|
|
define void @test_select(half* %p, half* %q, i1 zeroext %c) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%b = load half, half* %q, align 2
|
|
%r = select i1 %c, half %a, half %b
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; Test only two variants of fcmp. These get translated to f32 vcmpe
|
|
; instructions anyway.
|
|
; CHECK-ALL-LABEL: test_fcmp_une:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-VFP: vcmpe.f32
|
|
; CHECK-NOVFP: bl __aeabi_fcmpeq
|
|
; CHECK-FP16: vmrs APSR_nzcv, fpscr
|
|
; CHECK-ALL: movw{{ne|eq}}
|
|
define i1 @test_fcmp_une(half* %p, half* %q) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%b = load half, half* %q, align 2
|
|
%r = fcmp une half %a, %b
|
|
ret i1 %r
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_fcmp_ueq:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-VFP: vcmpe.f32
|
|
; CHECK-NOVFP: bl __aeabi_fcmpeq
|
|
; CHECK-FP16: vmrs APSR_nzcv, fpscr
|
|
; CHECK-LIBCALL: movw{{ne|eq}}
|
|
define i1 @test_fcmp_ueq(half* %p, half* %q) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%b = load half, half* %q, align 2
|
|
%r = fcmp ueq half %a, %b
|
|
ret i1 %r
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_br_cc:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-VFP: vcmpe.f32
|
|
; CHECK-NOVFP: bl __aeabi_fcmplt
|
|
; CHECK-FP16: vmrs APSR_nzcv, fpscr
|
|
; CHECK-VFP: strmi
|
|
; CHECK-VFP: strpl
|
|
; CHECK-NOVFP: strne
|
|
; CHECK-NOVFP: streq
|
|
define void @test_br_cc(half* %p, half* %q, i32* %p1, i32* %p2) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%b = load half, half* %q, align 2
|
|
%c = fcmp uge half %a, %b
|
|
br i1 %c, label %then, label %else
|
|
then:
|
|
store i32 0, i32* %p1
|
|
ret void
|
|
else:
|
|
store i32 0, i32* %p2
|
|
ret void
|
|
}
|
|
|
|
declare i1 @test_dummy(half* %p) #0
|
|
; CHECK-ALL-LABEL: test_phi:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: [[LOOP:.LBB[1-9_]+]]:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl test_dummy
|
|
; CHECK-FP16: bne [[LOOP]]
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-VFP: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: [[LOOP:.LBB[1-9_]+]]:
|
|
; CHECK-LIBCALL-VFP: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl test_dummy
|
|
; CHECK-LIBCALL: bne [[LOOP]]
|
|
; CHECK-LIBCALL-VFP: bl __aeabi_f2h
|
|
define void @test_phi(half* %p) #0 {
|
|
entry:
|
|
%a = load half, half* %p
|
|
br label %loop
|
|
loop:
|
|
%r = phi half [%a, %entry], [%b, %loop]
|
|
%b = load half, half* %p
|
|
%c = call i1 @test_dummy(half* %p)
|
|
br i1 %c, label %loop, label %return
|
|
return:
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_fptosi_i32:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-VFP: vcvt.s32.f32
|
|
; CHECK-NOVFP: bl __aeabi_f2iz
|
|
define i32 @test_fptosi_i32(half* %p) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%r = fptosi half %a to i32
|
|
ret i32 %r
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_fptosi_i64:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-ALL: bl __aeabi_f2lz
|
|
define i64 @test_fptosi_i64(half* %p) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%r = fptosi half %a to i64
|
|
ret i64 %r
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_fptoui_i32:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-VFP: vcvt.u32.f32
|
|
; CHECK-NOVFP: bl __aeabi_f2uiz
|
|
define i32 @test_fptoui_i32(half* %p) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%r = fptoui half %a to i32
|
|
ret i32 %r
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_fptoui_i64:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-ALL: bl __aeabi_f2ulz
|
|
define i64 @test_fptoui_i64(half* %p) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%r = fptoui half %a to i64
|
|
ret i64 %r
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_sitofp_i32:
|
|
; CHECK-VFP: vcvt.f32.s32
|
|
; CHECK-NOVFP: bl __aeabi_i2f
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_sitofp_i32(i32 %a, half* %p) #0 {
|
|
%r = sitofp i32 %a to half
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_uitofp_i32:
|
|
; CHECK-VFP: vcvt.f32.u32
|
|
; CHECK-NOVFP: bl __aeabi_ui2f
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_uitofp_i32(i32 %a, half* %p) #0 {
|
|
%r = uitofp i32 %a to half
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_sitofp_i64:
|
|
; CHECK-ALL: bl __aeabi_l2f
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_sitofp_i64(i64 %a, half* %p) #0 {
|
|
%r = sitofp i64 %a to half
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_uitofp_i64:
|
|
; CHECK-ALL: bl __aeabi_ul2f
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_uitofp_i64(i64 %a, half* %p) #0 {
|
|
%r = uitofp i64 %a to half
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_fptrunc_float:
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_fptrunc_float:
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_fptrunc_float(float %f, half* %p) #0 {
|
|
%a = fptrunc float %f to half
|
|
store half %a, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_fptrunc_double:
|
|
; CHECK-FP16: bl __aeabi_d2h
|
|
; CHECK-LIBCALL-LABEL: test_fptrunc_double:
|
|
; CHECK-LIBCALL: bl __aeabi_d2h
|
|
define void @test_fptrunc_double(double %d, half* %p) #0 {
|
|
%a = fptrunc double %d to half
|
|
store half %a, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_fpextend_float:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-LIBCALL-LABEL: test_fpextend_float:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
define float @test_fpextend_float(half* %p) {
|
|
%a = load half, half* %p, align 2
|
|
%r = fpext half %a to float
|
|
ret float %r
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_fpextend_double:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-LIBCALL-LABEL: test_fpextend_double:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-VFP: vcvt.f64.f32
|
|
; CHECK-NOVFP: bl __aeabi_f2d
|
|
define double @test_fpextend_double(half* %p) {
|
|
%a = load half, half* %p, align 2
|
|
%r = fpext half %a to double
|
|
ret double %r
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_bitcast_halftoi16:
|
|
; CHECK-ALL-NEXT: .fnstart
|
|
; CHECK-ALL-NEXT: ldrh r0, [r0]
|
|
; CHECK-ALL-NEXT: bx lr
|
|
define i16 @test_bitcast_halftoi16(half* %p) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%r = bitcast half %a to i16
|
|
ret i16 %r
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_bitcast_i16tohalf:
|
|
; CHECK-ALL-NEXT: .fnstart
|
|
; CHECK-ALL-NEXT: strh r0, [r1]
|
|
; CHECK-ALL-NEXT: bx lr
|
|
define void @test_bitcast_i16tohalf(i16 %a, half* %p) #0 {
|
|
%r = bitcast i16 %a to half
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
declare half @llvm.sqrt.f16(half %a) #0
|
|
declare half @llvm.powi.f16(half %a, i32 %b) #0
|
|
declare half @llvm.sin.f16(half %a) #0
|
|
declare half @llvm.cos.f16(half %a) #0
|
|
declare half @llvm.pow.f16(half %a, half %b) #0
|
|
declare half @llvm.exp.f16(half %a) #0
|
|
declare half @llvm.exp2.f16(half %a) #0
|
|
declare half @llvm.log.f16(half %a) #0
|
|
declare half @llvm.log10.f16(half %a) #0
|
|
declare half @llvm.log2.f16(half %a) #0
|
|
declare half @llvm.fma.f16(half %a, half %b, half %c) #0
|
|
declare half @llvm.fabs.f16(half %a) #0
|
|
declare half @llvm.minnum.f16(half %a, half %b) #0
|
|
declare half @llvm.maxnum.f16(half %a, half %b) #0
|
|
declare half @llvm.copysign.f16(half %a, half %b) #0
|
|
declare half @llvm.floor.f16(half %a) #0
|
|
declare half @llvm.ceil.f16(half %a) #0
|
|
declare half @llvm.trunc.f16(half %a) #0
|
|
declare half @llvm.rint.f16(half %a) #0
|
|
declare half @llvm.nearbyint.f16(half %a) #0
|
|
declare half @llvm.round.f16(half %a) #0
|
|
declare half @llvm.fmuladd.f16(half %a, half %b, half %c) #0
|
|
|
|
; CHECK-ALL-LABEL: test_sqrt:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vsqrt.f32
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-VFP-LIBCALL: vsqrt.f32
|
|
; CHECK-NOVFP: bl sqrtf
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_sqrt(half* %p) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%r = call half @llvm.sqrt.f16(half %a)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_fpowi:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl __powisf2
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_fpowi:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl __powisf2
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_fpowi(half* %p, i32 %b) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%r = call half @llvm.powi.f16(half %a, i32 %b)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_sin:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl sinf
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_sin:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl sinf
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_sin(half* %p) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%r = call half @llvm.sin.f16(half %a)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_cos:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl cosf
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_cos:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl cosf
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_cos(half* %p) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%r = call half @llvm.cos.f16(half %a)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_pow:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl powf
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_pow:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl powf
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_pow(half* %p, half* %q) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%b = load half, half* %q, align 2
|
|
%r = call half @llvm.pow.f16(half %a, half %b)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_exp:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl expf
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_exp:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl expf
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_exp(half* %p) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%r = call half @llvm.exp.f16(half %a)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_exp2:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl exp2f
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_exp2:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl exp2f
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_exp2(half* %p) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%r = call half @llvm.exp2.f16(half %a)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_log:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl logf
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_log:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl logf
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_log(half* %p) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%r = call half @llvm.log.f16(half %a)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_log10:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl log10f
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_log10:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl log10f
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_log10(half* %p) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%r = call half @llvm.log10.f16(half %a)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_log2:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl log2f
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_log2:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl log2f
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_log2(half* %p) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%r = call half @llvm.log2.f16(half %a)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_fma:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl fmaf
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_fma:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl fmaf
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_fma(half* %p, half* %q, half* %r) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%b = load half, half* %q, align 2
|
|
%c = load half, half* %r, align 2
|
|
%v = call half @llvm.fma.f16(half %a, half %b, half %c)
|
|
store half %v, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_fabs:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vabs.f32
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_fabs:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bfc
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_fabs(half* %p) {
|
|
%a = load half, half* %p, align 2
|
|
%r = call half @llvm.fabs.f16(half %a)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_minnum:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl fminf
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_minnum:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl fminf
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_minnum(half* %p, half* %q) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%b = load half, half* %q, align 2
|
|
%r = call half @llvm.minnum.f16(half %a, half %b)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_maxnum:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl fmaxf
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_maxnum:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl fmaxf
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_maxnum(half* %p, half* %q) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%b = load half, half* %q, align 2
|
|
%r = call half @llvm.maxnum.f16(half %a, half %b)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_copysign:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vbsl
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_copysign:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-VFP-LIBCALL: vbsl
|
|
; CHECK-NOVFP: bfc
|
|
; CHECK-NOVFP: and
|
|
; CHECK-NOVFP: orr
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_copysign(half* %p, half* %q) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%b = load half, half* %q, align 2
|
|
%r = call half @llvm.copysign.f16(half %a, half %b)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_floor:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl floorf
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_floor:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl floorf
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_floor(half* %p) {
|
|
%a = load half, half* %p, align 2
|
|
%r = call half @llvm.floor.f16(half %a)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_ceil:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl ceilf
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_ceil:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl ceilf
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_ceil(half* %p) {
|
|
%a = load half, half* %p, align 2
|
|
%r = call half @llvm.ceil.f16(half %a)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_trunc:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl truncf
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_trunc:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl truncf
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_trunc(half* %p) {
|
|
%a = load half, half* %p, align 2
|
|
%r = call half @llvm.trunc.f16(half %a)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_rint:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl rintf
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_rint:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl rintf
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_rint(half* %p) {
|
|
%a = load half, half* %p, align 2
|
|
%r = call half @llvm.rint.f16(half %a)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_nearbyint:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl nearbyintf
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_nearbyint:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl nearbyintf
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_nearbyint(half* %p) {
|
|
%a = load half, half* %p, align 2
|
|
%r = call half @llvm.nearbyint.f16(half %a)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_round:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: bl roundf
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_round:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl roundf
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_round(half* %p) {
|
|
%a = load half, half* %p, align 2
|
|
%r = call half @llvm.round.f16(half %a)
|
|
store half %r, half* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-FP16-LABEL: test_fmuladd:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-FP16: vmla.f32
|
|
; CHECK-FP16: vcvtb.f16.f32
|
|
; CHECK-LIBCALL-LABEL: test_fmuladd:
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-VFP-LIBCALL: vmla.f32
|
|
; CHECK-NOVFP: bl __aeabi_fmul
|
|
; CHECK-LIBCALL: bl __aeabi_f2h
|
|
define void @test_fmuladd(half* %p, half* %q, half* %r) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%b = load half, half* %q, align 2
|
|
%c = load half, half* %r, align 2
|
|
%v = call half @llvm.fmuladd.f16(half %a, half %b, half %c)
|
|
store half %v, half* %p
|
|
ret void
|
|
}
|
|
|
|
; f16 vectors are not legal in the backend. Vector elements are not assigned
|
|
; to the register, but are stored in the stack instead. Hence insertelement
|
|
; and extractelement have these extra loads and stores.
|
|
|
|
; CHECK-ALL-LABEL: test_insertelement:
|
|
; CHECK-ALL: sub sp, sp, #8
|
|
; CHECK-ALL: ldrh
|
|
; CHECK-ALL: strh
|
|
; CHECK-ALL: ldrh
|
|
; CHECK-ALL: strh
|
|
; CHECK-ALL: ldrh
|
|
; CHECK-ALL: strh
|
|
; CHECK-ALL: ldrh
|
|
; CHECK-ALL: strh
|
|
; CHECK-ALL: mov
|
|
; CHECK-ALL-DAG: ldrh
|
|
; CHECK-ALL-DAG: add
|
|
; CHECK-ALL: strh
|
|
; CHECK-ALL: ldrh
|
|
; CHECK-ALL: strh
|
|
; CHECK-ALL: ldrh
|
|
; CHECK-ALL: strh
|
|
; CHECK-ALL: ldrh
|
|
; CHECK-ALL: strh
|
|
; CHECK-ALL: ldrh
|
|
; CHECK-ALL: strh
|
|
; CHECK-ALL: add sp, sp, #8
|
|
define void @test_insertelement(half* %p, <4 x half>* %q, i32 %i) #0 {
|
|
%a = load half, half* %p, align 2
|
|
%b = load <4 x half>, <4 x half>* %q, align 8
|
|
%c = insertelement <4 x half> %b, half %a, i32 %i
|
|
store <4 x half> %c, <4 x half>* %q
|
|
ret void
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_extractelement:
|
|
; CHECK-VFP: sub sp, sp, #8
|
|
; CHECK-VFP: ldrh
|
|
; CHECK-VFP: ldrh
|
|
; CHECK-VFP: orr
|
|
; CHECK-VFP: str
|
|
; CHECK-VFP: ldrh
|
|
; CHECK-VFP: ldrh
|
|
; CHECK-VFP: orr
|
|
; CHECK-VFP: str
|
|
; CHECK-VFP: mov
|
|
; CHECK-VFP: add
|
|
; CHECK-VFP: ldrh
|
|
; CHECK-VFP: strh
|
|
; CHECK-VFP: add sp, sp, #8
|
|
; CHECK-VFP: bx lr
|
|
; CHECK-NOVFP: ldrh
|
|
; CHECK-NOVFP: strh
|
|
; CHECK-NOVFP: ldrh
|
|
; CHECK-NOVFP: strh
|
|
; CHECK-NOVFP: ldrh
|
|
; CHECK-NOVFP: strh
|
|
; CHECK-NOVFP: ldrh
|
|
; CHECK-NOVFP: strh
|
|
; CHECK-NOVFP: ldrh
|
|
define void @test_extractelement(half* %p, <4 x half>* %q, i32 %i) #0 {
|
|
%a = load <4 x half>, <4 x half>* %q, align 8
|
|
%b = extractelement <4 x half> %a, i32 %i
|
|
store half %b, half* %p
|
|
ret void
|
|
}
|
|
|
|
; test struct operations
|
|
|
|
%struct.dummy = type { i32, half }
|
|
|
|
; CHECK-ALL-LABEL: test_insertvalue:
|
|
; CHECK-ALL-DAG: ldr
|
|
; CHECK-ALL-DAG: ldrh
|
|
; CHECK-ALL-DAG: strh
|
|
; CHECK-ALL-DAG: str
|
|
define void @test_insertvalue(%struct.dummy* %p, half* %q) {
|
|
%a = load %struct.dummy, %struct.dummy* %p
|
|
%b = load half, half* %q
|
|
%c = insertvalue %struct.dummy %a, half %b, 1
|
|
store %struct.dummy %c, %struct.dummy* %p
|
|
ret void
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_extractvalue:
|
|
; CHECK-ALL: .fnstart
|
|
; CHECK-ALL: ldrh
|
|
; CHECK-ALL: strh
|
|
define void @test_extractvalue(%struct.dummy* %p, half* %q) {
|
|
%a = load %struct.dummy, %struct.dummy* %p
|
|
%b = extractvalue %struct.dummy %a, 1
|
|
store half %b, half* %q
|
|
ret void
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_struct_return:
|
|
; CHECK-FP16: vcvtb.f32.f16
|
|
; CHECK-VFP-LIBCALL: bl __aeabi_h2f
|
|
; CHECK-NOVFP-DAG: ldr
|
|
; CHECK-NOVFP-DAG: ldrh
|
|
define %struct.dummy @test_struct_return(%struct.dummy* %p) {
|
|
%a = load %struct.dummy, %struct.dummy* %p
|
|
ret %struct.dummy %a
|
|
}
|
|
|
|
; CHECK-ALL-LABEL: test_struct_arg:
|
|
; CHECK-ALL-NEXT: .fnstart
|
|
; CHECK-NOVFP-NEXT: mov r0, r1
|
|
; CHECK-ALL-NEXT: bx lr
|
|
define half @test_struct_arg(%struct.dummy %p) {
|
|
%a = extractvalue %struct.dummy %p, 1
|
|
ret half %a
|
|
}
|
|
|
|
attributes #0 = { nounwind }
|